关于认识静态代理,与动态代理,与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,我们可以理解一下,所谓的代理,就是在原来类的方法的基础上,给方法增加更多的能力和信息。作为一个新的类。也就是中间层和代理

相关推荐
loading小马2 小时前
Mybatis-Plus超级实用的多种功能用法
java·spring boot·后端·maven·mybatis
计算机学姐3 小时前
基于SpringBoot的自习室座位预定系统【预约选座+日期时间段+协同过滤推荐算法+数据可视化统计】
java·vue.js·spring boot·后端·spring·信息可视化·tomcat
Marktowin3 小时前
控制权限系列之(2)手把手教你使用基于角色的权限控制
后端
仙俊红3 小时前
Spring Cloud 核心组件部署方式速查表
后端·spring·spring cloud
码农幻想梦4 小时前
实验九 Restful和ajax实现
后端·ajax·restful
今天多喝热水4 小时前
SpEL(Spring Expression Language) 表达式
java·后端·spring
码农水水4 小时前
浅谈 MySQL InnoDB 的内存组件
java·开发语言·数据库·后端·mysql·面试
独自破碎E4 小时前
Spring Boot的多环境配置
java·spring boot·后端
Edward-tan5 小时前
【玩转全栈】----Django模板语法、请求与响应
后端·python·django