• SpringFramework:Spring AOP


    SpringFramework:Spring AOP



    一、Spring AOP 概述

    1. AOP 简介

    AOP(Aspect oriented programming),即面向切面编程,它是一个编程范式,是 OOP(面向对象编程)的一种延续,目的就是提高代码的模块性。

    Spring AOP 基于动态代理的方式实现,如果是实现了接口的话就会使用 JDK 动态代理,反之则使用 CGLIB 代理,Spring中 AOP 的应用主要体现在 事务、日志、异常处理等方面,通过在代码的前后做一些增强处理,可以实现对业务逻辑的隔离,提高代码的模块化能力,同时也是解耦。Spring主要提供了 Aspect 切面、JoinPoint 连接点、PointCut 切入点、Advice 增强等实现方式

    2. AOP 为什么叫面向切面编程

    • 切 : 指的是横切逻辑,原有业务逻辑代码不动,只能操作横切逻辑代码,所以面向横切逻辑
    • 面 : 横切逻辑代码往往要影响的是很多个方法,每个方法如同一个点,多个点构成一个面。这里有一个面的概念

    3. AOP 主要用来解决的问题

    例如:现有三个类,Horse、Pig、Dog,这三个类中都有 eat 和 run 两个方法。

    通过 OOP 思想中的继承,我们可以提取出一个 Animal 的父类,然后将 eat 和 run 方法放入父类中,Horse、Pig、Dog通过继承Animal类即可自动获得 eat() 和 run() 方法。这样将会少些很多重复的代码。

    OOP 编程思想可以解决大部分的代码重复问题。但是有一些问题是处理不了的。比如在父类 Animal 中的多个方法的相同位置出现了重复的代码,OOP 就解决不了。这部分重复的代码,一般统称为横切逻辑代码

    横切逻辑代码存在的问题:

    • 代码重复问题
    • 横切逻辑代码和业务代码混杂在一起,代码臃肿,不变维护

    AOP 就是用来解决这些问题的:AOP 另辟蹊径,提出横向抽取机制,将横切逻辑代码和业务逻辑代码分离,代码拆分比较容易,难的是如何在不改变原有业务逻辑的情况下,悄无声息的将横向逻辑代码应用到原有的业务逻辑中,达到和原来一样的效果

    AOP 主要用来解决:在不改变原有业务逻辑的情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复。

    4. AOP 的相关术语

    1. 连接点(Joinpoint): 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
    2. 切入点(Pointcut): 切入点是指我们要对哪些连接点(Joinpoint)进行拦截
    3. 通知/增强(Advice): 所谓通知是指拦截到Joinpoint之后要做的事情,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)。
    4. 织入(Weaving): 是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
    5. 切面(Aspect): 是切入点和通知(引介)的结合
    6. 代理(Proxy): 一个类被AOP织入增强后,就产生了一个结果代理类
    7. 目标对象(Target): 代理的目标对象

    二、Spring AOP Demo

    1. xml配置方式

    引入依赖

    
    <dependency>
    	<groupId>org.springframeworkgroupId>
    	<artifactId>spring-contextartifactId>
    	<version>5.3.22version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    配置 xml 信息

    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
     
        <bean id="aopTank" class="designpattern.aop.v1.AopTank"/>
        <bean id="aopMethod" class="designpattern.aop.v1.AopMethod"/>
     
        <aop:config>
            <aop:aspect id="time" ref="aopMethod">
                <aop:pointcut id="onmove" expression="execution(public void com.xxx.demo.api.aop.demo.AopDog.*(..))"/>
                <aop:before method="before" pointcut-ref="onmove"/>
                <aop:after method="after" pointcut-ref="onmove"/>
            aop:aspect>
        aop:config>
     
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    切入时添加方法

    public class AopMethod {
        public void before() {
            System.out.println("before...");
        }
     
        public void after() {
            System.out.println("after...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    被切入的类

    public class AopDog {
        public void eat() {
            System.out.println("The dog is eating...");
        }
        public void drink() {
            System.out.println("The dog is drinking water...");
        }
     
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    测试

    public class Main {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("app.xml");
            AopDog dog = (AopDog) context.getBean("AopDog");
            tank.move();
            tank.voice();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2. 注解配置方式

    引入依赖

            <dependency>
                <groupId>org.aspectjgroupId>
                <artifactId>aspectjweaverartifactId>
                <version>1.9.4version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    xml配置同上

    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
            
        <aop:aspectj-autoproxy/>
     
        <bean id="aopTank" class="designpattern.aop.v1.AopTank"/>
        <bean id="aopMethod" class="designpattern.aop.v1.AopMethod"/>
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    切入时添加方法

    public class AopMethod {
      	@Before("execution(public void com.xxx.demo.api.aop.demo.AopDog.*(..))")
        public void before() {
            System.out.println("before...");
        }
     
        @After("execution(public void com.xxx.demo.api.aop.demo.AopDog.*(..))")
        public void after() {
            System.out.println("after...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    被切入的类 同xml方式
    测试 同xml方式


    三、相关知识点

    1. JDK 动态代理和 CGLIB 代理

    JDK 动态代理主要是针对类实现了某个接口,AOP 则会使用 JDK 动态代理。他基于反射的机制实现,生成一个实现同样接口的一个代理类,然后通过重写方法的方式,实现对代码的增强;

    而如果某个类没有实现接口,AOP 则会使用 CGLIB 代理。他的底层原理是基于 asm 第三方框架,通过修改字节码生成成成一个子类,然后重写父类的方法,实现对代码的增强。

    2. Spring AOP 和 AspectJ AOP

    Spring AOP 基于动态代理实现,属于运行时增强。

    AspectJ 则属于编译时增强,主要有3种方式:

    • 编译时织入: 指的是增强的代码和源代码我们都有,直接使用 AspectJ 编译器编译就行了,编译之后生成一个新的类,他也会作为一个正常的Java 类装载到 JVM;
    • 编译后织入: 指的是代码已经被编译成 class 文件或者已经打成 jar包,这时候要增强的话,就是编译后织入,比如你依赖了第三方的类库,又想对他增强的话,就可以通过这种方式;
    • 加载时织入: 指的是在 JVM 加载类的时候进行织入。

    总结下来的话,就是 Spring AOP 只能在运行时织入,不需要单独编译,性能相比 AspectJ 编译织入的方式慢,而 AspectJ 只支持编译前后和类加载时织入,性能更好,功能更加强大

    3. @Aspect、@Pointcut、@Around 注解

    • @Pointcut表示一个切入点,value表示切入点的作用范围
    • @Aspect 表示申明一个切面
      • @Around,环绕增强:方法正常执行的前后调用
      • @Before,前置增强:方法执行前调用
      • @After,后置 final增强:不管方法正常退出还是异常都会执行
      • @AfterReturning,后置增强:方法正常退出时执行
      • @AfterThowing,异常抛出增强:方法抛异常时执行

  • 相关阅读:
    NB的Github项目,看到最后一个我惊呆了!
    golang gin 监听rabbitmq队列无限消费
    PHP数组转json和php的json转数组
    区块链系统Docker&Kuberntes一键部署
    编程笔记 html5&css&js 088 JavaScript 数组
    dubbo和webservice 区别
    H12-821_146
    一文详解|增长那些事儿
    springboot单独在指定地方输出sql
    齐次坐标与投影几何
  • 原文地址:https://blog.csdn.net/weixin_47410172/article/details/127861527