首页 > 其他分享 >Spring-AOP概述

Spring-AOP概述

时间:2024-06-17 17:30:23浏览次数:8  
标签:Spring AOP 方法 概述 carl 通知 com public

1. AOP的基本概念

AOP:面向切面编程(Aspect Oriented Programming),通过预编译方式运行期动态代理实现程序功能的统一维护的一种技术

AOP的作用:

利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率

没有AOP技术的时候如何实现方法增强,常用的三种方式:

  1. 在方法内部添加功能

  2. 添加一个子类,通过子类扩展增加父类的方法

  3. 在新的方法中调用旧方法,在新方法中增强功能

public class UserServiceImpl implements IUserService {

    /**
     * find user list.
     *
     * @return user list
     */
    @Override
    public List<User> findUserList() {
        System.out.println("execute method: findUserList");
        return Collections.singletonList(new User("pdai", 18));
    }

    /**
     * add user
     */
    @Override
    public void addUser() {
        System.out.println("execute method: addUser");
        // do something
    }

}

AOP的操作

通过注解/配置文件的方式,不修改源代码,直接增强方法功能

在这里插入图片描述

面向对象编程(OOP):针对业务处理过程的实体,及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。

面向切面编程(AOP):AOP则是针对业务处理过程中的切面进行提取,用于处理某个步骤或阶段,以获得逻辑过程中各部分之间低耦合的隔离效果

在这里插入图片描述

1.1 AOP术语

连接点(joinpoint):表示需要在程序中插入横切关注点的扩展点(需要被增强的方法【在哪里干】),这些方法就称为连接点(连接点为切入准备点),连接点可以做类初始化、方法执行、方法调用、字段调用或处理异常等,Spring只支持方法作为连接点

