反射机制是Java语言提供的一种基础功能,赋予程序在运行时自省(introspect)的能力。
通过反射,我们可以直接操作类或者对象,如获取某个对象的类定义,获取类声明的属性和方法,调用方法或者构造对象,甚至在运行时修改类定义。
动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制,应用于包装RPC调用、AOP等。
实现动态代理的方式
- JDK动态代理,利用了Java语言的反射机制。
- ASM。
- cglib(基于ASM)。
- Javassist。
- AspectJ。
等等。
Java反射机制
官方文档。
要看一下java.lang或java.lang.reflect包下的相关抽象。
Class、Field、Method、Constructor。
注意AccessibleObject.setAccessible(boolean flag)方法,可以在运行时修改成员的访问限制,做到暴力访问。不过之后可能会改成只有当被反射操作的模块和指定的包对反射调用者模块Open,才能对其使用setAccessible。
动态代理
这是一个代理机制。代理可以看作是对调用目标的一个包装,这样我们对目标代码的调用不直接发生,而通过代理来完成。动态代理也可以看作是装饰器模式的应用。
通过代理,可以让调用者与实现者之间解耦,如进行RPC调用时,RPC框架内部的寻址、序列化、反序列化等对调用者往往没有意义,通过代理,可以提供更加友善的接口。
一个JDK动态代理的demo:
public class MyDynamicProxy {public static void main (String[] args) {HelloImpl hello = new HelloImpl();MyInvocationHandler handler = new MyInvocationHandler(hello);// 构造代码实例Hello proxyHello = (Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(), HelloImpl.class.getInterfaces(), handler);// 调用代理方法proxyHello.sayHello();}}interface Hello {void sayHello();}class HelloImpl implements Hello {@Overridepublic void sayHello() {System.out.println("Hello World");}}class MyInvocationHandler implements InvocationHandler {private Object target;public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("Invoking sayHello");Object result = method.invoke(target, args);return result;}}
这种实现以接口为中心,相当于添加了一种对于被调用者没有太大意义的限制。实例化的是Proxy对象,而非真正的被调用类,实践中可能会带来不便。
Spring AOP支持两种模式的动态代理:JDK动态代理和cglib。
cglib动态代理采取的是创建目标类的子类的方式,子类化可以达到近似使用被调用者本身的效果。
JDK Proxy & cglib
JDK Proxy的优势:
- 最小化依赖关系,简化开发和维护,毕竟JDK本身的支持比cglib更可靠;
- 平滑进行JDK版本升级;
- 代码实现简单。
cglib的优势:
- 调用目标时,不必添加额外接口;
- 只操作关心的类,不必为其他相关类增加工作量。
