关于认识静态代理,与动态代理,与aop与事务--认识动态代理,和静态代理(一)

这里提供一个认识代理,以及这些,的一脉思路

我们从几个层面,来认识这些思路的实现。

从逻辑上理解。

所谓的代理,其实就是在代码层面,抽象出来一些公共要做的事情,然后可以省略出来。

从代理的实现方式来说,我们可以分为静态代理:

这里先做一个小的解释,背景说明。

在代码实现层面,所谓的静态代理,就是把原来的类,实例化成对象,然后再这个类的方法上面,下面,写更多的代码,做更多的事情。

上代码,给个demo:

静态代理实现接口代理示例

下面我将展示一个完整的静态代理模式示例,用于代理接口的实现。

1. 基础接口定义

java 复制代码
// 1. 定义接口
public interface UserService {
    void addUser(String username);
    String getUser(int id);
    void deleteUser(int id);
}

2. 真实实现类

java 复制代码
// 2. 真实实现类
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username) {
        System.out.println("真实操作:添加用户 " + username);
        // 这里可以是实际的数据库操作
    }
    
    @Override
    public String getUser(int id) {
        System.out.println("真实操作:获取用户,ID = " + id);
        return "用户" + id;
    }
    
    @Override
    public void deleteUser(int id) {
        System.out.println("真实操作:删除用户,ID = " + id);
    }
}

3. 静态代理类

java 复制代码
// 3. 静态代理类
public class UserServiceProxy implements UserService {
    // 持有真实对象的引用
    private UserService realService;
    
    // 可以通过构造器注入真实对象
    public UserServiceProxy(UserService realService) {
        this.realService = realService;
    }
    
    @Override
    public void addUser(String username) {
        // 前置增强
        System.out.println("【代理日志】开始执行 addUser 方法");
        System.out.println("【参数校验】用户名: " + username);
        
        try {
            // 调用真实对象的方法
            realService.addUser(username);
            
            // 后置增强
            System.out.println("【代理日志】addUser 方法执行成功");
        } catch (Exception e) {
            // 异常增强
            System.out.println("【代理日志】addUser 方法执行异常: " + e.getMessage());
            throw e;
        } finally {
            // 最终增强
            System.out.println("【代理日志】addUser 方法执行结束\n");
        }
    }
    
    @Override
    public String getUser(int id) {
        System.out.println("【代理日志】开始执行 getUser 方法");
        System.out.println("【参数校验】用户ID: " + id);
        
        if (id <= 0) {
            System.out.println("【参数校验】ID必须大于0");
            return null;
        }
        
        long startTime = System.currentTimeMillis();
        String result = realService.getUser(id);
        long endTime = System.currentTimeMillis();
        
        System.out.println("【性能监控】getUser 方法执行耗时: " + (endTime - startTime) + "ms");
        System.out.println("【代理日志】getUser 方法执行结束,返回值: " + result + "\n");
        
        return result;
    }
    
    @Override
    public void deleteUser(int id) {
        System.out.println("【代理日志】开始执行 deleteUser 方法");
        System.out.println("【权限校验】检查删除权限...");
        
        // 模拟权限校验
        if (id == 1) {
            System.out.println("【权限校验】无权删除管理员用户");
            return;
        }
        
        realService.deleteUser(id);
        System.out.println("【代理日志】deleteUser 方法执行结束\n");
    }
    
    // 代理类可以有自己的额外方法
    public void showProxyInfo() {
        System.out.println("=== 代理信息 ===");
        System.out.println("代理类: " + this.getClass().getName());
        System.out.println("真实类: " + realService.getClass().getName());
        System.out.println("================\n");
    }
}

4. 测试类

java 复制代码
// 4. 测试类
public class StaticProxyDemo {
    public static void main(String[] args) {
        System.out.println("======= 静态代理模式演示 =======\n");
        
        // 创建真实对象
        UserService realService = new UserServiceImpl();
        
        // 创建代理对象,传入真实对象
        UserServiceProxy proxy = new UserServiceProxy(realService);
        
        // 显示代理信息
        proxy.showProxyInfo();
        
        // 通过代理对象调用方法
        System.out.println("1. 测试 addUser 方法:");
        proxy.addUser("张三");
        
        System.out.println("2. 测试 getUser 方法:");
        String user = proxy.getUser(1001);
        System.out.println("获取到的用户: " + user);
        
        System.out.println("3. 测试参数校验:");
        proxy.getUser(-1);
        
        System.out.println("4. 测试权限控制:");
        proxy.deleteUser(1);  // 尝试删除管理员
        proxy.deleteUser(1001); // 删除普通用户
        
        System.out.println("======= 演示结束 =======");
    }
}

