关于JDK动态代理与Cglib代理
最近有时间学习一下SpringAOP源码,底层用到了代理,大概是这样的:
当需要被代理的类实现了接口,则使用JDK动态代理创建代理对象,增加增强操作执行目标方法
当需要被代理的类未实现接口,则使用Cglib代理创建目标类的子类,增加增强操作执行目标方法
由此可见JDK动态代理的使用条件是 被代理的类必须实现了接口。(接口是什么无关要紧,但是必须实现了接口,生成的代理类对象也是实现了此接口的类)
Cglib代理主要是通过增强字节码,生成目标代理类的子类从而实现代理。所以这里要求目标类不能被final修饰!!!
>>JDK动态代理
1.定义动态代理类 JDKDynamicProxy.java
package com.dfx.study.jdkdynamic; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * JDK动态代理类 * @author Administrator * 被代理类需要实现一个接口 */ public class JDKDynamicProxy implements InvocationHandler{ Object targetObj;//代理对象 /** * 返回代理后的对象 * @param obj 需要被代理的对象 * @return */ public Object getProxy(Object obj){ this.targetObj = obj; /* * 参数解析: * obj.getClass().getClassLoader() 指定代理类的类加载器 * obj.getClass().getInterfaces() 需要被代理的类所实现的接口数组 * this 当前InvocationHandler对象 */ return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this); } /** * 调用目标对象的方法时,会通过代理对象的invoke方法 再里面执行目标对象的方法 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("JDK proxy invoke执行前");//在目标方法执行前的增强操作 method.invoke(targetObj, args);//执行目标方法 System.out.println("JDK proxy invoke执行后");//在目标方法执行后的增强操作 return null; } }
2.目标类的接口定义 UserService.java
package com.dfx.study.jdkdynamic; /** * 定义接口,用于测试JDK动态代理 生成代理对象 * @author Administrator * */ public interface UserService { public void say(); }
3.目标类定义 (实现了UserService接口) UserServiceImpl.java
package com.dfx.study.jdkdynamic; /** * 接口的实现类(也就是这次用于测试的需要被代理的类) * @author Administrator * */ public class UserServiceImpl implements UserService{ @Override public void say() { System.out.println("我是目标类真实输出内容:Hello JDKDynamicProxy!!!"); } }
4.测试代理类 JDKDynamicProxy.java
package com.dfx.study.jdkdynamic; /** * JDK动态代理测试类 * @author Administrator * */ public class JDKDynamicProxyTest { public static void main(String[] args){ JDKDynamicProxy jdkProxy = new JDKDynamicProxy();//创建实现InvocationHandler接口的对象 动态代理实现类对象 UserService userService = (UserService)jdkProxy.getProxy(new UserServiceImpl());//生成代理对象 userService.say();//调用代理对象的say方法 } }
5.测试结果:
JDK proxy invoke执行前 我是目标类真实输出内容:Hello JDKDynamicProxy!!! JDK proxy invoke执行后
进一步思考,判断JDK动态代理是不是真的只能代理实现接口的类呢,我们去掉接口试一下,也就是修改 UserServiceImpl.java如下
package com.dfx.study.jdkdynamic; /** * 接口的实现类(也就是这次用于测试的需要被代理的类) * @author Administrator * */ public class UserServiceImpl {
public void say() { System.out.println("我是目标类真实输出内容:Hello JDKDynamicProxy!!!"); } }
更改测试类 JDKDynamicProxy.java
package com.dfx.study.jdkdynamic; /** * JDK动态代理测试类 * @author Administrator * */ public class JDKDynamicProxyTest { public static void main(String[] args){ JDKDynamicProxy jdkProxy = new JDKDynamicProxy();//创建实现InvocationHandler接口的对象 动态代理实现类对象 //UserService userService = (UserService)jdkProxy.getProxy(new UserServiceImpl());//生成代理对象 UserServiceImpl userService = (UserServiceImpl)jdkProxy.getProxy(new UserServiceImpl());//不通实现接口 生成代理对象 userService.say();//调用代理对象的say方法 } }
测试结果: (会抛出异常,生成的代理类不能被强制转换为我们的目标类UserServiceImpl,所以最后生成的代理类一定是和目标类实现了同一接口的类)
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.dfx.study.jdkdynamic.UserServiceImpl at com.dfx.study.jdkdynamic.JDKDynamicProxyTest.main(JDKDynamicProxyTest.java:13)
JDK动态代理总结:
1.代理逻辑的实际处理类一定要实现InvocationHandler(拦截器)接口,在其中的invoke()方法中对目标对象的方法进行增强,接口代理对象的所有方法都会转发到invoke()方法处理
2.实际处理类中有一个Obeject类型的对象表示目标对象,所有实现了接口的要选择此种增强的方法都可以使用这个实际处理类创建代理对象
3.代理对象使通过java反射机制在运行时通过Proxy.newProxyInstance动态生成的
4.JDK动态代理的目标类(也就是需要被代理类)必须要实现了接口,通过JDK动态代理生成的代理类一定是实现了目标类的接口,且代理对象在被调用的时候只能调用接口里的方法。
5.JDK动态代理返回的代理对象只能用接口类型接收,不能用目标类接收。(这里说的比较抽象,下面用代码解释一下)
UserService userService = (UserService) jdkProxy.getProxy(new UserServiceImpl());//生成代理对象 正确
UserServiceImpl userService = (UserServiceImpl) jdkProxy.getProxy(new UserServiceImpl());//生成代理对象 错误
>>Cglib代理
1.定义代理类 CglibProxy.java
package com.dfx.study.cglib; import java.lang.reflect.Method; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; /** * Cglib实现代理 * @author Administrator * */ public class CglibProxy implements MethodInterceptor{ Enhancer enhancer = new Enhancer(); /** * 得到代理对象 (是被代理对象的子类) * @param obj * @return */ public Object getProxy(Object obj){ enhancer.setSuperclass(obj.getClass());//将需要被代理的类设置为代理类的父类 enhancer.setCallback(this); Object object = enhancer.create();//创建被代理类的子类对象 return object; } /** * 调用目标对象的方法时,会通过代理对象的invoke方法 再里面执行目标对象的方法 */ @Override public Object intercept(Object obj, Method method, Object[] params, MethodProxy methodProxy) throws Throwable { //System.out.println("Cglib proxy 入参:obj="+obj); System.out.println("Cglib proxy 入参:method="+method); System.out.println("Cglib proxy 入参:params="+params); System.out.println("Cglib proxy 入参:methodProxy="+methodProxy); System.out.println("Cglib proxy invoke执行前");//在目标方法执行前的增强操作 String con = (String) methodProxy.invokeSuper(obj, params); System.out.println("methodProxy.invokeSuper(obj, params) 执行结果:"+con); System.out.println("Cglib proxy invoke执行后");//在目标方法执行前的增强操作 return null; } }
2.定义需要被代理的目标类 UserService.java (不需要实现其他接口)
package com.dfx.study.cglib; /** * 用于Cglib代理的测试类,不需要实现接口 * @author Administrator * */ public class UserService { public String say(){ System.out.println("我是目标类真实输出内容:Hello CglibProxy!!!"); return "Cglib"; } }
3.测试Cglib代理 CglibProxyTest.java
package com.dfx.study.cglib; /** * Cglib代理测试类 * @author Administrator * */ public class CglibProxyTest { public static void main(String[] args){ CglibProxy cglibProxy = new CglibProxy(); UserService userService = (UserService)cglibProxy.getProxy(new UserService());//得到代理对象 userService.say(); } }
4.Cglib代理测试结果:
Cglib proxy 入参:method=public java.lang.String com.dfx.study.cglib.UserService.say() Cglib proxy 入参:params=[Ljava.lang.Object;@5a10411 Cglib proxy 入参:methodProxy=org.springframework.cglib.proxy.MethodProxy@2ef1e4fa Cglib proxy invoke执行前 我是目标类真实输出内容:Hello CglibProxy!!! methodProxy.invokeSuper(obj, params) 执行结果:Cglib Cglib proxy invoke执行后
进一步证明一下Cglib代理的类不能被final修饰(final修饰的类无法被继承),因为它要通过生成目标类的子类从而实现代理,接下来我们做如下修改 UserService.java
package com.dfx.study.cglib; /** * 用于Cglib代理的测试类,不需要实现接口 * @author Administrator * 添加final修饰类 */ public final class UserService { public String say(){ System.out.println("我是目标类真实输出内容:Hello CglibProxy!!!"); return "Cglib"; } }
测试结果:(抛出异常 无法从final修饰的类中生成子类信息,由此我们可得知如果想要通过Cglib成功代理,那这个类一定不能用final修饰)
Exception in thread "main" java.lang.IllegalArgumentException: Cannot subclass final class class com.dfx.study.cglib.UserService at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:446) at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216) at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:377) at org.springframework.cglib.proxy.Enhancer.create(Enhancer.java:285) at com.dfx.study.cglib.CglibProxy.getProxy(CglibProxy.java:25) at com.dfx.study.cglib.CglibProxyTest.main(CglibProxyTest.java:11)
Cglib代理总结:
1.CGLIB动态代理不要求目标对象一定要实现接口;
2.代理逻辑的实际处理类要实现MethodInterceptor接口,在intercept()方法中对目标对象的方法进行增强
3.CGLIB通过Enhancer对象指定代理的目标对象,实现处理逻辑的对象,使用create()在运行期间动态得到代理对象;
综合总结:
1.JDK动态代理要求目标对象一定要实现接口,Cglib则不用.
2.JDK动态代理通过反射机制要动态生成代理类,生成类的过程比较高效.
3.Cglib基于继承来实现代理,代理对象实际上是目标对象的子类,它内部通过第三方类库ASM,加载目标对象类的class文件,修改字节码来生成子类,生成类的过程较低效,但生成类以后的执行很高效,可以通过将ASM生成的类进行缓存来解决生成类过程低效的问题.