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