JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。

为了解决这个问题,我们可以用 CGLIB 动态代理机制来避免。

CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。很多知名的开源框架都使用到了CGLIB, 例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。

在 CGLIB 动态代理机制中 MethodInterceptor 接口和 Enhancer 类是核心。

你需要自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法。

 

1:obj :被代理的对象(需要增强的对象)

2:method :被拦截的方法(需要增强的方法)

3:args :方法入参

4:methodProxy :用于调用原始方法

你可以通过 Enhancer类来动态获取被代理类,当代理类调用方法的时候,实际调用的是 MethodInterceptor 中的 intercept 方法。

CGLIB 动态代理类使用步骤
1:定义一个类;

2:自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;

3:通过 Enhancer 类的 create()创建代理类;

 

代码示例 

 

1、定义类

 1  
 2 /**
 3  * @author hjf
 4  * @version 1.0
 5  * @date 2021/3/27 17:59
 6  * @Description 1、定义一个类
 7  */
 8 public class AliSmsService {
 9     public String send(String message) {
10         System.out.println("send message:" + message);
11         return message;
12     }
13 }
View Code

2、自定义MethodInterceptor

 1 /**
 2  * @author hjf
 3  * @version 1.0
 4  * @date 2021/3/27 18:01
 5  * @Description 2、实现 MethodInterceptor
 6  */
 7 public class DebugMethodInterceptor implements MethodInterceptor {
 8  
 9  
10     /**
11      * @param obj    被代理的对象(需要增强的对象)
12      * @param method 被拦截的方法(需要增强的方法)
13      * @param args   方法入参
14      * @param proxy  用于调用原始方法
15      */
16     @Override
17     public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
18         if (method.getDeclaringClass().equals(Object.class)) {
19             return method.invoke(this, args);
20         }
21         //调用方法之前,我们可以添加自己的操作
22         System.out.println("before method " + method.getName());
23         Object object = null;
24         if (method.getDeclaringClass().isInterface()) {
25             if (method.getName().equals("hello")) {
26                 object = "hello";
27             }
28         } else {
29             object = proxy.invokeSuper(obj, args);
30         }
31  
32         //调用方法之后,我们同样可以添加自己的操作
33         System.out.println("after method " + method.getName());
34         return object;
35     }
36 }
View Code

3、通过 Enhancer 创建代理类

 1 /**
 2  * @author hjf
 3  * @version 1.0
 4  * @date 2021/3/27 18:06
 5  * @Description 3、通过 Enhancer 类的 create()创建代理类;
 6  */
 7 public class CglibProxyFactory {
 8  
 9     public static <T> T getObject(Class<T> clazz) {
10         //1、创建 Enhancer 动态代理增加类
11         Enhancer enhancer = new Enhancer();
12         //2、设置 类加载器
13         enhancer.setClassLoader(clazz.getClassLoader());
14         //3、设置 被代理对象
15         enhancer.setSuperclass(clazz);
16         //4、设置 代理对象 ==> 设置方法拦截器
17         enhancer.setCallback(new DebugMethodInterceptor());
18         //5、创建 增加对象
19         return (T) enhancer.create();
20     }
21 }
View Code

测试

 1     @Test
 2     public void test01() {
 3         // 实际使用
 4         AliSmsService aliSmsService = CglibProxyFactory.getObject(AliSmsService.class);
 5         aliSmsService.send(" 恭喜你中奖了,本次放松的获奖验证码为: 88888 ");
 6     }
 7  
 8     //测试 代理接口
 9  
10     @Test
11     public void test02() {
12         // 实际使用
13         UserMapper userMapper = CglibProxyFactory.getObject(UserMapper.class);
14         System.out.println(userMapper.hello());
15     }
16  
17     //接口
18     /**
19      * TODO
20      *
21      * @author 发哥讲Java
22      * @version 1.0    
23      * @date 2021-03-10 16:27
24      */
25     public interface UserMapper {
26         public String hello();
27         public String hello2();
28     
29     }    
View Code

相对于JDK 动态代理,cglib的优势在于速度更快,底层实现方式为fastclass,将一个类的方法建立索引,直接通过索引去找方法执行。