转自: LearningNotes-Java中的动态代理.md
代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
代理
代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
代理可以实现过滤请求、插入横切逻辑等功能,应用场景丰富多彩。
代理的方式分为静态代理和动态代理两种。
静态代理
程序运行前代理类的字节码文件依然存在,需要程序员编写源文件。
动态代理
程序运行时动态生成代理类的字节码文件,不需要程序员编写代理类java文件。
实现方式
让你来实现一个代理类,需要哪些上下文,有哪些解决方案?
jdk和cglib两种解决方案。
要生产一个类A的代理类,唯一需要了解的就是生产一个什么类,所以需要类A的接口。
至于如何生产一个class文件,在既定规则下你可以先编写java文件,再编译成class文件。而最好的做法是直接操作字节码文件,jdk操作字节码文件用了反射包里面的接口和类,cglib操作字节码文件用了开源框架asm框架。
代理的图示
![][1]
通过代理层这一中间层,有效的控制对于真实委托类对象的直接访问,同时可以实现自定义的控制策略(Spring的AOP机制),设计上获得更大的灵活性。
JDK的动态代理实现
jdk的动态代理,依赖的是反射包下的InvocationHandler接口,我们的代理类实现InvocationHandler,重写invoke()方法,每当我们的代理类调用方法时,都会默认先经过invoke()方法。
UserService接口
1 2 3 4
| public interface UserService { public String getName(int id); public Integer getAge(int id); }
|
接口的实现类UserServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12
| public class UserServiceImpl implements UserService { public String getName(int id) { System.out.println("------getName------"); return "Tom"; } public Integer getAge(int id) { System.out.println("------getAge------"); return 10; } }
|
UserInvocationHandler.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class UserInvocationHandler implements InvocationHandler { private Object target; UserInvocationHandler() { super(); } UserInvocationHandler(Object target) { super(); this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("getName".equals(method.getName())){ System.out.println("++++++before " + method.getName() + "++++++"); Object result = method.invoke(target, args); System.out.println("++++++after " + method.getName() + "++++++"); return result; }else{ Object result = method.invoke(target, args); return result; } } }
|
测试类TestUserInvocationHandler.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class TestUserInvocationHandler { public static void main(String[] args) { UserService userService = new UserServiceImpl(); InvocationHandler invocationHandler = new UserInvocationHandler(userService); UserService userServiceProxy = (UserService) Proxy.newProxyInstance( userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), invocationHandler); System.out.println(userServiceProxy.getName(1)); System.out.println(userServiceProxy.getAge(1)); } }
|
运行结果:
![][2]
cglib的动态代理实现
cglib需要的jar包:cglib.jar 和 asm.jar
cglib依赖的是cglib包下的MethodInterceptor接口,每调用代理类的方法,都会调用intercept方法
CglibMethodInterceptor.java
1 2 3 4 5 6 7 8 9 10 11 12 13
| import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibMethodInterceptor implements MethodInterceptor { public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("------before " + methodProxy.getSuperName() + "------"); Object o1 = methodProxy.invokeSuper(o, args); System.out.println("------after " + methodProxy.getSuperName() + "------"); return o1; } }
|
TestCglibMethodInterceptor.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import net.sf.cglib.proxy.Enhancer; public class TestCglibMethodInterceptor { public static void main(String[] args) { CglibMethodInterceptor cglibProxy = new CglibMethodInterceptor(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserServiceImpl.class); enhancer.setCallback(cglibProxy); UserServiceImpl o = (UserServiceImpl) enhancer.create(); o.getName(1); o.getAge(1); } }
|
运行结果:
![][3]
总结
JDK动态代理的原理是根据定义好的规则,用传入的接口创建一个新类,这就是为什么采用JDK动态代理时只能用接口引用指向代理,而不能用传入的类引用指向代理。
cglib采用的是创建一个继承实现类的子类,用asm库动态修改子类的代码来实现的,所以可以用传入的类引用指向代理类。