java动态代理之cglib

Cglib动态代理实战
本文介绍Cglib动态代理的原理及应用,通过实战演示如何利用Cglib进行方法拦截,实现对方法执行时间的监控。同时对比了Cglib与JDK动态代理的区别,展示了不依赖接口实现的动态代理方案。

        cglib是一个强大的高性能的代码生成包,他广泛的被许多的AOP框架使用,例如Spring AOP 和dynaop ,为他们提供方法的拦截(interception),最流行的OR Mapping 工具hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联。在这里要说明的是,jdk提供了动态代理功能,但是被代理类必须要有接口,否则是生成不了代理的,但是cglib生成代理时,不需要目标类有接口,因为它借助了asm直接修改class文件的字节码。

     

      就这个动态代理说开来,其实spring的aop不是什么神秘的东西,就是借助动态代理来实现的,并且spring内置了两种代理生成方式,一个就是jdk动态代理API,一个就是cglib。典型的spring组件如声明式的事务管理,通知等。spring的IOC更简单,根据配置文件反射而已。这些东西自己实现并不是什么难事,但是要做到spring那样完善和庞大就是另外一回事了。spring提供的测试框架的mock模块当然也是利用了动态代理的。

下面看看demo吧,具体可自行查阅cglib文档
①:创建一个简单的类,注意该类不能声明为final

QueryDAO.java

package com.cglib.proxy;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @version 1.0
 * @history
 */
public class QueryDAO {

   
    @SuppressWarnings("unchecked")
    public List executeQuery(String userName) throws Exception{
        List list = this.queryForList(userName);
        return list;
    }
   
   
    @SuppressWarnings({ "unchecked", "static-access" })
    public List queryForList(String userName){
        try {
            Thread.currentThread().sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Map<String,String> paramMap = new HashMap<String, String>();
        paramMap.put("userName", userName);
        List list = new ArrayList();
        list.add(paramMap);
        return list;
    }
}


②:创建性能检测类PerformanceMonitor,需要实现MethodInterceptor(方法拦截)
PerformanceMonitor.java

package com.cglib.proxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * @version 1.0
 * @history
 */
public class PerformanceMonitor implements MethodInterceptor {

    private Object target;
    private Enhancer enhander = new Enhancer();

    public Object createProxy(Object target){
        this.target = target;
        enhander.setSuperclass(target.getClass()); //指定父类
        enhander.setCallback(this); //回调当前类
        return enhander.create(); //创建代理类实例
    }


    public Object intercept(Object arg0, Method method, Object[] param,
            MethodProxy proxyMethod) throws Throwable {
        long startTime = System.currentTimeMillis(); //开始时间
        Object result = method.invoke(this.target, param);  //反射回调方法
        long endTime = System.currentTimeMillis(); //结束时间
        System.out.println("执行该方法所需时间:" + (endTime-startTime)+"毫秒" );
        return result;
    }

}


③:Cglib代理类测试效果:
CglibProxyTest.java

package com.cglib.proxy;

public class CglibProxyTest {

    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        QueryDAO target = new QueryDAO(); //声明被代理类
        PerformanceMonitor performance = new PerformanceMonitor();
        QueryDAO dao = (QueryDAO)performance.createProxy(target);
        dao.executeQuery("admin");
    }

}


输入结果
执行该方法所需时间:187毫秒