我们在来认识一下动态代理。

先做一个简单的说明。

所谓的动态代理,我们针对的对象,还是类。

他相比静态代理,实现的效果,都是对类的方法的信息的增加,但是实现方式不一样。

我们看一个demo的例子:

口语化解释:什么是动态代理?

想象这样一个场景:

你是一个明星,我是你的经纪人。有公司想找你拍广告,他们不会直接找你,而是先找我这个经纪人。

静态代理就是:

  • 我是你专属的经纪人,只代理你一个人
  • 如果又来了一个歌手要找经纪人,需要再专门雇一个经纪人
  • 每个明星都要配一个专门的经纪人,成本很高

动态代理就是:

  • 我是一个"经纪人公司"
  • 你(明星)来了,我马上为你生成一个专属经纪人
  • 歌手来了,我也马上生成一个专属经纪人
  • 但我这个"经纪人公司"的核心处理逻辑是一样的:先谈价格、签合同、安排日程、收钱
  • 我不用为每个明星提前写好经纪人方案,而是运行时根据需要动态生成

动态代理比静态代理好在哪里?

  1. 不用写重复代码

    • 静态代理:10个接口就要写10个代理类
    • 动态代理:写1个处理器,就能代理所有接口
  2. 维护方便

    • 静态代理:接口加方法,所有代理类都要跟着改
    • 动态代理:只需要改处理器逻辑,所有代理都生效
  3. 更加灵活

    • 可以运行时决定代理谁
    • 可以动态添加功能

最简单的Demo实现

第一步:明星(接口)

java 复制代码
// 明星会做的事
interface Star {
    void sing();  // 唱歌
    void act();   // 演戏
}

第二步:真实的明星

java 复制代码
// 真实的周杰伦
class RealStar implements Star {
    public void sing() {
        System.out.println("周杰伦在唱《七里香》");
    }
    
    public void act() {
        System.out.println("周杰伦在演《头文字D》");
    }
}

第三步:经纪人公司(动态代理核心)

java 复制代码
import java.lang.reflect.*;

// 经纪人公司 - 动态代理核心
public class BrokerCompany {
    public static void main(String[] args) {
        // 真实的明星
        Star realStar = new RealStar();
        
        // 动态创建经纪人(代理)
        Star agent = (Star) Proxy.newProxyInstance(
            realStar.getClass().getClassLoader(),  // 用明星的类加载器
            new Class[]{Star.class},               // 明星会的技能(接口)
            new InvocationHandler() {              // 统一的处理逻辑
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 演出前:谈价格、签合同
                    System.out.println("经纪人:先谈价格,签合同");
                    
                    // 让明星真正演出
                    Object result = method.invoke(realStar, args);
                    
                    // 演出后:收钱、结账
                    System.out.println("经纪人:演出结束,收钱结账");
                    
                    return result;
                }
            }
        );
        
        // 现在找明星演出,实际上是通过经纪人
        System.out.println("=== 找明星唱歌 ===");
        agent.sing();
        
        System.out.println("\n=== 找明星演戏 ===");
        agent.act();
    }
}

运行结果:

复制代码
=== 找明星唱歌 ===
经纪人:先谈价格,签合同
周杰伦在唱《七里香》
经纪人:演出结束,收钱结账

=== 找明星演戏 ===
经纪人:先谈价格,签合同
周杰伦在演《头文字D》
经纪人:演出结束,收钱结账

关键理解点:

  1. Proxy.newProxyInstance - 这个方法是"经纪人公司",能动态创建经纪人
  2. InvocationHandler - 这是经纪人的"工作流程模板"
  3. 运行时生成 - 代码里没有Star的代理类,是运行时生成的
  4. 统一处理 - 不管明星是唱歌还是演戏,经纪人的工作流程都一样

如果再来个新明星?

java 复制代码
// 新明星:林俊杰
class AnotherStar implements Star {
    public void sing() {
        System.out.println("林俊杰在唱《江南》");
    }
    
    public void act() {
        System.out.println("林俊杰在演音乐剧");
    }
}

// 用同一个经纪人公司代理新明星
Star anotherAgent = (Star) Proxy.newProxyInstance(
    anotherStar.getClass().getClassLoader(),
    new Class[]{Star.class},
    handler  // 同样的处理逻辑!
);

这个是jdk的基于接口,实现的动态代理,是aop的底层实现。

