zoukankan      html  css  js  c++  java
  • Spring AOP实现原理

    代理模式

    我们知道,Spring AOP的主要作用就是不通过修改源代码的方式、将非核心功能代码织入来实现对方法的增强。那么Spring AOP的底层如何实现对方法的增强?实现的关键在于使用了代理模式
    代理模式的作用就是为其它对象提供一种代理,以控制对这个对象的访问,用于解决在直接访问对象时带来的各种问题。代理主要分为两种方式:静态代理动态代理

    静态代理

    静态代理主要通过将目标类与代理类实现同一个接口,让代理类持有真实类对象,然后在代理类方法中调用真实类方法,在调用真实类方法的前后添加我们所需要的功能扩展代码来达到增强的目的
    示例代码:

    /**
     * 代理类与目标类的共同接口
     */
    public interface Subject {
        void request();
        void response();
    }
    /**
     * 目标类
     */
    public class RealSubject implements Subject {
    
        @Override
        public void request() {
            System.out.println("执行目标对象的request方法......");
        }
    
        @Override
        public void response() {
            System.out.println("执行目标对象的response方法......");
        }
    }
    /**
     * 代理类
     */
    public class ProxySubject implements Subject {
    
        private Subject subject;
    
        public ProxySubject(Subject subject) {
            this.subject = subject;
        }
    
        @Override
        public void request() {
            System.out.println("before 前置增强");
            subject.request();
            System.out.println("after 后置增强");
        }
    
        @Override
        public void response() {
            System.out.println("before 前置增强");
            subject.response();
            System.out.println("after 后置增强");
        }
    }
    
    public class Main {
    
        public static void main(String[] args) {
            //目标对象
            Subject realSubject = new RealSubject();
            //代理对象 通过构造器注入目标对象
            Subject proxySubject = new ProxySubject(realSubject);
    
            proxySubject.request();
            proxySubject.response();
        }
    }
    

    运行结果:

    before 前置增强
    执行目标对象的request方法......
    after 后置增强
    before 前置增强
    执行目标对象的response方法......
    after 后置增强
    

    通过以上的代码示例,我们不难发现静态代理的缺点。假如我们的Subject接口要增加其它的方法,则ProxySubject代理类也必须同时代理这些新增的方法。同时我们也看到,request方法和response方法所织入的代码是一样的,这会使得代理类中出现大量冗余的代码,非常不利于扩展和维护。为了解决静态代理的这些缺陷,于是有了动态代理

    动态代理

    与静态代理相比,动态代理的代理类不需要程序员自己手动定义,而是在程序运行时动态生成
    动态代理可以分为JDK动态代理CgLib动态代理

    1.JDK动态代理

    JDK动态代理与静态代理一样,目标类需要实现一个代理接口,它的开发步骤如下:
    1.定义一个java.lang.reflect.InvocationHandler接口的实现类,重写invoke方法
    2.将InvocationHandler对象作为参数传入java.lang.reflect.Proxy的newProxyInstance方法中
    3.通过调用java.lang.reflect.Proxy的newProxyInstance方法获得动态代理对象
    4.通过代理对象调用目标方法
    示例代码:

    /**
     * 自定义InvocationHandler的实现类
     */
    public class JdkProxySubject implements InvocationHandler {
    
        private Subject subject;
    
        public JdkProxySubject(Subject subject) {
            this.subject = subject;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            System.out.println("before 前置通知");
            Object result = null;
    
            try {
                result = method.invoke(subject, args);
            }catch (Exception ex) {
                System.out.println("ex: " + ex.getMessage());
                throw ex;
            }finally {
                System.out.println("after 后置通知");
            }
            return result;
        }
    }
    
    public class Main {
    
        public static void main(String[] args) {
    
            //获取InvocationHandler对象 在构造方法中注入目标对象
            InvocationHandler handler = new JdkProxySubject(new RealSubject());
            //获取代理类对象
            Subject proxySubject = (Subject)Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{Subject.class}, handler);
            //调用目标方法
            proxySubject.request();
            proxySubject.response();
        }
    }
    

    运行结果:

    before 前置通知
    执行目标对象的request方法......
    after 后置通知
    before 前置通知
    执行目标对象的response方法......
    after 后置通知
    
    2.CgLib动态代理

    CgLib动态代理的原理是对指定的业务类生成一个子类,并覆盖其中的业务方法来实现代理。它的开发步骤:
    1.定义一个org.springframework.cglib.proxy.MethodInterceptor接口的实现类,重写intercept方法
    2.获取org.springframework.cglib.proxy.Enhancer类的对象
    3.分别调用Enhancer对象的setSuperclass和setCallback方法,使用create方法获取代理对象
    4.通过代理对象调用目标方法
    示例代码:

    /**
     * 自定义MethodInterceptor实现类
     */
    public class MyMethodInterceptor implements MethodInterceptor {
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    
            System.out.println("before 前置通知");
            Object result = null;
    
            try {
                result = methodProxy.invokeSuper(obj, args);
            }catch (Exception ex) {
                System.out.println("ex: " + ex.getMessage());
                throw ex;
            }finally {
                System.out.println("after 后置通知");
            }
            return result;
        }
    }
    
    public class Main {
    
        public static void main(String[] args) {
            //获取Enhancer 对象
            Enhancer enhancer = new Enhancer();
            //设置代理类的父类(目标类)
            enhancer.setSuperclass(RealSubject.class);
            //设置回调方法
            enhancer.setCallback(new MyMethodInterceptor());
            //获取代理对象
            Subject proxySubject = (Subject)enhancer.create();
    
            //调用目标方法
            proxySubject.request();
            proxySubject.response();
        }
    }
    

    运行结果:

    before 前置通知
    执行目标对象的request方法......
    after 后置通知
    before 前置通知
    执行目标对象的response方法......
    after 后置通知
    
    3.两种代理的区别

    JDK动态代理和CgLib动态代理的主要区别:
    JDK动态代理只能针对实现了接口的类的接口方法进行代理
    CgLib动态代理基于继承来实现代理,所以无法对final类、private方法和static方法实现代理

    Spring AOP的代理

    Spring AOP中的代理使用的默认策略是:
    如果目标对象实现了接口,则默认采用JDK动态代理
    如果目标对象没有实现接口,则采用CgLib进行动态代理
    如果目标对象实现了接口,且强制CgLib代理,则采用CgLib进行动态代理

  • 相关阅读:
    SCAU 9504 面试
    SCAU 9503 懒人选座位
    SCAU 8628 相亲
    SCAU 10691 ACM 光环
    SCAU 8626 原子量计数
    SCAU 10674 等差对
    HDU ACM 1048 The Hardest Problem Ever (水题)
    SCAU 9502 ARDF
    SCAU 10686 DeathGod不知道的事情
    SCAU 8629 热身游戏(高精度)
  • 原文地址:https://www.cnblogs.com/liantdev/p/10132680.html
Copyright © 2011-2022 走看看