切点(pointcut):选择一组相关连接点的模式,即连接点的集合,通俗理解为:哪些方法真正被增强了,真正增强的方法被称为切点(切点为实际切入点),Spring支持perl5正则表达式和AspectJ切入点模式

  • 切点表达式

    切入点表达式作用:知道对哪个类里面的哪个方法进行增强,有多种表达式,并且表达式之间可以组合使用

    • Execution表达式

      精确匹配指定包/指定类/指定方法/指定权限修饰符等
      语法结构:execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

      exection([权限修饰符][返回类型][类全路径].方法名称(形式参数) 异常)

      • modifiers-pattern:表示权限修饰符,*表示所有权限修饰符,public表示只匹配public修饰的方法

        //匹配UserDaoImpl类的add方法,该方法必须为public修饰
        execution(public * com.carl.dao.impl.UserDaoImpl.add(..))
        //匹配UserDaoImpl类的所有方法,且不限制权限修饰符
        execution(* com.carl.dao.impl.UserDaoImpl.*(..))
        //匹配所有public修饰的方法
        execution(public * *(..))
        
      • ret-type-pattern:表示返回值类型,*表示所有返回值类型,void表示返回值为void的,int表示返回值为int的依此推断

        //对com.carl.dao.impl.UserDaoImpl类里的add方法进行增强,不限制返回值
        exection(* com.carl.dao.impl.UserDaoImpl.add(..))
        
        //对com.carl.dao.impl.UserDaoImpl类里的所有返回值为int类型的方法进行增强
        exection(int com.carl.dao.impl.UserDaoImpl.*(..))
        
        //对com.carl.dao包里的所有类里返回值类型为void的方法进行增强
        exection(void com.carl.dao.*.*(..))
        
      • declaring-type-pattern:表示类路径,例如com.carl.dao.impl.UserDaoImpl表示匹配com.carl.dao.impl包下的UserDaoImpl类,如果要匹配com.carl.dao.impl包下的所有类,则使用com.carl.dao.impl.*

        //对com.carl.dao.impl.UserDaoImpl类里的add方法进行增强
        exection(* com.carl.dao.impl.UserDaoImpl.add(..))
        
        //对com.carl.dao.impl.UserDaoImpl类里的所有方法进行增强
        exection(* com.carl.dao.impl.UserDaoImpl.*(..))
        
        //对com.carl.dao包里的所有类里的方法进行增强
        exection(* com.carl.dao.*.*(..))
        
      • name-pattern:表示方法名,*表示所有方法名,紧跟在declaring-type-pattern后面(没有空格),例如com.carl.dao.impl.UserDaoImpl.*表示UserDaoImpl类下的所有方法

        // 任何一个名字以“set”开始的方法的执行:
        execution(* set*(..))
        //对com.carl.dao.impl包下所有类里的add方法进行增强
        exection(* com.carl.dao.impl.*.add(..))
        //对com.carl.dao.impl包下所有类里抛出Execption及子异常的add方法进行增强
        exection(* com.carl.dao.impl.*.add(..) throws Exception)
        
      • param-pattern:表示方法的形式参数,()表示没有参数,()表示任意类型和数量的参数,(…)同()

        //对com.carl.dao.impl.UserDaoImpl类里的add方法进行增强
        exection(* com.carl.dao.impl.UserDaoImpl.add(..))
        //对com.carl.dao.impl.UserDaoImpl类里的add方法进行增强,add方法必须有两个形式参数,第一个为int,第二个为String
        exection(* com.carl.dao.impl.UserDaoImpl.add(int,String))
        //对com.carl.dao.impl.UserDaoImpl类里的空参add方法进行增强
        exection(* com.carl.dao.impl.UserDaoImpl.add())
        
      • throws-pattern:表示异常信息,如Exception表示抛出Exception或者它的子类

        //对com.carl.dao.impl包下所有类里抛出Execption及子异常的add方法进行增强
        exection(* com.carl.dao.impl.*.add(..) throws Exception)
        
    • Within表达式

      模糊匹配某个包内的所有类和方法,语法结构:within(type-pattern)

      within(包路径匹配)

      //匹配controller包下的所有方法
      within(com.example.demo.controller.*)
      //匹配controller包或其子包下的所有方法
      within(com.example.demo.controller..*)
      
    • This表达式

      模糊匹配某个类或其子类的所有方法
      语法结构:this(type)

      this(类路径匹配)

      //匹配UserService及其子类中的所有方法
      this(com.example.demo.service.UserService)
      
    • Target表达式

      模糊匹配某个类或其子类的所有方法
      语法结构:target(type)

      target(类路径匹配)

      //匹配UserService及其子类中的所有方法
      target(com.example.demo.service.UserService)
      
    • Args表达式

      用于匹配当前执行的方法参数类型与指定类型相匹配的所有方法
      语法结构:args(type-pattern)

      args(String)

      //限制目标方法必须有两个形参
      args(param1,param2)
      
      //使用方式:
      // 下面的args(arg0,arg1)会限制目标方法必须有2个形参
      // 此处access方法指定arg0、arg1为String类型,则args(arg0,arg1)还要求目标方法的两个形参都是String类型
      @AfterReturning(returning="rvt" , pointcut="execution(* org.crazyit.app.service.impl.*.*(..)) && args(arg0,arg1)")
      public void access(Object rvt, String arg0 , String arg1){
        System.out.println("调用目标方法第1个参数为:" + arg0);
        System.out.println("调用目标方法第2个参数为:" + arg1);
        System.out.println("获取目标方法返回值:" + rvt);
        System.out.println("模拟记录日志功能...");
      }
      
    • @annotation表达式

      用于匹配被指定注解标注的方法
      语法结构:@annotation(type)

      @annotation(注解类型)

      //匹配所有被MyAnnotation注解标注的方法
      @annotation(com.example.demo.annotation.MyAnnotation)
      

    组合表达式的逻辑运算符

    &&:要求连接点同时匹配两个切入点表达式

    ||:要求连接点匹配任意个切入点表达式

    !:要求连接点不匹配指定的切入点表达式

