这篇文章试验了JDK动态代理与CGLIB动态代理。从Spring的AOP框架介绍中得知对于使用接口的类,Spring使用JDK 动态代理(原来做项目中试图从Bean强制转换为实现类,结果报错,原来是这么回事),没有接口的就使用别的AOP框架aspectj,但这些都是依赖于 Java字节码工具ASM生成一个原类的新类,调用Callback
但是JDK动态代理为什么必须使用接口一直很疑惑,难道原理不是像ASM一样修改字节码吗?带着这个疑问,开始看JDK的Proxy代码。使用JDK动态代理的代码代码。
于是从创建代理函数看起,即public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException ,
通过源码可以看到,这个类第一步生成一个代理类(注意,这里的参数就是接口列表),
然后通过代理类找到构造参数为InvocationHandler的构造函数并生成一个新类。
接口起什么作用呢,于是又看getProxyClass方法的代码,这个源码很长,就不细说了。大致分为三段:
第一:验证
第二:缓存创建新类的结构,如果创建过,则直接返回。(注意:这里的KEY就是接口列表)
第三:如果没有创建过,则创建新类
创建代码如下
根据前面的代码Constructor cons = cl.getConstructor(constructorParams);
可以猜测到接口创建的新类proxyClassFile 不管采用什么接口,都是以下结构
生成新类的看不到源代码,不过猜测它的执行原理很有可能是如果类是Proxy的子类,则调用InvocationHandler进行方法的Invoke
到现在大家都应该明白了吧,JDK动态代理的原理是根据定义好的规则,用传入的接口创建一个新类,这就是为什么采用动态代理时为什么只能用接口引用指向代理,而不能用传入的类引用执行动态类。
cglib采用的是用创建一个继承实现类的子类,用asm库动态修改子类的代码来实现的,所以可以用传入的类引用执行代理类
JDK动态代理与CGLIB对比如下:
//JDK动态代理测试代码
//CGLIB测试代码
补充说明,如果在实现类中,接口定义的方法互相调用不会在调用InvocationHandler的invoke方法,JDK动态代理应该不是嵌入到Java的反射机制中,而是在反射机制上的一个调用。
应用举例如下:
JDK动态代理的简单使用示例:
创建一个实现java.lang.reflect.InvocationHandler 接口的代理类,如:
用java.lang.reflect.Proxy.newProxyInstance()方法创建动态实例来调用代理实例的方法:
CGLib动态代理示例:
创建一个实现net.sf.cglib.proxy.MethodInterceptor接口的实例来为目标业务类加入进行代理时要进行的操作或增强:
通过java.lang.reflect.Proxy的getProxy()动态生成目标业务类的子类,即是代理类,再由此得到代理实例:
总结下Spring的AOP运用的设计模式 , AOP 主要利用代理模式, 然后依赖通知(本人认为是策略模式)来实现AOP。 这样通知就可以独立发展。
但是JDK动态代理为什么必须使用接口一直很疑惑,难道原理不是像ASM一样修改字节码吗?带着这个疑问,开始看JDK的Proxy代码。使用JDK动态代理的代码代码。
- ITestBean tb = (ITestBean) Proxy.newProxyInstance(tb.getClass().getClassLoader(), tb.getClass().getInterfaces(), new TestBeanHander(tb));
ITestBean tb = (ITestBean) Proxy.newProxyInstance(tb.getClass().getClassLoader(), tb.getClass().getInterfaces(), new TestBeanHander(tb));
于是从创建代理函数看起,即public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException ,
通过源码可以看到,这个类第一步生成一个代理类(注意,这里的参数就是接口列表),
- Class cl = getProxyClass(loader, interfaces);
Class cl = getProxyClass(loader, interfaces);
然后通过代理类找到构造参数为InvocationHandler的构造函数并生成一个新类。
- Constructor cons = cl.getConstructor(constructorParams);//这个有用,在后面细说
- return (Object) cons.newInstance(new Object[] { h });
Constructor cons = cl.getConstructor(constructorParams);//这个有用,在后面细说 return (Object) cons.newInstance(new Object[] { h });
接口起什么作用呢,于是又看getProxyClass方法的代码,这个源码很长,就不细说了。大致分为三段:
第一:验证
第二:缓存创建新类的结构,如果创建过,则直接返回。(注意:这里的KEY就是接口列表)
第三:如果没有创建过,则创建新类
创建代码如下
- long num;
- //获得代理类数字标识
- synchronized (nextUniqueNumberLock) {
- num = nextUniqueNumber++;
- }
- //获得创建新类的类名$Proxy,包名为接口包名,但需要注意的是,如果有两个接口而且不在同一个包下,也会报错
- String proxyName = proxyPkg + proxyClassNamePrefix + num;
- //调用class处理文件生成类的字节码,根据接口列表创建一个新类,这个类为代理类,
- byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
- proxyName, interfaces);
- //通过JNI接口,将Class字节码文件定义一个新类
- proxyClass = defineClass0(loader, proxyName,
- proxyClassFile, 0, proxyClassFile.length);
long num; //获得代理类数字标识 synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; } //获得创建新类的类名$Proxy,包名为接口包名,但需要注意的是,如果有两个接口而且不在同一个包下,也会报错 String proxyName = proxyPkg + proxyClassNamePrefix + num; //调用class处理文件生成类的字节码,根据接口列表创建一个新类,这个类为代理类, byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); //通过JNI接口,将Class字节码文件定义一个新类 proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
根据前面的代码Constructor cons = cl.getConstructor(constructorParams);
可以猜测到接口创建的新类proxyClassFile 不管采用什么接口,都是以下结构
- public class $Proxy1 extends Proxy implements 传入的接口{
- }
public class $Proxy1 extends Proxy implements 传入的接口{ }
生成新类的看不到源代码,不过猜测它的执行原理很有可能是如果类是Proxy的子类,则调用InvocationHandler进行方法的Invoke
到现在大家都应该明白了吧,JDK动态代理的原理是根据定义好的规则,用传入的接口创建一个新类,这就是为什么采用动态代理时为什么只能用接口引用指向代理,而不能用传入的类引用执行动态类。
cglib采用的是用创建一个继承实现类的子类,用asm库动态修改子类的代码来实现的,所以可以用传入的类引用执行代理类
JDK动态代理与CGLIB对比如下:
//JDK动态代理测试代码
- ITestBean tb = new TestBean();
- tb = (ITestBean) Proxy.newProxyInstance(tb.getClass().getClassLoader(), tb.getClass().getInterfaces(), new TestBeanHander(tb));//这句用接口引用指向,不会报错
- TestBean tmp = (TestBean) tb;//强制转换为实现类,将抛出类强制转换异常
ITestBean tb = new TestBean(); tb = (ITestBean) Proxy.newProxyInstance(tb.getClass().getClassLoader(), tb.getClass().getInterfaces(), new TestBeanHander(tb));//这句用接口引用指向,不会报错 TestBean tmp = (TestBean) tb;//强制转换为实现类,将抛出类强制转换异常
//CGLIB测试代码
- TestProxy tp = new TestProxy();
- tb = (ITestBean) tp.getProxy(TestBean.class);
- tmp = (TeatBean) tb;//强制转换为实现类,不会抛出异常
TestProxy tp = new TestProxy(); tb = (ITestBean) tp.getProxy(TestBean.class); tmp = (TeatBean) tb;//强制转换为实现类,不会抛出异常
补充说明,如果在实现类中,接口定义的方法互相调用不会在调用InvocationHandler的invoke方法,JDK动态代理应该不是嵌入到Java的反射机制中,而是在反射机制上的一个调用。
应用举例如下:
JDK动态代理的简单使用示例:
- package com.proxy;
- public class ForumServiceImpl implements ForumService{
- public void removeTopic(int topicId){
- System.out.println("模拟删除记录"+topicId);
- try{
- Thread.currentThread().sleep(20);
- }catch(Exception e){
- throw new RuntimeException(e);
- }
- }
- public void removeForum(int forumId){
- System.out.println("模拟删除记录"+forumId);
- try{
- Thread.currentThread().sleep(20);
- }catch(Exception e){
- throw new RuntimeException(e);
- }
- }
- }
package com.proxy; public class ForumServiceImpl implements ForumService{ public void removeTopic(int topicId){ System.out.println("模拟删除记录"+topicId); try{ Thread.currentThread().sleep(20); }catch(Exception e){ throw new RuntimeException(e); } } public void removeForum(int forumId){ System.out.println("模拟删除记录"+forumId); try{ Thread.currentThread().sleep(20); }catch(Exception e){ throw new RuntimeException(e); } } }
创建一个实现java.lang.reflect.InvocationHandler 接口的代理类,如:
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- public class PerformanceHandler implements InvocationHandler{
- private Object target; //要进行代理的业务类的实例
- public PerformanceHandler(Object target){
- this.target = target;
- }
- //覆盖java.lang.reflect.InvocationHandler的方法invoke()进行织入(增强)的操作
- //在实际应用中, 这里会引用一个Intercepter类来做处理。 然后Intercepter就可以独立发展
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable{
- System.out.println("Object target proxy:"+target);
- System.out.println("模拟代理加强的方法...");
- Object obj = method.invoke(target, args); //调用目标业务类的方法
- System.out.println("模拟代理加强的方法执行完毕...");
- return obj;
- }
- }
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class PerformanceHandler implements InvocationHandler{ private Object target; //要进行代理的业务类的实例 public PerformanceHandler(Object target){ this.target = target; } //覆盖java.lang.reflect.InvocationHandler的方法invoke()进行织入(增强)的操作 //在实际应用中, 这里会引用一个Intercepter类来做处理。 然后Intercepter就可以独立发展 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ System.out.println("Object target proxy:"+target); System.out.println("模拟代理加强的方法..."); Object obj = method.invoke(target, args); //调用目标业务类的方法 System.out.println("模拟代理加强的方法执行完毕..."); return obj; } }
用java.lang.reflect.Proxy.newProxyInstance()方法创建动态实例来调用代理实例的方法:
- import java.lang.reflect.Proxy;
- public class TestForumService {
- public static void main(String args[]){
- ForumService target = new ForumServiceImpl();//要进行代理的目标业务类
- PerformanceHandler handler = new PerformanceHandler(target);//用代理类把目标业务类进行编织
- //创建代理实例,它可以看作是要代理的目标业务类的加多了横切代码(方法)的一个子类
- ForumService proxy = (ForumService)Proxy.newProxyInstance(
- target.getClass().getClassLoader(),
- target.getClass().getInterfaces(), handler);
- proxy.removeForum(10);
- proxy.removeTopic(20);
- }
- }
import java.lang.reflect.Proxy; public class TestForumService { public static void main(String args[]){ ForumService target = new ForumServiceImpl();//要进行代理的目标业务类 PerformanceHandler handler = new PerformanceHandler(target);//用代理类把目标业务类进行编织 //创建代理实例,它可以看作是要代理的目标业务类的加多了横切代码(方法)的一个子类 ForumService proxy = (ForumService)Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler); proxy.removeForum(10); proxy.removeTopic(20); } }
CGLib动态代理示例:
创建一个实现net.sf.cglib.proxy.MethodInterceptor接口的实例来为目标业务类加入进行代理时要进行的操作或增强:
- import java.lang.reflect.Method;
- import net.sf.cglib.proxy.MethodProxy;
- import net.sf.cglib.proxy.Enhancer;
- import net.sf.cglib.proxy.MethodInterceptor;
- /**
- *CGlib采用非常底层的字节码技术,可以为一个类创建子类,
- 并在子类中采用方法拦截技术拦截父类方法的调用,并顺势进行增强,即是织入横切逻辑
- * @author tufu
- */
- public class CglibProxy implements MethodInterceptor{
- private Enhancer enhancer = new Enhancer();
- //覆盖MethodInterceptor接口的getProxy()方法,设置
- public Object getProxy(Class clazz){
- enhancer.setSuperclass(clazz); //设者要创建子类的类
- enhancer.setCallback(this); //设置回调的对象
- return enhancer.create(); //通过字节码技术动态创建子类实例,
- }
- public Object intercept(Object obj,Method method,Object[] args,
- MethodProxy proxy) throws Throwable {
- System.out.println("模拟代理增强方法");
- //通过代理类实例调用父类的方法,即是目标业务类方法的调用
- Object result = proxy.invokeSuper(obj, args);
- System.out.println("模拟代理增强方法结束");
- return result;
- }
- }
import java.lang.reflect.Method; import net.sf.cglib.proxy.MethodProxy; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; /** *CGlib采用非常底层的字节码技术,可以为一个类创建子类, 并在子类中采用方法拦截技术拦截父类方法的调用,并顺势进行增强,即是织入横切逻辑 * @author tufu */ public class CglibProxy implements MethodInterceptor{ private Enhancer enhancer = new Enhancer(); //覆盖MethodInterceptor接口的getProxy()方法,设置 public Object getProxy(Class clazz){ enhancer.setSuperclass(clazz); //设者要创建子类的类 enhancer.setCallback(this); //设置回调的对象 return enhancer.create(); //通过字节码技术动态创建子类实例, } public Object intercept(Object obj,Method method,Object[] args, MethodProxy proxy) throws Throwable { System.out.println("模拟代理增强方法"); //通过代理类实例调用父类的方法,即是目标业务类方法的调用 Object result = proxy.invokeSuper(obj, args); System.out.println("模拟代理增强方法结束"); return result; } }
通过java.lang.reflect.Proxy的getProxy()动态生成目标业务类的子类,即是代理类,再由此得到代理实例:
- import com.proxy.ForumServiceImpl;
- import java.lang.reflect.Proxy;
- public class TestCglibProxy {
- public static void main(String args[]){
- CglibProxy proxy = new CglibProxy();
- //动态生成子类的方法创建代理类
- ForumServiceImpl fsi =
- (ForumServiceImpl)proxy.getProxy(ForumServiceImpl.class);
- fsi.removeForum(10);
- fsi.removeTopic(2);
- }
- }
import com.proxy.ForumServiceImpl; import java.lang.reflect.Proxy; public class TestCglibProxy { public static void main(String args[]){ CglibProxy proxy = new CglibProxy(); //动态生成子类的方法创建代理类 ForumServiceImpl fsi = (ForumServiceImpl)proxy.getProxy(ForumServiceImpl.class); fsi.removeForum(10); fsi.removeTopic(2); } }
总结下Spring的AOP运用的设计模式 , AOP 主要利用代理模式, 然后依赖通知(本人认为是策略模式)来实现AOP。 这样通知就可以独立发展。