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动态代理
相关推荐
点点滴滴的记录2 分钟前
Java的CompletableFuture实现原理
java·开发语言·javascript
xiaolingting3 分钟前
Java 引用是4个字节还是8个字节?
java·jvm·引用·指针压缩
一只傻小白,8 分钟前
JAVA项目中freemarker静态模板技术
java·开发语言
袁庭新8 分钟前
Spring Boot项目接收前端参数的11种方式
java·springboot·袁庭新·如何接收前端数据·boot接收数据
机跃10 分钟前
递归算法常见问题(Java)
java·开发语言·算法
程序员-小李30 分钟前
餐厅下单助手系统(Java+MySQL)
java·开发语言·mysql
开心工作室_kaic34 分钟前
springboot496基于java手机销售网站设计和实现(论文+源码)_kaic
java·开发语言·智能手机
像少年啦飞驰点、35 分钟前
SpringBoot + HttpSession 自定义生成sessionId
java·开发语言
珊珊来吃40 分钟前
EXCEL中给某一列数据加上双引号
java·前端·excel
我曾经是个程序员1 小时前
使用C#生成一张1G大小的空白图片
java·算法·c#