我们在来看基于cglib的动态代理:

CGLIB动态代理Demo

一、CGLIB vs JDK动态代理对比

特性 JDK动态代理 CGLIB动态代理
代理对象 只能代理接口 可以代理普通类
原理 实现接口,继承Proxy 继承目标类,生成子类
性能 反射调用较慢 方法调用较快(直接调用父类)
依赖 JDK自带 需要额外jar包

二、完整Demo实现

第一步:添加Maven依赖

xml 复制代码
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

第二步:创建目标类(不需要接口!)

java 复制代码
// 1. 目标类 - 注意:没有实现任何接口!
public class UserService {
    
    // 普通方法
    public void addUser(String name) {
        System.out.println("真实操作:添加用户 " + name);
    }
    
    // final方法 - CGLIB无法代理
    public final void finalMethod() {
        System.out.println("这是一个final方法,不能被代理");
    }
    
    // private方法 - CGLIB无法代理
    private void privateMethod() {
        System.out.println("这是一个private方法");
    }
    
    // 有返回值的方法
    public String getUser(int id) {
        System.out.println("真实操作:查询用户 " + id);
        return "用户" + id;
    }
    
    // 静态方法 - CGLIB无法代理
    public static void staticMethod() {
        System.out.println("这是一个静态方法");
    }
}

第三步:创建方法拦截器(类似InvocationHandler)

java 复制代码
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// 2. 方法拦截器 - 核心类
public class CglibInterceptor implements MethodInterceptor {
    
    // 目标对象
    private Object target;
    
    public CglibInterceptor(Object target) {
        this.target = target;
    }
    
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        String methodName = method.getName();
        
        System.out.println("【CGLIB代理】开始执行方法: " + methodName);
        
        // 过滤掉final、static、private等方法
        if ("finalMethod".equals(methodName) || 
            "privateMethod".equals(methodName) ||
            "staticMethod".equals(methodName)) {
            System.out.println("【CGLIB代理】跳过无法代理的方法: " + methodName);
            return method.invoke(target, args);
        }
        
        // 前置增强
        System.out.println("【CGLIB代理】前置处理,参数: " + (args != null ? java.util.Arrays.toString(args) : "无"));
        
        long startTime = System.currentTimeMillis();
        
        // 关键区别:这里有两种调用方式
        Object result = null;
        
        // 方式1:使用MethodProxy(更快,推荐)
        // 直接调用父类方法,避免反射
        result = proxy.invokeSuper(obj, args);
        
        // 方式2:使用反射(较慢)
        // result = method.invoke(target, args);
        
        long endTime = System.currentTimeMillis();
        
        // 后置增强
        System.out.println("【CGLIB代理】后置处理,返回值: " + result);
        System.out.println("【CGLIB代理】方法耗时: " + (endTime - startTime) + "ms\n");
        
        return result;
    }
}

第四步:创建CGLIB代理工厂

java 复制代码
import net.sf.cglib.proxy.Enhancer;

// 3. CGLIB代理工厂
public class CglibProxyFactory {
    
    /**
     * 创建代理对象
     * @param targetClass 目标类
     * @return 代理对象
     */
    public static <T> T createProxy(Class<T> targetClass) {
        // 创建增强器
        Enhancer enhancer = new Enhancer();
        
        // 设置父类(目标类)
        enhancer.setSuperclass(targetClass);
        
        // 设置回调(拦截器)
        enhancer.setCallback(new CglibInterceptor(targetClass));
        
        // 设置构造器策略 - 使用无参构造器
        enhancer.setStrategy(null);
        
        // 创建代理对象
        return (T) enhancer.create();
    }
    
    /**
     * 创建代理对象(带自定义拦截器)
     */
    public static <T> T createProxy(Class<T> targetClass, MethodInterceptor interceptor) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(interceptor);
        return (T) enhancer.create();
    }
    
    /**
     * 创建代理对象(需要目标实例)
     */
    public static <T> T createProxy(T target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new CglibInterceptor(target));
        return (T) enhancer.create();
    }
}

第五步:测试CGLIB代理

