java动态代理和cglib动态代理

在工作中用代理的地方非常多,但一直还没仔细来看代理的原理,今天被同事提到,所以自己开始仔细研究了一下这两者代理都做了些什么工作,并通过编写测试用例的方式来对两种代理原理作理解。
在自行看代码之前,初步问了一下朋友,大概解释这两者区别是,java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。这是朋友说的,我并没自己实验过,所以也没映象,所以开始自己动手实践之:
java动态代理

使用方法:

接口:

public interface Call {
void doCall(String doCall);
}

public interface Processor {
void doProcess(String doProcess);
}

 

实现类:

public class ServiceImpl implements Call, Processor {

public void doCall(String doCall) {
	System.out.println("doCall");
}

public void doProcess(String doProcess) {
	System.out.println("doProcess");
}
}

 

具体代理Handler:

public class ServiceHandler implements InvocationHandler {

private Call callService;

public ServiceHandler(Call callService) {
this.callService = callService;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  System.out.println("proxyMethod=" + method.getName());
  Object obj = method.invoke(this.callService, args);
  System.out.println("after invoke!");
  return obj;
}

}

 
使用java动态代理:

public class JdkProxyTest {    
@Test
public void testJdkProxy() {
Call call = new ServiceImpl();
ServiceHandler handler = new ServiceHandler(call);
Call callProxy = (Call) Proxy.newProxyInstance(call.getClass().getClassLoader(), 
new Class[]{Call.class}, handler);
callProxy.doCall("test");
}
}

 

最终效果就是执行代理接口的doCall方法之前,该方法被ServiceHandler给处理了。

通过查看java.lang.reflect.Proxy代码,大致拟了一下它的实现原理:
1. 取到

new Class[]{Call.class}

 这里所有接口,通过

Class.forName

 把接口类加载到JVM,放到内部Set里保存,把接口的完整名字保存,带包名的接口名字,并以把这组接口名称数组转换成List作为key,用于下面生成代理类后保存到内部Map的key.也就是相当于这一组的接口名称对应的一个生成的代理类


2. 主要是从内存里找是否之前已经生成好了这同一组接口的代理类,如果有就直接拿出。这里第一次是需要新建立的,所以开始创建代理,首先检查代理目标接口的访问控制符是否是默认包级别的,如果是就需要给生成的代理类设置目标接口同样的包名,才能默认访问这种级别下的接口。如果这种有默认访问控制标识符的目标接口,又有不同包名的目标接口,则会报出错误。否则其它情况,是给的无包名的代理类,生成的代理类的默认名称是$Proxy开头加Proxy里标识唯一类名的数字,是静态long型变量,每次生成一次代理类会累加


3. 调用

ProxyGenerator.generateProxyClass(proxyName, interfaces)

 动态生成class字节码类,该类相当于是Proxy的子类,实现了需要代理的接口方法,并在每个方法里调用了InvocationHandler的invoke方法,而我们自己实现的InvocationHandler接口类里完成了以反射方式最终对目标业务类的接口方法进行调用。所以此种方式实现的动态代理只能代理接口方法,对具体类的代理不能实现。

 

最终动态生成Proxy子类方法在下面这一句代码

 

byte[] proxyClassFile =  ProxyGenerator.generateProxyClass(
		    proxyName, interfaces);

 这里应该是根据指定接口和代理类名生成class字节数据,再用这代码生成最终类

   proxyClass = defineClass0(loader, proxyName,
			proxyClassFile, 0, proxyClassFile.length);

 

 java动态代理不能代理目标类的超类,原因是java动态代理的设计就是目标代理类是继承了Proxy,而java是不支持多继承,也就无法继承目标类的超类。

你可能感兴趣的:(java,jvm,工作)