spring AOP之代理

1.代理概念

  • 什么是代理
    • 为某一个对象创建一个代理对象,程序不直接用原本的对象,而是由创建的代理对象来控制原对象,通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好的隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间
  • 什么是静态代理
    • 由程序创建或特定工具自动生成源代码,在程序运行前,代理类的.class文件就已经存在
  • 什么是动态代理
    • 在程序运行时,运用反射机制动态创建而成,无需手动编写代码
      • JDK动态代理
      • CGLib动态代理

2.静态代理

  • 什么是静态代理

    • 由程序创建或特定工具自动生成源代码,在程序运行前,代理类的.class文件就已经存在
    • 通过将目标类与代理类实现同一个接口,让代理类持有真实类对象,然后在代理类方法中调用真实类方法,在调用真实类方法的前后添加我们所需要的功能扩展代码来达到增强的目的
  • 优点

    • 代理使客户端不需要知道实现类是什么怎么做的,而客户端只需知道代理即可
    • 方便增加功能、扩展业务逻辑
  • 缺点

    • 代理类中出现大量冗余的代码,非常不利于扩展和维护
    • 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法,增加了代码维护的复杂度
  • 代码示例

    java 复制代码
    package com.gen;
    
    interface PayService {
        void pay();
    }
    
    class AliPayService implements PayService {
        @Override
        public void pay() {
            System.out.println("支付宝支付");
        }
    }
    
    class StaticProxyPayService implements PayService {
    
        private PayService payService;
    
        public StaticProxyPayService(PayService payService) {
            this.payService = payService;
        }
    
        @Override
        public void pay() {
            System.out.println("代理代码begin");
            this.payService.pay();
            System.out.println("代理代码end");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            new StaticProxyPayService(new AliPayService()).pay();
        }
    }

3.JDK动态代理

  • JDK动态代理与静态代理一样,目标类需要实现一个代理接口,再通过代理对象调用目标方法

  • 编写流程

    复制代码
    定义一个java.lang.reflect.InvocationHandler接口的实现类,重写invoke方法
    
    public interface InvocationHandler {
    
        /**
         * @param proxy  被代理的对象
         * @param method 要调用的方法
         * @param args   方法调用时所需参数
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    }
  • 代码示例

    java 复制代码
    package com.gen;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    interface PayService {
        void pay();
    }
    
    class AliPayService implements PayService {
        @Override
        public void pay() {
            System.out.println("支付宝支付");
        }
    }
    
    class JdkProxy implements InvocationHandler {
    
        /**
         * 目标对象
         */
        private Object targetObj;
    
        /**
         * 获取代理对象
         *
         * @param targetObj
         * @return
         */
        public Object newProxyInstance(Object targetObj) {
            this.targetObj = targetObj;
            // 绑定关系,也就是和具体的哪个实现类关联
            return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(), targetObj.getClass().getInterfaces(), this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = null;
            try {
                System.out.println("JDK代理代码begin");
                result = method.invoke(targetObj, args);
                System.out.println("JDK代理代码end");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            PayService payService = (PayService) new JdkProxy().newProxyInstance(new AliPayService());
            payService.pay();
        }
    }

4.CGLib动态代理

  • CGLib动态代理的原理是对指定的业务类生成一个子类,并覆盖其中的业务方法来实现代理

  • 代码示例

    java 复制代码
    package com.gen;
    
    
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    interface PayService {
        void pay();
    }
    
    class AliPayService implements PayService {
        @Override
        public void pay() {
            System.out.println("支付宝支付");
        }
    }
    
    class CglibProxy implements MethodInterceptor {
    
        /**
         * 目标对象
         */
        private Object targetObj;
    
        /**
         * 获取代理对象
         *
         * @param targetObj
         * @return
         */
        public Object newProxyInstance(Object targetObj) {
            this.targetObj = targetObj;
    
            Enhancer enhancer = new Enhancer();
            // 设置代理类的父类
            enhancer.setSuperclass(this.targetObj.getClass());
            // 设置回调函数
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            Object result = null;
            try {
                System.out.println("CGLib代理代码begin");
                result = methodProxy.invokeSuper(o, args);
                System.out.println("CGLib代理代码end");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            PayService payService = (PayService) new CglibProxy().newProxyInstance(new AliPayService());
            payService.pay();
        }
    }

5.总结

  • 动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理,解耦和易维护

  • 两种动态代理的区别

    • JDK动态代理:要求目标对象实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以用CGLib动态代理
    • CGLib动态代理它是在内存中构建一个子类对象,从而实现对目标对象功能的扩展
    • JDK动态代理是自带的,CGLib需要引入第三方包
    • CGLib动态代理基于继承来实现代理,所以无法对final类、private方法和static方法实现代理
  • spring AOP中的代理使用的默认策略

    • 如果目标对象实现了接口,则默认采用JDK动态代理
    • 如果目标对象没有实现接口,则采用CGLib动态代理
    • 如果目标对象实现了接口,程序里面依旧可以指定使用CGLib动态代理
相关推荐
刃神太酷啦3 分钟前
Linux 基础 IO 收官:库的构建与使用、进程地址空间及核心知识点全解----《Hello Linux!》(11)
java·linux·c语言·数据库·c++·算法·php
猿小羽7 分钟前
Spring AI + MCP 实战:构建标准化、可扩展的 AI Agent 架构体系
java·spring boot·llm·架构设计·ai agent·spring ai·mcp
独自破碎E9 分钟前
Spring Boot的多环境配置
java·spring boot·后端
拽着尾巴的鱼儿11 分钟前
Idea-Spring-boot 项目启动无法识别lombok
java·intellij-idea
2301_7806698612 分钟前
单元测试、反射、注解、动态代理
java·单元测试
开开心心就好13 分钟前
视频伪装软件,.vsec格式批量伪装播放专用
java·linux·开发语言·网络·python·电脑·php
笨手笨脚の17 分钟前
深入理解 Java 虚拟机-02 对象
java·jvm·压缩指针·对象分配
Coder_Boy_17 分钟前
基于SpringAI的在线考试系统-数据库设计核心业务方案(微调)
java·数据库·人工智能·spring boot·领域驱动
yangminlei21 分钟前
Spring Boot 3 + Spring AI 实战:十分钟集成 OpenAI API 构建智能应用
java·openvino
是三好22 分钟前
java集合
java·开发语言