通知(增强)(advice)在连接点上执行的逻辑部分,提供了在切入点所选择的连接点处扩展现有方法的手段。实际增强的逻辑部分称为通知或增强(通知为切入逻辑)

  • 通知的类型:

    • 前置通知(before)

      前置通知,即在目标方法调用之前执行,注意,无论方法是否遇到异常都会执行

    • 后置通知(after returning)

      在目标方法执行后执行,前提是目标方法没有遇到异常,如果有异常则不执行该通知

    • 环绕通知(around)

      可以控制目标方法的执行(ProceedingJoinPoint.proceed()),可以在目标方法前后实现增强

    • 异常通知(after throwing)

      在目标方法抛出异常时执行,可以获取异常信息

    • 最终通知(after)

      在目标方法执行后执行,无论是否有异常都会执行

切面(aspectJ):由切点和通知组成,即包括切点的逻辑定义和连接点的定义,在Spring中可以使用Schema和@AspectJ方式进行组织实现

引入(inter-type declaration):也称为内部类型声明,为已有的类添加额外新的字段或方法,Spring允许引入新的接口(必须对应一个实现类)到所有被代理的对象

目标对象(Target Object):需要被织入横切面的对象,即改对象是切入点选择的对象,需要被通知的对象,也称为被通知对象;由于SpringAOP采用代理模式实现,因此该对象就是被代理对象

织入(wearing):将增强添加到目标类的具体连接点的过程

AOP代理(AOP proxy):AOP框架使用代理模式创建的对象。从而实现在连接点处插入通知,就是通过代理来对目标对象应用切面。在Spring中,AOP代理可以用JDK动态代理或CGLIB代理实现,而通过拦截器模型应用到切面

Spring框架基于AspectJ实现AOP操作

AspectJ不是Spring的组成部分,是独立的AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作

2. Spring AOP和AspectJ的区别

AspectJ是Java实现的AOP框架,能够对Java代码进行AOP编译,让Java代码具有AspectJ的AOP功能(需要特殊的编译器)
AspectJ是目前实现AOP框架中最成熟,功能最丰富的语言,更幸运的是,AspectJ与Java程序完全兼容

Spring中的AOP注解几乎都是来源于AspectJ

在这里插入图片描述

Spring2.0开始,就已经开始采用AspectJ 5一样的注解,并使用AspectJ来做切入点解析和匹配,但是,AOP在运行时仍然是纯的Sping AOP,并不依赖于AspectJ的编译器或者织入器

上述提到的织入的概念:是aspect(切面)应用到目标类/方法的过程。这个过程一般分为动态织入和静态织入

  • 动态织入:在运行时动态将要增强的代码织入到目标类中,这样往往是通过动态代理技术实现的,例如:JDK的动态代理(底层反射)或者CGLIB的动态代理(底层通过继承实现),Spring AOP采用的就是基于运行时增强的代理技术

  • 静态织入:AspectJ采用的就是静态织入的方式,即在编译期织入,在这个期间,使用AspectJ的acj编译器(类似javac)把aspect类编译成class字节码后,在Java目标类编译时织入,即先编译aspect再编译目标类

    在这里插入图片描述

下表总结了 Spring AOP 和 AspectJ 之间的关键区别:

Spring AOPAspectJ
在纯 Java 中实现使用 Java 编程语言的扩展实现
不需要单独的编译过程除非设置 LTW,否则需要 AspectJ 编译器 (ajc)
只能使用运行时织入运行时织入不可用。支持编译时、编译后和加载时织入
功能不强-仅支持方法级编织更强大 - 可以编织字段、方法、构造函数、静态初始值设定项、最终类/方法等
只能在由 Spring 容器管理的 bean 上实现可以在所有域对象上实现
仅支持方法执行切入点支持所有切入点
代理是由目标对象创建的, 并且切面应用在这些代理上在执行应用程序之前 (在运行时) 前, 各方面直接在代码中进行织入
比 AspectJ 慢多了更好的性能
易于学习和应用相对于 Spring AOP 来说更复杂

3. Spring AOP的底层原理

Spring AOP的底层使用的动态代理