java 复制代码
// 4. 测试类
public class CglibDemo {
    public static void main(String[] args) {
        System.out.println("======= CGLIB动态代理演示 =======\n");
        
        // 注意:我们没有创建UserService的实例!
        // CGLIB会帮我们创建子类实例
        
        System.out.println("1. 创建CGLIB代理对象");
        UserService proxy = CglibProxyFactory.createProxy(UserService.class);
        
        System.out.println("\n2. 查看代理对象信息");
        System.out.println("代理对象类名: " + proxy.getClass().getName());
        System.out.println("父类: " + proxy.getClass().getSuperclass().getName());
        System.out.println("是否继承自UserService: " + UserService.class.isAssignableFrom(proxy.getClass()));
        
        System.out.println("\n3. 调用普通方法");
        proxy.addUser("张三");
        
        System.out.println("\n4. 调用有返回值的方法");
        String user = proxy.getUser(1001);
        System.out.println("主程序收到: " + user);
        
        System.out.println("\n5. 调用final方法(不会被代理增强)");
        proxy.finalMethod();
        
        System.out.println("\n6. 测试方法过滤");
        try {
            // 尝试调用private方法(会报错)
            // proxy.privateMethod(); // 编译错误
            
            // 尝试调用static方法
            UserService.staticMethod(); // 正常调用,但不会被代理
        } catch (Exception e) {
            System.out.println("异常: " + e.getMessage());
        }
        
        System.out.println("\n7. 查看代理对象的实际类型");
        System.out.println("生成的代理类名: " + proxy.getClass().getName());
        System.out.println("方法列表:");
        for (java.lang.reflect.Method method : proxy.getClass().getDeclaredMethods()) {
            System.out.println("  - " + method.getName() + 
                             " (来自: " + method.getDeclaringClass().getSimpleName() + ")");
        }
    }
}

第六步:深入了解CGLIB生成的类

java 复制代码
// 5. 查看CGLIB生成的字节码
import net.sf.cglib.core.DebuggingClassWriter;

public class CglibDeepDive {
    public static void main(String[] args) {
        // 保存CGLIB生成的类文件到磁盘
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./cglib-classes");
        
        // 创建代理
        UserService proxy = CglibProxyFactory.createProxy(UserService.class);
        
        // 调用方法
        proxy.addUser("李四");
        
        System.out.println("\n=== 生成的类文件已保存到 ./cglib-classes ===");
        System.out.println("你可以用反编译工具查看这些文件:");
        System.out.println("1. UserService$$EnhancerByCGLIB$$xxxxxxxx.class - 代理类");
        System.out.println("2. UserService$$FastClassByCGLIB$$xxxxxxxx.class - 快速调用类");
        System.out.println("3. CglibInterceptor$$FastClassByCGLIB$$xxxxxxxx.class");
    }
}

第七步:查看生成的代理类(反编译结果)

java 复制代码
// 这是CGLIB生成的代理类的伪代码(实际是字节码,这里用Java表示)

public class UserService$$EnhancerByCGLIB$$a1b2c3d4 extends UserService {
    
    // 生成的字段
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$addUser$0$Method;
    private static final MethodProxy CGLIB$addUser$0$Proxy;
    
    static {
        // 初始化Method和MethodProxy
        CGLIB$addUser$0$Method = UserService.class.getMethod("addUser", String.class);
        CGLIB$addUser$0$Proxy = MethodProxy.create(
            UserService.class, 
            UserService$$EnhancerByCGLIB$$a1b2c3d4.class, 
            "addUser", 
            "(Ljava/lang/String;)V", 
            "CGLIB$addUser$0"
        );
    }
    
    // 重写父类方法
    public final void addUser(String name) {
        MethodInterceptor interceptor = this.CGLIB$CALLBACK_0;
        if (interceptor != null) {
            // 调用拦截器
            interceptor.intercept(this, 
                CGLIB$addUser$0$Method, 
                new Object[]{name}, 
                CGLIB$addUser$0$Proxy);
        } else {
            // 直接调用父类
            super.addUser(name);
        }
    }
    
    // CGLIB生成的方法(用于快速调用)
    public void CGLIB$addUser$0(String name) {
        super.addUser(name);  // 直接调用父类,没有增强!
    }
    
    // 重写其他方法...
    public final String getUser(int id) {
        // 类似实现...
    }
}

第八步:性能对比测试

java 复制代码
// 6. CGLIB vs JDK性能对比
public class ProxyPerformanceTest {
    
    interface IUserService {
        String getUser(int id);
    }
    
    static class UserServiceImpl implements IUserService {
        public String getUser(int id) {
            return "User" + id;
        }
    }
    
    static class UserServiceConcrete {
        public String getUser(int id) {
            return "User" + id;
        }
    }
    
