• Spring Aop原理解析和使用示例


    Spring Aop原理解析和使用示例

    前言

    众所周知,Spring拥有两大特性:IoC和AOP。IoC,英文全称Inversion of Control,意为控制反转。AOP,英文全称Aspect-Oriented Programming,意为面向切面编程。

    今天我们的主题就是Spring的Aop的源码解析。

    Spring Aop相关概念

    Spring Aop基本原理

    Spring Aop的实现的原理就是JDK的动态代理。核心思想就是利用动态代理模式,再方法执行前后或者出现异常时加入相关逻辑。

    JDK动态代理入门

    利用java的反射机制,使用Proxy.newProxyInstance创建代理类对象,从而代理目标对象的调用。

    **目的:**在完成目标对象调用时,进行功能增强。

    **大致实现:**使用反射包 java.lang.reflect , 里面有三个类 : InvocationHandler , Proxy。

    Proxy.newProxyInstance:静态方法,用于创建代理对象。参数列表如下:

    • ClassLoader loader 类加载器,负责向内存中加载对象的。 使用反射获取对象的ClassLoader类a, a.getCalss().getClassLoader(), 目标对象的类加载器。
    • Class<?>[] interfaces: 接口, 目标对象实现的接口,也是反射获取的。
    • InvocationHandler h : 我们自己写的,代理类要完成的功能。

    InvocationHandler:接口类,实现目标对象的代理调用。接口方法如下:

    • public Object invoke(Object proxy, Method method, Object[] args){}
      • Object proxy: jdk创建的代理对象,无需赋值。
      • Method method: 目标类中的方法,jdk提供method对象的。
      • Object[] args: 目标类中方法的参数, jdk提供的。

    动态代理举例

    ClassLoader loader 类加载器,负责向内存中加载对象的。 使用反射获取对象的ClassLoader类a, a.getCalss().getClassLoader(), 目标对象的类加载器
    Class<?>[] interfaces: 接口, 目标对象实现的接口,也是反射获取的。
    InvocationHandler h : 我们自己写的,代理类要完成的功能。
    
    • 1
    • 2
    • 3

    InvocationHandler:接口类,实现目标对象的代理调用

    动态代理举例

    /**
     * 接口类
     */
    public interface IHelloWorld {
        void publicSayHello();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    /**
     * 实现类
     */
    public class HelloWorld implements IHelloWorld {
        private void privateSayHello() {
            System.out.println("private:hello world");
        }
    
        /**
         * 接口实现方法
         */
        public void publicSayHello() {
            System.out.println("public:hello world");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    /**
     * 自定义动态代理
     */
    public class MyInvocationHandler implements InvocationHandler {
        private Object target;
    
        public MyInvocationHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("start.");
            Object obj = method.invoke(target, args);
            System.out.println("end.");
            return obj;
        }
    
        /**
         * 获取动态代理对象
         * @return 代理对象
         */
        public Object getProxy() {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    /**
     * 测试类
     */
    public class ProxyTest {
        public static void main(String[] args) {
            HelloWorld helloWorld = new HelloWorld();
            MyInvocationHandler myInvocationHandler = new MyInvocationHandler(helloWorld);
            Object object = myInvocationHandler.getProxy();
            IHelloWorld object1 = (IHelloWorld)object;
            object1.publicSayHello();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    输出如下:

    start.
    public:hello world
    end.

    Spring Aop代码实现原理

    Spring Aop的代码实现逻辑是嵌入在Spring Bean的生成过程中的。

    1. Spring Bean的生成过程是什么样的?

      Spring Bean的生成过程大致可以总结为"实例化" ==> “依赖注入” ==> “回调(Aware回调)” ==> “初始化” ==> BeanPostProcessor

    2. Spring Aop的能力是利用Spring Bean生成过程中的BeanPostProcessor,对生成的实例进行动态代理的后置处理,使得Spring Bean生成后,拿到的是经过Spring动态代理之后的代理对象

    代码示例:

    // bean生命周期
    // 实例化 -> 依赖注入 -> 回调(Aware回调) -> 初始化 -> AOP
    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getType();
        try {
            Object instance = clazz.getConstructor().newInstance();
            // 依赖注入
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field f : declaredFields) {
                if (f.isAnnotationPresent(Autowired.class)) {
                    f.setAccessible(true);
                    Object value = getBean(f.getName());
                    f.set(instance, value);
                }
            }
    
            // 回调
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware) instance).setBeanName(beanName);
            }
    
            // AOP BeanPostProcessor机制
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorArrayList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(beanName, instance);
            }
    
            // 初始化
            if (instance instanceof InitializingBean) {
                ((InitializingBean) instance).afterPropertiesSet();
            }
    
            // AOP BeanPostProcessor机制
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorArrayList) {
                instance = beanPostProcessor.postProcessAfterInitialization(beanName, instance);
            }
    
            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    Spring BeanProcess接口:

    public interface BeanPostProcessor {
        Object postProcessBeforeInitialization(String beanName, Object bean);
        Object postProcessAfterInitialization(String beanName, Object bean);
    }
    
    • 1
    • 2
    • 3
    • 4

    Spring Aop使用示例

    基于配置文件的使用示例

     <bean id="agentAdvisorXML" class="com.fufu.spring.aop.AgentAdvisorXML"/>
    <!--Spring基于Xml的切面-->
         <aop:config>
             <!-- 定义切点函数 -->
             <aop:pointcut id="singPointCut" expression="execution(* com.test.proxy.User.sing(..))"/>
             <!-- 定义切面 order 定义优先级,值越小优先级越大-->
             <aop:aspect ref="agentAdvisorXML" order="0">
                 <!--前置通知-->
                 <aop:before method="getMoney" pointcut-ref="singPointCut"/>
                 <!--后置通知-->
                 <aop:after method="writeReceipt" pointcut-ref="singPointCut"/>
             </aop:aspect>
         </aop:config>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    基于注解的使用示例

    1. 开启@AspectJ注解支持

      <aop:aspectj-autoproxy/>
      
      • 1
    2. 在类的相应位置加上注解

      	 /**
      	 * Created by zhoujunfu on 2018/9/7.
      	 * 基于注解的Spring AOP
      	 */
      	@Aspect
      	@Component
      	public class AgentAdvisor {
      	 
      	    @Before(value = "execution(* com.fufu.proxy.ShowService.sing(..))")
      	    public void getMoney() {
      	        System.out.println("get money");
      	    }
      	 
      	    @After(value = "execution(* com.fufu.proxy.ShowService.sing(..))")
      	    public void writeReceipt() {
      	        System.out.println("write receipt");
      	    }
      	}
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

      手撸Spring源码,精简易懂:https://download.csdn.net/download/jjs15259655776/85816604

  • 相关阅读:
    代码随想录67——额外题目【动态规划】:5最长回文子串、132分割回文串II、673最长递增子序列的个数
    ctfshow 命令执行(40-50)
    用C++元编程实现任意函数签名的回调
    14.9 Socket 高效文件传输
    基于北方苍鹰优化算法的函数寻优算法
    笔试强训2
    浅浅复习Java基础和部分高级篇
    C++知识点总结(6):高精度乘法真题代码
    《Java笔记——基础语法》
    基于Pix4D使用无人机光学影像制作正射影像(DOM)和数字表面模型(DSM) 操作步骤
  • 原文地址:https://blog.csdn.net/jjs15259655776/article/details/125512066