有两种动态代理的情况

  • 有接口的动态代理(JDK动态代理)

    接口使用JDK动态代理

    定义接口IJdkProxyService

    public interface IJdkProxyService {
    
        void doMethod1();
    
        String doMethod2();
    
        String doMethod3() throws Exception;
    }
    

    定义接口实现类JdkProxyDemoServiceImpl

    @Service
    public class JdkProxyDemoServiceImpl implements IJdkProxyService {
    
        @Override
        public void doMethod1() {
            System.out.println("JdkProxyServiceImpl.doMethod1()");
        }
    
        @Override
        public String doMethod2() {
            System.out.println("JdkProxyServiceImpl.doMethod2()");
            return "hello world";
        }
    
        @Override
        public String doMethod3() throws Exception {
            System.out.println("JdkProxyServiceImpl.doMethod3()");
            throw new Exception("some exception");
        }
    }
    

    定义切面LogAspect

    package tech.pdai.springframework.aspect;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    import org.springframework.stereotype.Component;
    
    @EnableAspectJAutoProxy
    @Component
    @Aspect
    public class LogAspect {
    
        /**
         * define point cut.
         */
        @Pointcut("execution(* tech.pdai.springframework.service.*.*(..))")
        private void pointCutMethod() {
        }
    
    
        /**
         * 环绕通知.
         *
         * @param pjp pjp
         * @return obj
         * @throws Throwable exception
         */
        @Around("pointCutMethod()")
        public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("-----------------------");
            System.out.println("环绕通知: 进入方法");
            Object o = pjp.proceed();
            System.out.println("环绕通知: 退出方法");
            return o;
        }
    
        /**
         * 前置通知.
         */
        @Before("pointCutMethod()")
        public void doBefore() {
            System.out.println("前置通知");
        }
    
    
        /**
         * 后置通知.
         *
         * @param result return val
         */
        @AfterReturning(pointcut = "pointCutMethod()", returning = "result")
        public void doAfterReturning(String result) {
            System.out.println("后置通知, 返回值: " + result);
        }
    
        /**
         * 异常通知.
         *
         * @param e exception
         */
        @AfterThrowing(pointcut = "pointCutMethod()", throwing = "e")
        public void doAfterThrowing(Exception e) {
            System.out.println("异常通知, 异常: " + e.getMessage());
        }
    
        /**
         * 最终通知.
         */
        @After("pointCutMethod()")
        public void doAfter() {
            System.out.println("最终通知");
        }
    
    }
    

    测试结果

    -----------------------
    环绕通知: 进入方法
    前置通知
    JdkProxyServiceImpl.doMethod1()
    最终通知
    环绕通知: 退出方法
    -----------------------
    环绕通知: 进入方法
    前置通知
    JdkProxyServiceImpl.doMethod2()
    后置通知, 返回值: hello world
    最终通知
    环绕通知: 退出方法
    -----------------------
    环绕通知: 进入方法
    前置通知
    JdkProxyServiceImpl.doMethod3()
    异常通知, 异常: some exception
    最终通知
    
  • 无接口的动态代理(GCLib动态代理)

    非接口使用Cglib代理

    类定义CglibProxyDemoServiceImpl

    @Service
    public class CglibProxyDemoServiceImpl {
    
        public void doMethod1() {
            System.out.println("CglibProxyDemoServiceImpl.doMethod1()");
        }
    
        public String doMethod2() {
            System.out.println("CglibProxyDemoServiceImpl.doMethod2()");
            return "hello world";
        }
    
        public String doMethod3() throws Exception {
            System.out.println("CglibProxyDemoServiceImpl.doMethod3()");
            throw new Exception("some exception");
        }
    }
    

    切面定义和定义切面LogAspect相同
    测试结果

    -----------------------
    环绕通知: 进入方法
    前置通知
    CglibProxyDemoServiceImpl.doMethod1()
    最终通知
    环绕通知: 退出方法
    -----------------------
    环绕通知: 进入方法
    前置通知
    CglibProxyDemoServiceImpl.doMethod2()
    后置通知, 返回值: hello world
    最终通知
    环绕通知: 退出方法
    -----------------------
    环绕通知: 进入方法
    前置通知
    CglibProxyDemoServiceImpl.doMethod3()
    异常通知, 异常: some exception
    最终通知
    

