反射机制是Java语言提供的一种基础功能,赋予程序在运行时自省的能力。通过反射我们可以直接操作类或者对象,比如说获取某个对象的类定义,获取类声明的属性和方法,调用方法或者构造对象,甚至可以运行时修改类定义。
动态代理是一种方便运行时动态构建代理,动态处理代理方法调用的机制,很多场景都是利用类似机制做到的,比如说用来包装RPC调用,面向切面的编程(AOP)。
实现动态代理的方式很多,比如说JDK自身提供的动态代理,就是主要利用了上面提到的反射机制。还要其他实现方式,比如利用传说中更高性能的字节码操作机制,类似ASM,cglib等。
一. 反射机制及其演进
反射提供AccessibleObject.setAccessible(boolean flag)。它的子类也大都重写的这个方法,它可以在运行时,修改成员访问限制。
但是,在Java9中,因为Jigsaw项目新增的模块化系统,出于强封装性的考虑,对反射访问进行了限制。Jigsaw引入了所谓的Open的概念,只有当被反射操作的模块和指定的包对反射调用者模块Open,才能使用setAccessible;否则,被认为是不合法操作。如果我们的实体类是定义在模块里面,我们需要在模块描述符中明确声明:
module MyEntities {
// Open for reflection
opens com.mycorp to java.persistence;
}
动态代理,首先是一个代理机制。如果属性设计模式中的代理模式,我们会知道,代理可以看作是对调用目标的一个包装,这样我们对目标代码的调用不是直接发生的,而是通过代理完成。其实很多动态代理场景,我认为也可以看作是装饰器模式的应用。
通过代理可以让调用者与实现者之间解耦。比如进行RPC调用,框架内部的寻址,序列化,反序列化等,对于调用者往往是没有太大意义的,通过代理,可以提供更加友善的界面。
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 {
@Override
public void sayHello() {
System.out.println("Hello World");
}
}
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Invoking sayHello");
Object result = method.invoke(target, args);
return result;
}
}
如果被调用者没有实现接口,而我们还是希望利用动态代理机制,那么可以使用cglib方式。
public class Dao {
public void update() {
System.out.println("PeopleDao.update()"); }
public void select() {
System.out.println("PeopleDao.select()"); }
}
public class DaoProxy implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
System.out.println("Before Method Invoke");
proxy.invokeSuper(object, objects);
System.out.println("After Method Invoke");
return object;
}
public class CglibTest {
@Test
public void testCglib() {
DaoProxy daoProxy = new DaoProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Dao.class);
enhancer.setCallback(daoProxy);
Dao dao = (Dao)enhancer.create();
dao.update();
dao.select();
}
}
}
JDK Proxy优势:
- 最小化依赖关系,减少依赖意味着简化开发和维护,JDK本身的支持,可能比cglib更加可靠。
- 平滑进行JDK版本升级
- 代码实现简单
基于cglib框架的优势:
- 不需要实现接口
- 只操作我们关心的类,而不必为其他相关类增加工作量。
- 高性能。