    public static void main(String[] args) {
        int iterations = 10_000_000;
        
        System.out.println("性能对比测试 (迭代次数: " + iterations + ")");
        
        // 1. 直接调用
        UserServiceConcrete direct = new UserServiceConcrete();
        long start = System.currentTimeMillis();
        for (int i = 0; i < iterations; i++) {
            direct.getUser(i);
        }
        System.out.println("直接调用耗时: " + (System.currentTimeMillis() - start) + "ms");
        
        // 2. CGLIB代理调用
        UserServiceConcrete cglibProxy = CglibProxyFactory.createProxy(UserServiceConcrete.class);
        start = System.currentTimeMillis();
        for (int i = 0; i < iterations; i++) {
            cglibProxy.getUser(i);
        }
        System.out.println("CGLIB代理调用耗时: " + (System.currentTimeMillis() - start) + "ms");
        
        // 3. JDK动态代理调用
        IUserService jdkProxy = (IUserService) java.lang.reflect.Proxy.newProxyInstance(
            ProxyPerformanceTest.class.getClassLoader(),
            new Class[]{IUserService.class},
            new java.lang.reflect.InvocationHandler() {
                UserServiceImpl target = new UserServiceImpl();
                public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
                    return method.invoke(target, args);
                }
            }
        );
        
        start = System.currentTimeMillis();
        for (int i = 0; i < iterations; i++) {
            jdkProxy.getUser(i);
        }
        System.out.println("JDK代理调用耗时: " + (System.currentTimeMillis() - start) + "ms");
    }
}

三、CGLIB关键特性总结

1. 继承方式

java 复制代码
// CGLIB生成的代理类继承目标类
public class ProxyClass extends TargetClass {
    // 重写所有非final方法
}

2. 方法调用优化

java 复制代码
// 使用MethodProxy,比反射快
proxy.invokeSuper(obj, args);  // 直接调用父类方法

// vs JDK的反射调用
method.invoke(target, args);  // 反射调用,较慢

3. 限制

  • 不能代理final方法
  • 不能代理private方法
  • 不能代理static方法
  • 需要目标类有无参构造器

4. Spring中的使用

java 复制代码
// Spring AOP的配置
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)  // 强制使用CGLIB

// 或者
<aop:aspectj-autoproxy proxy-target-class="true"/>

四、运行结果示例

复制代码
======= CGLIB动态代理演示 =======

1. 创建CGLIB代理对象

2. 查看代理对象信息
代理对象类名: UserService$$EnhancerByCGLIB$$12345678
父类: UserService
是否继承自UserService: true

3. 调用普通方法
【CGLIB代理】开始执行方法: addUser
【CGLIB代理】前置处理,参数: [张三]
真实操作:添加用户 张三
【CGLIB代理】后置处理,返回值: null
【CGLIB代理】方法耗时: 2ms

4. 调用有返回值的方法
【CGLIB代理】开始执行方法: getUser
【CGLIB代理】前置处理,参数: [1001]
真实操作:查询用户 1001
【CGLIB代理】后置处理,返回值: 用户1001
【CGLIB代理】方法耗时: 1ms
主程序收到: 用户1001

5. 调用final方法(不会被代理增强)
真实操作:这是一个final方法,不能被代理

这就是CGLIB动态代理的核心原理和实现方式!

看完这3个demo,我们可以理解一下,所谓的代理,就是在原来类的方法的基础上,给方法增加更多的能力和信息。作为一个新的类。也就是中间层和代理

相关推荐
qq_297574671 天前
【实战教程】SpringBoot 实现多文件批量下载并打包为 ZIP 压缩包
java·spring boot·后端
计算机程序设计小李同学1 天前
基于 Spring Boot + Vue 的龙虾专营店管理系统的设计与实现
java·spring boot·后端·spring·vue
Charlie_lll1 天前
力扣解题-[3379]转换数组
数据结构·后端·算法·leetcode
VX:Fegn08951 天前
计算机毕业设计|基于springboot + vue云租车平台系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
汤姆yu1 天前
2026基于springboot的在线招聘系统
java·spring boot·后端
计算机学姐1 天前
基于SpringBoot的校园社团管理系统
java·vue.js·spring boot·后端·spring·信息可视化·推荐算法
hssfscv1 天前
Javaweb学习笔记——后端实战8 springboot原理
笔记·后端·学习
咚为1 天前
Rust tokio:Task ≠ Thread:Tokio 调度模型中的“假并发”与真实代价
开发语言·后端·rust
Anastasiozzzz1 天前
对抗大文件上传---分片加多重Hash判重
服务器·后端·算法·哈希算法
Vivienne_ChenW1 天前
DDD领域模型在项目中的实战
java·开发语言·后端·设计模式