聊一聊 CGLIB 动态代理

0 前言

重温一下,何为代理:

  • 举个栗子,你准备自己到楼下去买咖啡,但是这时候突然有事走不开,你就让你的同事帮你买咖啡(顺便请他喝一杯hhh),因此你的同事就是代理类,你就是被代理类,而买咖啡这个行为就是你们需要执行的。

  • 书接上文,JDK 动态代理机制只能代理实现了 invocationHandler 接口的类,而 CGLIB 动态代理是针对子类来实现代理的,其主要原理是针对目标类生成一个子类,覆盖目标类的方法实现增强。这篇文章主要介绍下 CGLIB 动态代理

  • 说明:CGLIB 动态代理需要引入依赖

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>


1 具体实现

  • 定义一个功能接口(买咖啡)

/**
* @Author Jasper
* @Time 2024/01/22
*/
public interface BuyCoffeeService {
void buyCoffee();
}


  • 被代理对象(实现类)

/**
* @Author Jasper
* @Time 2024/01/22
*/
public class BuyCoffeeServiceImpl implements BuyCoffeeService {
@Override
public void buyCoffee() {
System.out.println("到楼下买咖啡!");
}
}


  • 创建一个 CGLIB 动态代理处理器,实现 MethodInterceptor 接口

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

/**
* @Author Jasper
* @Time 2024/01/22
*/
public class CglibProxyFactory implements MethodInterceptor{

/** 维护一个目标对象 */
private Object target;

/**
* 过构造器传入目标对象
* @param target
*/
public CglibProxyFactory(Object target) {
this.target = target;
}

public Object getProxyInstance(){
// 创建enhancer,用于生成一个子类,去生成代理对象
Enhancer enhancer = new Enhancer();
// 目标对象类作为代理类的父类
// CGLIB体现的是继承思想,所以我们需要把代理类作为我们目标类的一个子类,也就是把目标类设置为父类,代理类去继承它
enhancer.setSuperclass(target.getClass());
// 设置 enhancer 的回调对象
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}

/**
* @param o 代理对象
* @param method 被代理对象的方法
* @param objects 方法入参
* @param methodProxy 代理方法
* @return Object
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("CGLIB 代理开始...");
Object obj = method.invoke(target, objects);
System.out.println("CGLIB 代理结束...");
return obj;
}
}


  • 测试类:

import top.ezjava.demo.dynamicProxy.jdkProxy.BuyCoffeeServiceImpl;

/**
* @Author Jasper
* @Time 2023/2/7
*/
public class TestCglibProxy {
public static void main(String[] args) {
// 创建目标对象
BuyCoffeeServiceImpl buyCoffeeService = new BuyCoffeeServiceImpl();
// 传入目标对象,得到代理类
CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(buyCoffeeService);
// 得到代理对象,进行强转
BuyCoffeeServiceImpl proxyInstance = (BuyCoffeeServiceImpl) cglibProxyFactory.getProxyInstance();
// 通过代理对象调用目标对象的方法
proxyInstance.buyCoffee();
}
}

/**
输出结果:
CGLIB 代理开始...
到楼下买咖啡!
CGLIB 代理结束...
*/


3 总结

  • JDK 动态代理和 CGLIB 动态代理的区别:

1、JDK 动态代理实现接口的思想,CGLIB 动态继承的思想
2、JDK 动态代理实现 InvocationHandler 接口,CGLIB 动态代理实现 MethodInterceptor 接口
3、如果对象有接口实现就选择 JDK 动态代理,如果没有接口实现就选择 CGILB 动态代理


4 补充

  • SpringBoot 2.x 开始,默认实现 CGLIB 动态代理,其原因是 CGLIB 动态代理不容易出转换错误,原话如下:This was changed in 1.4 (see 5423). We’ve generally found cglib proxies less likely to cause unexpected cast exceptions.

  • 如果一定要使用 JDK 动态代理,需要在 application.yaml 文件里进行如下配置:

spring:
aop:
proxy-target-class: false # true为cglib代理(默认),false为jdk代理
  • CGLIB 动态代理就到此为止啦,如有错误,欢迎指正。

  • 创作不易,感谢阅读,若遇到问题,可以关注此微信公众号留言反馈,希望能够帮助到您。


原文始发于微信公众号(EzCoding):聊一聊 CGLIB 动态代理

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/203649.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!