JDK动态代理与CGLIB动态代理
本篇文章是学习了Guide哥的开源图书中的JDK动态代理和CGLIB动态代理后,结合自己的理解所写的.Guide哥原文请点我.
JDK动态代理在MyBatis中的应用可以查看文章:MyBatis入门-Mapper接口动态代理
使用CGLIB时,需要在pom中引入依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
JDK动态代理
要使用JDK动态代理必须要知道有两个类:
- java.lang.reflect.InvocationHandler:用来增强我们的被代理对象
- java.lang.reflect.Proxy:用来创建代理对象
Proxy创建代理对象最常用的方法是newProxyInstance.其中的参数:
ClassLoader loader
:被代理类的加载器,将会用来初始化被代理对象Class<?>[] interfaces
:被代理类实现的接口列表InvocationHandler h
:增强的操作实现类
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{}
InvocationHandler接口中的invoke方法如下,其中的参数:
Object proxy
:被代理的对象Method method
:代理对象执行的方法Object[] args
:代理对象执行的方法的参数
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
当我们想要增强某一个类时,就需要创建一个以其为成员变量的InvocationHandler接口实现类,并在invoke方法中定义我们自己的增强实现.
实现步骤
- 定义一个接口及其实现类;
- 自定义
InvocationHandler
并重写invoke
方法,在invoke
方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑; - 通过
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
方法创建代理对象;
CGLIB动态代理
JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。
为了解决这个问题,我们可以用 CGLIB 动态代理机制来避免。
CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。很多知名的开源框架都使用到了CGLIB, 例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。
使用CGLIB进行动态代理必须要知道的有两个类:
- net.sf.cglib.proxy.Enhancer:增强类,用来创建代理对象
- net.sf.cglib.proxy.MethodInterceptor:方法拦截器接口,用来实现增强的操作
MethodInterceptor类中重写的intercept方法,其中的参数:
Object obj
:增强的对象java.lang.reflect.Method method
:拦截的方法Object[] args
:拦截方法的参数MethodProxy proxy
:用来调用原始方法
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
MethodProxy proxy) throws Throwable;
实现步骤
- 定义一个类;
- 自定义
MethodInterceptor
并重写intercept
方法,intercept
用于拦截增强被代理类的方法,和 JDK 动态代理中的invoke
方法类似; - 通过
Enhancer
类的create()
创建代理类;
测试
EmailService
public interface EmailService {
String sendEmail(String body);
}
EmailServiceImpl
public class EmailServiceImpl implements EmailService {
@Override
public String sendEmail(String body) {
Random random = new Random();
try {
// 模拟业务操作
TimeUnit.SECONDS.sleep(random.nextInt(10));
System.out.println("邮件发送成功....");
} catch (InterruptedException e) {
e.printStackTrace();
}
return body;
}
}
JdkEmailInvokeHandler
public class JdkEmailInvokeHandler implements InvocationHandler {
private final EmailService emailService;
public JdkEmailInvokeHandler(EmailService emailService) {
this.emailService = emailService;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 进入代理方法内部,自定义增强实现
System.out.println("JDK动态代理.....检查邮件服务器....");
Object invoke = method.invoke(emailService, args);
System.out.println("JDK动态代理.....邮件发送成功记录日志....");
return invoke;
}
}
CglibEmailMethodInterceptor
public class CglibEmailMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("CGLIB.....邮件服务器检查...");
Object invokeSuper = methodProxy.invokeSuper(o, objects);
System.out.println("CGLIB.....邮件发送成功日志记录");
return invokeSuper;
}
}
测试类
public class TestG {
public static void main(String[] args) {
// JDK动态代理测试
EmailService emailService = new EmailServiceImpl();
JdkEmailInvokeHandler jdkEmailInvokeHandler = new JdkEmailInvokeHandler(emailService);
EmailService jdkEmailService = (EmailService)Proxy.newProxyInstance(EmailServiceImpl.class.getClassLoader(),
new Class[]{EmailService.class},
jdkEmailInvokeHandler);
jdkEmailService.sendEmail("JDK邮件发送....");
// CGLIB动态代理测试
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(EmailServiceImpl.class.getClassLoader());
enhancer.setSuperclass(EmailServiceImpl.class);
CglibEmailMethodInterceptor cglibEmailMethodInterceptor = new CglibEmailMethodInterceptor();
enhancer.setCallback(cglibEmailMethodInterceptor);
EmailService cglibEmailService = (EmailService) enhancer.create();
cglibEmailService.sendEmail("CGLIB邮件发送....");
}
}
输出结果
JDK动态代理.....检查邮件服务器....
邮件发送成功....
JDK动态代理.....邮件发送成功记录日志....
CGLIB.....邮件服务器检查...
邮件发送成功....
CGLIB.....邮件发送成功日志记录