AOP切面的实现【底层源码】

AOP代理的创建【底层源码】

Cglib代理实现

JDK代理实现

4. AOP注解详解

注解名称解释
@Aspect用于定义一个切面
@pointcut用于定义切入点表达式,在使用时还需要定义一个包含名字和任意参数的方法签名来表示切入点名称,这个方法签名就是一个返回值为void,且方法体为空的普通方法
@Before用于定义前置通知,在使用时,通常需要指定一个value属性值,该属性值用于指定一个切入点表达式,也可以是已有的切入点
@AfterReturning用于定义后置通知,在使用时可以指定pointcut/value和returning属性,其中pointcut/value两个属性的作用一样,都用于指定切入点表达式
@Around用于定义环绕通知,在使用时需要指定一个value属性,该属性用于指定该通知被织入的切入点
@After-Throwing用于定义异常通知来处理程序中未处理的异常,在使用时可指定pointcut/value和throwing属性,其中throwing属性值用于指定一个形参名(形参名用于访问目标方法抛出的异常)
@After用于定义最终final通知,不管是否异常,该通知都会执行,使用时需要指定value属性
@DeclareParents用于定义引介通知(几乎不使用)

5. 基于AspectJ实现AOP切面

在项目工程中引入AOP相关依赖

在上述包依赖的基础上增加spring-aspects-5.3.9.jar,以及一个外部依赖包

aspectjweaver-1.9.9.1.jar
在这里插入图片描述

以下实现前提:

package com.carl.entity;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

/**
 * @Version 1.0.0
 *
 * @author carl蔡先生
 * @Date 2022/10/04
 * @Description 使用者
 */
@Component
public class User{
    /**
     * @see String
     * 姓名
     */
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public Integer add(int a, int b){
        int i = a / b;
        System.out.println("User Add Result:"+a / b);
        return i;
    }
}

基于xml配置实现

  1. 定义切面类UserProxy

    package com.carl.aop;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    
    /**
     * @Version 1.0.0
     * @author carl蔡先生
     * @Date 2022/10/04
     * @Description 用户代理
     */
    @Component(value = "diy")
    public class UserProxy {
        /**
         * 前置通知
         */
        public void before(JoinPoint joinPoint){
            Object[] args = joinPoint.getArgs();
            Signature signature = joinPoint.getSignature();
            System.out.println("[before] [前置通知:"+ signature.getName()+"],参数为:"+ Arrays.asList(args));
        }
    
    
    
        /**
         * 异常通知
         */
        public void afterThrowing(){
            System.out.println("afterThrowing");
        }
    
        /**
         * 后置通知
         */
        public void afterReturning(JoinPoint joinPoint){
            System.out.println("afterReturning:后置通知:");
        }
        /**
         * 最终通知
         */
        public void after(JoinPoint joinPoint){
            System.out.println("[after] [最终通知:"+ joinPoint.getSignature().getName()+"]");
        }
    
        /**
         * 环绕通知
         */
        public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("around1:前环绕通知");
            proceedingJoinPoint.proceed();
            System.out.println("around2:后环绕通知");
        }
    }
    
  2. 配置xml的aop代理
    添加aop的命名空间—红色部分

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:p="http://www.springframework.org/schema/p"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    

    添加aop-config配置

    <!--注解方式就使用自动装配咯,如果不使用注解,使用bean标签也是可以的啦-->
    <context:component-scan base-package="com.carl"/>
    <!--
      注意事项:
        1.aop:aspect标签的ref属性对应@Component注解中的value值,即bean实例中的id属性值
        2.aop:pointcut标签表示切点,使用表达式定义具体的增强方法
        3.aop:after-returning~aop:after-throwing这五个标签,可以通过调整顺序,改变执行顺序
        4.pointcut-ref属性,如果定义了aop:pointcut标签,就需要使用该属性关联,否则aop:pointcut不生效
          也可以使用pointcut属性,单独定义切点表达式:<aop:after method="after" pointcut="execution(Integer com.carl.entity.User.add(..))"/>
        5.一个aop:config标签可以定义多个切面aop:aspect
    -->
    <aop:config>
        <aop:aspect ref="diy">
            <aop:pointcut id="point" expression="execution(Integer com.carl.entity.User.add(..))"/>
            <aop:after-returning method="afterReturning" pointcut-ref="point"/>
            <aop:around method="around" pointcut-ref="point"/>
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
    
  3. 测试

    @Test
    public void testAop(){
        ApplicationContext context = new FileSystemXmlApplicationContext("/resource/bean1.xml");
        User user = context.getBean("user", User.class);
        user.add(1, 2);
    }
    

基于注解方式实现

  1. 定义切面类
    UserProxy

    package com.carl.aop;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    
    /**
     * @Version 1.0.0
     * @author carl蔡先生
     * @Date 2022/10/04
     * @Description 用户代理
     */
    @Component(value = "diy")
    @Aspect//1.添加注解@Aspect,标注UserProxy为切面类
    public class UserProxy {
    
        /**
         * 2.定义切点方法和表达式
         */
        @Pointcut("execution(Integer com.carl.entity.User.add(..))")
        public void pointcut(){}
        
        /**
         * 3.实现通知方法,使用注解标注通知的类型,设置value值为切点方法相当于使用切点方法上的表达式
         * 前置通知
         * 等价于 @Before("execution(Integer com.carl.entity.User.add(..))")
         * 等价于 @Before(value = "execution(Integer com.carl.entity.User.add(..))")
         */
        @Before(value = "pointcut()")
        public void before(JoinPoint joinPoint){
            Object[] args = joinPoint.getArgs();
            Signature signature = joinPoint.getSignature();
            System.out.println("[before] [前置通知:"+ signature.getName()+"],参数为:"+ Arrays.asList(args));
        }
    
        /**
         * 异常通知
         */
        @AfterThrowing(value = "pointcut()")
        public void afterThrowing(){
            System.out.println("afterThrowing");
        }
    
        /**
         * 后置通知
         */
        @AfterReturning("pointcut()")
        public void afterReturning(JoinPoint joinPoint){
            System.out.println("afterReturning:后置通知:");
        }
        /**
         * 最终通知
         */
        @After("pointcut()")
        public void after(JoinPoint joinPoint){
            System.out.println("[after] [最终通知:"+ joinPoint.getSignature().getName()+"]");
        }
    
        /**
         * 环绕通知
         */
        @Around("pointcut()")
        public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("around1:前环绕通知");
            proceedingJoinPoint.proceed();
            System.out.println("around2:后环绕通知");
        }
    }
    
  2. 配置xml文件
    添加aop的命名空间—红色部分

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:p="http://www.springframework.org/schema/p"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    

    添加aspectJ自动代理对象

    <aop:aspectj-autoproxy/>
    

6. AOP通知的执行顺序(重点)

Spring AOP遵循AspectJ的优先规则来确定通知的执行顺序。

在连接点开始时执行的通知,最高优先级的通知会先执行,即给定的两个前置通知中,优先级高的那个会先执行

在连接点结束时执行的通知,最高优先级的通知会后执行,即给定的两个后置通知中,优先级高的那个会最后执行

当定义在相同切面的多个通知,需要在同一个相同连接点中运行,执行的顺序是未知的(因为没有方法通过反射javac编译的类来获取声明的顺序)。不过可以考虑在每个切面类中按连接点压缩这些通知方法到一个通知方法,或者重构通知的片段到各自的切面类中–因为可以在切面类上做排序操作

当定义在不同切面的多个通知,需要在同一个相同连接点中运行,执行的顺序是未知的,除非你实现org.springframework.core.Ordered 接口或者使用@Order注解(@Order(数值类型))进行优先级排序,多个切面通过Orderd.getValue方法返回值(或@Order注解值)较低的那个会拥有更高的优先级

@Order(1)

标签:Spring,AOP,方法,概述,carl,通知,com,public
From: https://blog.csdn.net/qq_39052339/article/details/139748703

相关文章

  • AOP切面的实现原理【底层源码】
    AOP是基于IOC的Bean加载来实现的,将切面类的所有切面方法根据使用的注解生成对应的Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor,为后续交给代理增强实现做准备这里我们可以很明确的知道,AOP也是在Bean容器中被Spring管理的,根据初始化过程打断点定位......
  • AOP代理的创建【底层源码】
    代理的创建(源码)创建代理的方法是postProcessAfterInitialization:如果Bean被子类标识为代理,则使用配置的拦截器创建一个代理源码参考:AOP切面底层原理【底层源码】-postProcessAfterInitialization源码部分wrapIfNecessary方法主要用于判断是否需要创建代理,如果bean能......
  • 基于springboot的南门桥社区疫情防疫系统-48138(免费领源码+数据库)可做计算机毕业设计J
    Springboot南门桥社区疫情防疫系统的设计与实现摘 要信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克的课题。针对南门桥社区疫情防疫系统等问题,对南门桥社区......
  • Docker+Jenkins+Pipline实现SpringBoot项目input选择不同差异性yml文件打包、执行sh打
    场景Docker+Jenkins+Pipline如何获取git插件环境变量(提交sha、分支等)以及Jenkinsfile中获取sh执行结果(获取git最近提交信息):https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/139697004在上面的基础上实现了使用Jenkinsfile文件获取git的提交记录以及获取sh的执......
  • 小宋的SpringCloud学习记录day04:DB静态工具
    1.查询用户的同时,查询出用户对应的所有地址在UserVo实体类里面添加一个集合用于接收Address地址@ApiModelProperty("用户收货地址")privateList<AddressVO>addresses; 接下来我们对业务层进行改造,要求我们在查询用户的时候把地址也查出来Controller层:@ApiO......
  • Spring
    Spring**1、Spring框架是什么?Spring框架是一个开源的、轻量级的、基于Java的应用程序开发框架,用于构建企业级应用。它提供了全面的基础设施支持,包括依赖注入(DependencyInjection)、面向切面编程(Aspect-OrientedProgramming)、事务管理(TransactionManagement)、数据访问(Data......
  • Spring容器系列-启动原理
    Spring容器系列-启动原理  Spring具有非常庞大的体系,但无论多大都是由一个个小的模块组合而来,不管是SpringMvc还是SpringBoot,都是在Spring的基础上延伸而来,因此,看源码要找对方向。  我们知道,在SpringBoot之前,对象的管理和配置都是通过XML的方式来实现的,那么Spring是......
  • SpringBoot基础篇
    SpringBoot视频链接:【黑马程序员SpringBoot3+Vue3全套视频教程,springboot+vue企业级全栈开发从基础、实战到面试一套通关】https://www.bilibili.com/video/BV14z4y1N7pg?p=20&vd_source=12bdb5b78bd5d1c45cab173f3aad839b概述SpringBoot是Spring提供的一个子项目......
  • SpringBoot开发Activiti工作流实现审批流程(全套源码)
    前言activiti工作流引擎项目,企业erp、oa、hr、crm等企事业办公系统轻松落地,一套完整并且实际运用在多套项目中的案例,满足日常业务流程审批需求。一、项目形式springboot+vue+activiti集成了activiti在线编辑器,流行的前后端分离部署开发模式,快速开发平台,可插拔工作流服务。工......
  • SpringBoot配置第三方专业缓存技术Memcached 下载 安装 整合测试 5000字详解
    Memcached下载和安装是一个国内使用量还是比较大的技术打开文件夹我们需要在命令行窗口启动注意要以管理员方式运行先尝试进入指定文件然后又再次运行下载memcached.exe-dinstall启动memcached.exe-dstart停止memcached.exe-dstopmemcached.exe-din......