实现原理区别
通过反编译 JDK 的代理类和 CGLIB 的代理类,可以比较两种不同的实现机制:
目标类:
JDK 动态代理是通过实现目标类的接口,然后将目标类在构造动态代理时作为参数传入,使代理对象持有目标对象,再通过代理对象的 InvocationHandler 实现动态代理的操作。
CGLIB 动态代理是通过配置目标类信息,然后利用 ASM 字节码框架进行生成目标类的子类。当调用代理方法时,通过拦截方法的方式实现代理的操作。
总的来说: JDK 动态代理利用接口实现代理,只能代理实现了接口的类,代理对象是利用反射机制动态生成;CGLIB 动态代理利用继承的方式实现代理,可以代理未实现任何接口的类,代理对象是利用拦截机制动态生成。
一、JDK 动态代理
利用 JDK 的 API(利用反射机制),动态的在内存中构建代理对象,且代理类必须实现 InvocationHandler 接口,同时目标对象要实现接口,否则不能用动态代理。在 Java 动态代理机制中 InvocationHandler
接口和 Proxy
类是核心。
1、JDK 动态代理类使用步骤
定义一个接口及其实现类(被代理对象);
自定义 InvocationHandler
并重写invoke
方法,在 invoke
方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
方法创建代理对象;
1.1 定义被代理类
接口
/**
* @Description: TestProxy0Impl
* @Author: 青衣醉
* @Date: 2022/7/21 4:30 下午
*/
public interface TestProxy0 {
public String test0();
}
public interface TestProxy1 {
public void test1(String mm);
}
public interface TestProxy2 {
public String test2(String cc);
}
复制代码
实现类
package com.abc.test;
public class TestProxyImpl implements TestProxy0,TestProxy1,TestProxy2{
@Override
public String test0() {
System.out.println ("调用了test0方法");
return "方法结束";
}
@Override
public void test1(String mm) {
System.out.println ("test1"+mm);
}
@Override
public String test2(String cc) {
System.out.println ("test2");
return cc+"方法结束";;
}
}
复制代码
1.2 定义 InvocationHandler
代理类的逻辑处理程序,可在 invoke 方法中自定义些逻辑对被代理类方法进行增强
invoke()
方法: 当我们的动态代理对象调用原生方法的时候,最终实际上调用到的是 invoke()
方法,然后 invoke()
方法代替我们去调用了被代理对象的原生方法。
/**
* @Description: TargetInvoker
* @Author: 青衣醉
* @Date: 2022/7/21 5:14 下午
*/
public class TargetInvoker implements InvocationHandler {
private Object target;
public TargetInvoker(Object target) {
this.target = target;
}
//proxy 被代理对象
//method 目标对象中的方法对象
//args 方法对象的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk 代理执行前");
Object result = method.invoke(target, args);
System.out.println("jdk 代理执行后");
return result;
}
}
复制代码
1.3 定义 JDK 代理工厂
通过 Proxy 类的 newProxyInstance 方法创建代理对象
package com.abc.test;
import java.lang.reflect.Proxy;
/**
* @Description: JDK动态代理类厂
* @Author: 青衣醉
* @Date: 2022/7/27 10:15 上午
*/
public class JDKProxy {
public static Object getProxy(Object obj){
Class<?> aClass = obj.getClass ();
/**
* 第一个参数: aClass.getClassLoader (),使用handler对象的classloader对象来加载我们的代理对象
* 第二个参数:aClass.getInterfaces (),这里为代理类提供的接口是真实对象实现的接口
* ,这样代理对象就能像真实对象一样调用接口中的所有方法
* 第三个参数:handler,我们将代理对象关联到上面的InvocationHandler对象上
*/
return Proxy.newProxyInstance (aClass.getClassLoader (), aClass.getInterfaces (), new TargetInvoker (obj));
}
}
复制代码
1.4 测试
@Test
public void jdkProxy(){
TestProxy2 proxy = (TestProxy2) JDKProxy.getProxy (new TestProxyImpl ());
System.out.println (proxy.test2 ("使用jdk动态代理"));
}
复制代码
2、源码输出
idea 中通过配置 VM 参数可以将动态生成的代理类的字节码文件保存到本地,方便观察分析代理类的结构。
JDK8 及以前版本:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
JDK8 以后版本:-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true
3、源码分析
InvocationHandler 实现类 #invoke
从上面源码来看:
1、JDK 动态代理,通过继承 Proxy 类,然后实现被代理类的的接口,来生成代理对象;
2、调用时,在实现的接口方法里面调用父类中 InvocationHandler 的 invoke 方法;
3、在接口 InvocationHandler 的实现类里面定义具体的调用逻辑,这里就可以增加额外的功能,对目标方法进行增强。
二、CGLIB 动态代理
CGLIB 动态代理的实现机制是生成目标类的子类,通过调用父类(目标类)的方法实现,在调用父类方法时再代理中进行增强。在 CGLIB 动态代理机制中 MethodInterceptor
接口和 Enhancer
类是核心。
1、CGLIB 动态代理类使用步骤
定义一个类;
自定义 MethodInterceptor
并重写 intercept
方法,intercept
用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke
方法类似;
通过 Enhancer
类的 create()
创建代理类;
1.1 定义被代理类
package com.abc.test;
/**
* @Description: CglibSevice
* @Author: 青衣醉
* @Date: 2022/8/4 11:05 上午
*/
public class CglibSevice {
public String send (String msg){
System.out.println (msg);
return "发送邮件成功";
}
}
复制代码
1.2 定义 MethodInterceptor
(方法拦截器)
创建拦截器类(增强类), 实现 MethodInterceptor 接口. 在这里面可以对方法进行增强处理
package com.abc.test;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @Description: TargetInterceptor方法拦截接口
* @Author: 青衣醉
* @Date: 2022/7/26 5:07 下午
*/
public class TargetInterceptor implements MethodInterceptor {
/**
* 拦截所有目标类的方法调用
*
* @param obj 目标对象
* @param method 目标方法
* @param args 方法参数
* @param methodProxy 代理类实例
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("CGLIB 调用前");
//代理类对象调用父类方法
Object result = methodProxy.invokeSuper(obj, args);
System.out.println("CGLIB 调用后");
return result;
}
}
复制代码
1.3 定义 CGLIB 代理工厂
package com.abc.test;
import org.springframework.cglib.proxy.Enhancer;
import java.io.File;
import java.io.FileOutputStream;
/**
* @Description: CglibProxy动态代理类
* @Author: 青衣醉
* @Date: 2022/7/27 10:15 上午
*/
public class CglibProxy {
public static Object getProxy(Class<?> clazz){
Enhancer enhancer = new Enhancer();
// 设置类加载
enhancer.setClassLoader(clazz.getClassLoader());
// 设置被代理类
enhancer.setSuperclass(clazz);
// 设置方法拦截器
enhancer.setCallback(new TargetInterceptor());
// 创建代理类
Object o = enhancer.create ();
//输出字节码文件
extracted (enhancer);
return o;
}
}
复制代码
1.4 测试
@Test
public void cglibProxy(){
CglibSevice proxy = (CglibSevice) CglibProxy.getProxy (CglibSevice.class);
System.out.println (proxy.send ("测试cglib动态代理!"));
}
复制代码
2、源码输出
private static void extracted(Enhancer enhancer) {
try {
byte[] generate = new byte[0];
generate = enhancer.getStrategy ().generate (enhancer);
FileOutputStream fileOutputStream = new FileOutputStream (
new File ("/Users/tangyunhang/Studying/JAVAGJ/StudyTestLibrary/com/CglibProxy.class"));
fileOutputStream.write (generate);
fileOutputStream.flush ();
fileOutputStream.close ();
} catch (Exception e) {
e.printStackTrace ();
}
}
复制代码
3、源码分析
从上述源码中看到 CGLIB 生成的代理类是通过创建一个新的类继承被代理类,并重写了父类的方法,在重写的方法里面调用的接口的 intercept 方法,在这里面可以增加额外的逻辑实现对目标方法的增强。
三、总结
1、如果目标对象实现了接口,则默认采用 JDK 动态代理;
2、如果目标对象没有实现接口,则使用 Cglib 代理;
3、如果目标对象实现了接口,但强制使用了 Cglib,则使用 Cglib 进行代理
评论