动态代理
为其他对象提供一种代理以控制对这个对象的访问,增强一个类中的某个方法,对程序进行扩展
JDK 动态代理
jdk本身也提供了一种创建代理对象的动态代理机制,但是它只能代理接口,也就是先有一个目标类接口才能利用jdk动态代理机制来生成一个代理对象。
在 Java 动态代理机制中 InvocationHandler 接口和 Proxy 类是核心。
Proxy 类中使用频率最高的方法是:newProxyInstance() ,这个方法主要用来生成一个代理对象
// 代码示例
public static void javaProxyTest(){
Target target = new Target();
/*
newProxyInstance方法的参数:
loader :类加载器,用于加载代理对象。
interfaces : 被代理类实现的一些接口;
h : 实现了 InvocationHandler 接口的对象;
*/
Object proxy = Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]{TargetInterface.class}, new InvocationHandler() {
@Override
//1.proxy :动态生成的代理类
//2.method : 与代理类对象调用的方法相对应
//3.args : 当前 method 方法的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("java proxy ---before...");
Object result = method.invoke(target, args);
System.out.println("java proxy ---after...");
return result;
}
});
TargetInterface targetInterface = (TargetInterface) proxy;
targetInterface.test();
}
CGLIB 动态代理
CGLIB(Code Generation Library)是一个开源项目。其代码托管在Github.
是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成。
它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的拦截
**CGLIB 原理:**动态生成一个要代理类的子类,子类重写代理的类的所有非final的方法。在子类中拦截所有父类方法的调用,顺势植入横切逻辑。它比使用java反射的JDK动态代理要快。
CGLIB 底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。(ASM 是一个 Java 字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类)。
CGLIB缺点:对于final方法,无法进行代理。
CGLIB的API
- Jar包:
cglib-nodep-3.3.0.jar:使用nodep包不需要关联asm的jar包,jar包内部包含asm的类.
cglib-3.3.0.jar:使用此jar包需要关联asm的jar包,否则运行时报错. - CGLIB类库:
- net.sf.cglib.core: 底层字节码处理类,他们大部分与ASM有关系。
- net.sf.cglib.transform: 编译期或运行期类和类文件的转换
- net.sf.cglib.proxy: 实现创建代理和方法拦截器的类
- MethodInterceptor :在调用目标方法时,CGLib会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑,
- Enhancer: 是CGLib中的一个字节码增强器
- net.sf.cglib.reflect: 实现快速反射和C#风格代理的类
- net.sf.cglib.util: 集合排序等工具类
- net.sf.cglib.beans: JavaBean相关的工具类
代码示例:
public static void CglibProsyTest() {
UserService target = new UserService();
// 通过cglib技术
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
// 定义额外逻辑,也就是代理逻辑
enhancer.setCallbacks(new Callback[]{new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib---before...");
Object result = methodProxy.invoke(target, objects);
System.out.println("cglib---after...");
return result;
}
}});
// 动态代理所创建出来的UserService对象
// 原理,首先将被代理类Userservice设置成父类,然后设置拦截器MethodInterceptor,最后执行enhancer.create()动态生成一个代理类,并从Object强制转型成父类型UserService。
UserService userService = (UserService) enhancer.create();
// 执行这个userService的test方法时,就会额外会执行一些其他逻辑
userService.test();
}
JDK 动态代理和 CGLIB 动态代理对比
- JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB
动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。 - 就效率来说,大部分情况都是 JDK 动态代理更优秀