您的位置:首页 > 编程语言 > Java开发

Java 动态代理机制简单理解

2015-10-01 11:12 609 查看
  Spring有两个核心的思想,一个是IOC,另一个就是AOP,而这个AOP就是建立在JAVA动态代理基础上的,下面先用一个简单的示例来说明动态代理的用法,然后简单叙述动态代理实现的原理。

一、示例

实现代理有四个步骤

1、创建一个接口

public interface Login {
void validate();
void login();
}


2、编写这个接口的实现类,这个类里面只包含业务方法

public class LoginImpl implements Login{

@Override
public void validate() {
// TODO Auto-generated method stub
System.out.println("validate");
}

@Override
public void login() {
// TODO Auto-generated method stub
System.out.println("login");
}

}


3、创建代理类,实现InvacationHandler,通过这个代理类可以动态创建代理对象,并且完成相关业务。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class LoginProxy implements InvocationHandler {
private Object proxyObj;
public  LoginProxy(Object obj)
{
this.proxyObj=obj;
}

public static Object bind(Object obj)
{
Class clz=obj.getClass();
return Proxy.newProxyInstance(clz.getClassLoader(), clz.getInterfaces(), new LoginProxy(obj));
}
@Override
public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
// TODO Auto-generated method stub
login(arg1);
Object res=arg1.invoke(proxyObj, arg2);
logout(arg1);
return res;
}

private void login(Method method)
{
System.out.println("before "+method.getName());
}

private void logout(Method method)
{
System.out.println("after "+method.getName());
}
}


4、创建测试类。

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import sun.misc.ProxyGenerator;
public class Main {
public static void main(String[] args) {
//生成一个实现Login接口的类的字节码数组,测试用
byte[] clazzFile = ProxyGenerator.generateProxyClass("$Proxy11", LoginImpl.class.getInterfaces());
OutputStream os=null;
try {
//生成的class文件放在bin文件夹里面
os=new FileOutputStream(Main.class.getClassLoader().getResource("").getPath()+"/$Proxy11.class");
os.write(clazzFile);
os.flush();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//测试动态代理,生成一个代理对象,这个代理对象实现了LoginImpl实现的接口
Login login=(Login)LoginProxy.bind(new LoginImpl());
//代理类调用方法
login.validate();
login.login();
}
}


输出

before validate
validate
after validate
before login
login
after login


二、原理简述

  代理对象怎么获得的呢?这得从Proxy类的newProxyInstance开始,

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)  throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, interfaces); // stack walk magic: do not refactor

/*
* Invoke its constructor with the designated invocation handler.
*/
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
SecurityManager sm = System.getSecurityManager();
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
// create proxy instance with doPrivilege as the proxy class may
// implement non-public interfaces that requires a special permission
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return newInstance(cons, ih);
}
});
} else {
return newInstance(cons, ih);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}


  具体的细节可以不去看他,这里只要看红色的部分,显示获取了一个代理类,这个代理类里面有个参数是接口,正是需要代理的对象的所实现的接口,最后返回一个这个类的实例,这个类怎么得到的呢? //弱引用hashmap key是classloader,value是Map,缓存对应类加载器的接口数组 object存放的是生成的类的弱引用

private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache= new WeakHashMap<>();
//存放生成的代理类
private static Map<Class<?>, Void> proxyClasses = Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>());

private static Class<?> getProxyClass0(ClassLoader loader , Class<?>... interfaces) {
....................此处省略一对判断
....................此处省略得到接口名数组
            //对象的接口数组
          List<String> key = Arrays.asList(interfaceNames);
....................又是一堆验证
          //通过上面的接口数组生成一个类
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
  try {
   proxyClass = defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
    } catch (ClassFormatError e) {
   throw new IllegalArgumentException(e.toString());
     }
      }
    // add to set of all generated proxy classes, for isProxyClass
    proxyClasses.put(proxyClass, null);
          }
          finally {
      synchronized (cache) {
          //这里的cache是Map<List<String>,Object>的对象,最上面的那个
   if (proxyClass != null) {
   cache.put(key, new WeakReference<Class<?>>(proxyClass));
  } else {
  cache.remove(key);
  }
    cache.notifyAll();
     }
    }
    //返回类
     return proxyClass;


  在这函数里面,先获取这个对象的接口数组,再在缓存中去搜索看看有没有这个类,具体过程就是先通过classLoader来找Map,根据这个接口链表来找存放类的弱引用,假如没有的话就重新生成,至于怎么生成这个类的字节码,太复杂了,这里就省略了。

  那么为什么这个实现了接口的代理对象调用的是login方法,而结果确实invoke被调用了呢?我们看一下生成的$Proxy1这个类,把它反编译一下就能够看到。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy11 extends Proxy  implements Login
{
private static Method m1;
private static Method m4;
private static Method m3;
private static Method m0;
private static Method m2;

public $Proxy11(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}

public final boolean equals(Object paramObject)
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}

public final void validate()
{
try
{
this.h.invoke(this, m4, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}

public final void login()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}

public final int hashCode()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}

public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}

static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m4 = Class.forName("Login").getMethod("validate", new Class[0]);
m3 = Class.forName("Login").getMethod("login", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}


而Proxy类里面

protected InvocationHandler h;

protected Proxy(InvocationHandler h) {
doNewInstanceCheck();
this.h = h;
}


  到此真正的幕后已经出来了,就是生成了一个类,然后实例化一个对象,并把InvocationHandler接口的实现类的对象作为参数传进去,那么这个对象在调用login或者validate方法时,就会调用这个InvocationHandler接口的实现类的对象的invoke方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: