Cglib的Enhancer实现动态代理?
口语化回答
好的,面试官,cglib 代理相比 jdk 动态代理不同的就是不需要被代理的类去实现接口。假设我们现在有一个MyService,其中有一个方法是performTask,我们只需要定义一个新的类,实现MethodInterceptor 接口,然后再里面的 intercept 方法实现需要增强的方法。最终通过 cglib 的enhancer 类,先设置父类,父类就是我们要增强的类(也就是被代理类),再设置 callback 也就是我们要增强的功能。最后使用 create 就生成了 cglib 的一个代理类。
题目解析
和 jdk 的动态代理异曲同工,相比 jdk 反而 cglib 的更常考一点。大家理解一下下面的代码流程就很容易说出其中的过程。
面试得分点
目标类,方法拦截器,创建代理对象
题目详细答案
CGLIB是一种强大的代码生成库,能够在运行时生成代理类。与 JDK 动态代理不同的是,CGLIB 不需要接口,可以直接代理具体类。CGLIB 通过创建目标类的子类并覆盖其中的方法来实现代理。
实现步骤
1、 引入 CGLIB 库:确保在项目中添加 CGLIB 依赖。
2、 创建目标类:定义需要代理的具体类。
3、 创建方法拦截器:实现MethodInterceptor接口,并在intercept方法中定义代理逻辑。
4、 创建代理对象:通过Enhancer类创建代理对象。
代码 Demo
假设我们有一个简单的服务类MyService,通过 CGLIB 动态代理为MyService创建一个代理对象,并在方法调用前后添加日志。
引入 CGLIB 依赖
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
创建目标类
public class MyService {public void performTask() {System.out.println("Performing task");}
}
创建方法拦截器
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class LoggingMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Logging before method execution: " + method.getName());Object result = proxy.invokeSuper(obj, args);System.out.println("Logging after method execution: " + method.getName());return result;}
}
创建代理对象并使用
import net.sf.cglib.proxy.Enhancer;public class MainApp {public static void main(String[] args) {// 创建 Enhancer 对象Enhancer enhancer = new Enhancer();// 设置目标类为代理类的父类enhancer.setSuperclass(MyService.class);// 设置方法拦截器enhancer.setCallback(new LoggingMethodInterceptor());// 创建代理对象MyService proxyInstance = (MyService) enhancer.create();// 调用代理对象的方法proxyInstance.performTask();}
}
详细解释
引入 CGLIB 库:在项目中添加 CGLIB 依赖,以便使用 CGLIB 提供的类和接口。
目标类:MyService是一个具体类,定义了一个方法performTask。
方法拦截器:LoggingMethodInterceptor实现了MethodInterceptor接口。它的intercept方法在代理对象的方法调用时被调用。
intercept方法接收四个参数:obj:代理对象。method:被调用的方法。args:方法参数。proxy:用于调用父类方法的代理对象。
在intercept方法中,我们在方法调用前后添加了日志打印,并通过proxy.invokeSuper调用父类的方法。
创建代理对象:使用Enhancer类创建代理对象。setSuperclass方法设置目标类为代理类的父类。setCallback方法设置方法拦截器。create方法创建代理对象。
使用代理对象:
通过代理对象调用方法时,实际调用的是LoggingMethodInterceptor的intercept方法。
在intercept方法中,首先打印日志,然后通过proxy.invokeSuper调用目标对象的方法,最后再打印日志。
CGLIB动态代理通俗解析
面试官您好,关于CGLIB动态代理,我用一个更形象的例子来解释:
魔术师学徒的比喻
想象你是个魔术师(目标类),但想增加些新把戏:
- 你本人(MyService):只会基础魔术
- 学徒(代理类):继承你的所有技能
- 魔术秘籍(MethodInterceptor):教学徒在原有魔术前后添加新动作
实现步骤详解
1. 创建目标类(魔术师本人)
public class Magician {public void performTrick() {System.out.println("从帽子里变出兔子");}public final void secretMove() {System.out.println("这是独门绝技");}
}
2. 编写魔术秘籍(拦截器)
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;public class MagicEnhancer implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("【开场】撒魔术粉");// 调用父类(原魔术师)的方法Object result = proxy.invokeSuper(obj, args);System.out.println("【退场】鞠躬致谢");return result;}
}
3. 培养学徒(生成代理)
import net.sf.cglib.proxy.Enhancer;public class MagicShow {public static void main(String[] args) {// 创建增强器(魔术培训班)Enhancer enhancer = new Enhancer();// 指定师父(设置父类)enhancer.setSuperclass(Magician.class);// 传授秘籍(设置回调)enhancer.setCallback(new MagicEnhancer());// 培养出学徒(创建代理)Magician apprentice = (Magician) enhancer.create();// 学徒表演apprentice.performTrick();// 尝试调用final方法// apprentice.secretMove(); // 会报错,无法代理final方法}
}
输出结果
【开场】撒魔术粉
从帽子里变出兔子
【退场】鞠躬致谢
关键点说明
- 不需要接口:可以直接对普通类进行代理
- 继承机制:通过生成子类来覆盖方法
- MethodProxy:比JDK的Method调用效率更高
- 限制:
- 无法代理final方法(如上例的secretMove)
- 无法代理private方法
与JDK代理对比
特性 | CGLIB | JDK动态代理 |
原理 | 生成子类继承目标类 | 实现相同接口 |
速度 | 方法调用更快 | 反射调用稍慢 |
内存 | 需要生成类字节码,占用更多内存 | 占用较少 |
限制 | 不能代理final方法 | 必须实现接口 |
实际项目经验
在Spring项目中:
- Controller层通常用CGLIB代理(因为经常没有接口)
- 事务管理(@Transactional)底层用CGLIB
- 缓存(@Cacheable)也是基于CGLIB
优化建议:
// 可以设置不生成不必要的字段
enhancer.setStrategy(new DefaultGeneratorStrategy() {@Overrideprotected ClassGenerator transform(ClassGenerator cg) {return new TransformingClassGenerator(cg, new AddPropertyTransformer(new String[]{"final"}, new Class[]{boolean.class}));}
});
这样设计既保留了原有类的功能,又能灵活添加新特性,是Spring AOP的重要实现基础。