<think> 我们正在讨论Java动态代理CGLIB,需要从原理、使用场景和区别三个方面展开。参考引用内容,我们可以总结如下: 1. **JDK动态代理**: - 原理:基于接口,使用`java.lang.reflect.Proxy`和`InvocationHandler`创建代理对象。 - 要求:被代理类必须实现至少一个接口。 - 应用场景:Spring AOP中,如果目标对象实现了接口,则默认使用JDK动态代理。 2. **CGLIB代理**: - 原理:通过字节码技术,生成被代理类的子类作为代理对象(继承方式)。 - 要求:被代理类不能是final类,因为需要继承。方法也不能是final,否则无法重写。 - 应用场景:代理没有实现接口的类。Spring AOP中,如果目标对象没有实现接口,则使用CGLIB。 根据引用[4]和[5],我们可以明确两者的区别。 下面按照要求组织回答,使用中文,注意数学公式的表示(虽然本主题可能较少涉及数学公式,但如果有表达式,按照规则书写),并在适当位置添加引用标识。 同时,在最后生成相关问题。 </think> ### Java 动态代理CGLIB 深度解析 #### 一、核心原理对比 | 特性 | JDK 动态代理 | CGLIB 代理 | |--------------|-------------------------------------------|-------------------------------------------| | **实现基础** | 基于接口(`java.lang.reflect.Proxy`) | 基于继承(字节码增强) | | **代理对象** | 实现相同接口的代理类 | 目标类的子类 $$ \text{Proxy} = \text{Target} + \Delta $$ | | **方法调用** | 通过 `InvocationHandler.invoke()` 转发 | 通过 `MethodInterceptor.intercept()` 拦截 | | **创建方式** | `Proxy.newProxyInstance()` | `Enhancer.create()` | **原理示意图**: ```mermaid graph LR A[JDK动态代理] --> B[接口] B --> C[代理对象] C --> D[InvocationHandler] E[CGLIB代理] --> F[目标类] F --> G[子类代理] G --> H[MethodInterceptor] ``` #### 二、使用场景分析 | 场景 | JDK 动态代理 | CGLIB 代理 | |--------------------------|---------------------------|---------------------------| | **目标类有接口** | ✅ 首选方案 | ⚠️ 可用但非必要 | | **目标类无接口** | ❌ 不可用 | ✅ 唯一选择 | | **final 类/方法** | - | ❌ 无法代理 | | **Spring AOP 默认策略** | 有接口时使用 | 无接口时自动切换 | | **性能敏感场景** | 调用快,创建慢 | 创建快,调用稍慢 | **典型应用**: 1. **JDK 代理**:Spring 事务管理(基于接口的服务层) ```java public interface UserService { void saveUser(User user); } // 代理创建 UserService proxy = (UserService) Proxy.newProxyInstance( loader, new Class[]{UserService.class}, handler ); ``` [^2] 2. **CGLIB 代理**:Hibernate 延迟加载(实体类无接口) ```java Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(User.class); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) { // 延迟加载逻辑 return proxy.invokeSuper(obj, args); } }); User proxy = (User) enhancer.create(); ``` [^5] #### 三、核心区别详解 | 维度 | JDK 动态代理 | CGLIB 代理 | |--------------|---------------------------------------|-----------------------------------------| | **技术依赖** | Java 原生 API | ASM 字节码库(需单独引入) | | **代理范围** | 仅接口方法 | 除 final 方法外的所有方法 | | **性能** | 创建代理慢(反射),调用快 | 创建代理快(直接生成类),调用稍慢 | | **内存** | 无额外类加载 | 生成新类(PermGen/Metaspace 压力) | | **兼容性** | 所有 JDK 版本 | 需处理字节码版本兼容问题 | **性能对比公式**(n次调用): $$ \begin{cases} T_{\text{JDK}} \approx t_{\text{create}} + n \cdot t_{\text{invoke}} \\ T_{\text{CGLIB}} \approx t_{\text{generate}} + n \cdot (t_{\text{intercept}} + \epsilon) \end{cases} $$ 其中 $t_{\text{create}} \gg t_{\text{generate}}$,但 $t_{\text{invoke}} < t_{\text{intercept}}$[^3] #### 四、选择决策树 ```mermaid graph TD A[需要代理] --> B{目标类实现接口?} B -->|是| C[JDK动态代理] B -->|否| D{类/方法是否为final?} D -->|否| E[CGLIB代理] D -->|是| F[无法代理 重构代码] C --> G[考虑性能?] G -->|是| H[预创建代理对象] E --> I[注意OOM风险] ``` #### 五、Spring 框架中的应用 1. **代理选择策略**: ```java // Spring AOP 自动选择逻辑 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); ``` [^1] 2. **强制 CGLIB**: ```java @EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用 CGLIB ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值