Created
January 12, 2018 05:55
-
-
Save zhanggang807/a6b1606e6d2f4eba8c039b209692ef05 to your computer and use it in GitHub Desktop.
dynamic proxy of CG lib
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class UserServiceImpl { | |
public void add() { | |
System.out.println("This is add service"); | |
} | |
public void delete(int id) { | |
System.out.println("This is delete service?delete " + id); | |
} | |
} | |
class MyMethodInterceptor implements MethodInterceptor { | |
public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable { | |
System.out.println("Before:" + method); | |
Object object = proxy.invokeSuper(obj, arg); | |
System.out.println("After:" + method); | |
return object; | |
} | |
} | |
class TestCGLibProxy { | |
/** | |
* 代理对象的生成过程由Enhancer类实现,大概步骤如下: | |
1、生成代理类Class的二进制字节码; | |
2、通过 Class.forName加载二进制字节码,生成Class对象; | |
3、通过反射机制获取实例构造,并初始化代理类对象。 | |
4、通过System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "file path"); //生成class文件 | |
* @param args | |
*/ | |
public static void main(String[] args) { | |
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, System.getProperty("user.home") + File.separator + "Desktop"); // 生成class文件 | |
Enhancer enhancer = new Enhancer(); | |
enhancer.setSuperclass(UserServiceImpl.class); | |
enhancer.setCallback(new MyMethodInterceptor()); | |
UserServiceImpl userService = (UserServiceImpl) enhancer.create(); | |
System.out.println("====================="); | |
userService.add(); | |
System.out.println("====================="); | |
userService.delete(8); | |
System.out.println("====================="); | |
} | |
/** | |
5、使用 反编译工具 procyon 查看代理类实现 | |
通过CGLIB生成的字节码相比JDK实现来说显得更加复杂。 | |
1、代理类 UserService$$EnhancerByCGLIB$$394dddeb继承了委托类 UserSevice,且委托类的final方法不能被代理; | |
2、代理类为每个委托方法都生成两个方法,以add方法为例,一个是重写的add方法,一个是CGLIB$add$0方法,该方法直接调用委托类的add方法; | |
3、当执行代理对象的add方法时,会先判断是否存在实现了MethodInterceptor接口的对象 cglib$CALLBACK_0,如果存在,则调用MethodInterceptor对象的 intercept方法: | |
参数分别为:1、代理对象;2、委托类方法;3、方法参数;4、代理方法的MethodProxy对象。 | |
4、每个被代理的方法都对应一个MethodProxy对象, methodProxy.invokeSuper方法最终调用委托类的add方法,实现如下: | |
*/ | |
//参考资料 出处 http://mp.weixin.qq.com/s/iedEtNDp-u9C1RGvqj3Juw --> https://www.jianshu.com/p/13aa63e1ac95 | |
} | |
/** | |
* FastClass实现机制 //todo 这个要好好了解下具体原理 | |
* | |
* FastClass其实就是对Class对象进行特殊处理,提出下标概念index,通过索引保存方法的引用信息, | |
* 将原先的反射调用,转化为方法的直接调用,从而体现所谓的fast,下面通过一个例子了解一下FastClass的实现机制 | |
* | |
* 在FastTest中有两个方法,getIndex中对Test类的每个方法根据hash建立索引,invoke根据指定的索引,直接调用 | |
* 目标方法,避免了反射调用。所以当调用methodProxy.invokeSuper方法时,实际上是调用代理类的CGLIB$add$0方法, | |
* CGLIB$add$0直接调用了委托类的add方法。 | |
* | |
* | |
*/ | |
class Test { | |
public void f() { | |
System.out.println("f method"); | |
} | |
public void g() { | |
System.out.println("g method"); | |
} | |
} | |
class FastTest { | |
public int getIndex(String signature) { | |
switch (signature.hashCode()) { | |
case 3078479: | |
return 1; | |
case 3108270: | |
return 2; | |
} | |
return -1; | |
} | |
public Object invoke(int index, Object o, Object[] ol) { | |
Test t = (Test) o; | |
switch (index) { | |
case 1: | |
t.f(); | |
return null; | |
case 2: | |
t.g(); | |
return null; | |
} | |
return null; | |
} | |
} | |
/** | |
* 总结 jdk和cglib动态代理实现的区别 | |
1、jdk动态代理生成的代理类和委托类实现了相同的接口; | |
2、cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final关键字修饰的方法; | |
3、jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法; | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment