精华内容
下载资源
问答
  • 5大通知类型中,环绕通知功能最为强大,因为环绕通知,可以控制目标方法是否执行。 如果需要记录异常信息,使用异常通知。 其他通知,只能做记录工作,不能做处理 切入点表达式 within表达式 粗粒度的...
    AOP Aspect-OrientedProgramming 面向切面编程

    可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
    在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程
    主要目的:
    将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
    在这里插入图片描述在这里插入图片描述
    本质是在一系列纵向的控制流程中,把那些相同的子流程提取成一个横向的面

    术语
    Aspect(切面)

    aspect 由 pointcount 和 advice 组成, 它既包含了横切逻辑的定义, 也包括了连接点的定义. Spring AOP就是负责实施切面的框架, 它将切面所定义的横切逻辑织入到切面所指定的连接点中.
    AOP的工作重心在于如何将增强织入目标对象的连接点上, 这里包含两个工作:

    1. 如何通过 pointcut 和 advice 定位到特定的 joinpoint 上
    2. 如何在 advice 中编写切面代码.
      可以简单地认为, 使用 @Aspect 注解的类就是切面.
    advice(通知/增强)

    由 aspect 添加到特定的 join point(即满足 point cut 规则的 join point) 的一段代码.
    许多 AOP框架, 包括 Spring AOP, 会将 advice 模拟为一个拦截器(interceptor), 并且在 join point 上维护多个 advice, 进行层层拦截.
    例如 HTTP 鉴权的实现, 我们可以为每个使用 RequestMapping 标注的方法织入 advice, 当 HTTP 请求到来时, 首先进入到 advice 代码中, 在这里我们可以分析这个 HTTP 请求是否有相应的权限, 如果有, 则执行 Controller, 如果没有, 则抛出异常. 这里的 advice 就扮演着鉴权拦截器的角色了.

    连接点(join point)

    程序运行中的一些时间点, 例如一个方法的执行, 或者是一个异常的处理.
    在 Spring AOP 中, join point 总是方法的执行点, 即只有方法连接点.

    切点(point cut)

    匹配 join point 的谓词(a predicate that matches join points).
    Advice 是和特定的 point cut 关联的, 并且在 point cut 相匹配的 join point 中执行.
    在 Spring 中, 所有的方法都可以认为是 joinpoint, 但是我们并不希望在所有的方法上都添加 Advice, 而 pointcut 的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配joinpoint, 给满足规则的 joinpoint 添加 Advice.

    关于join point 和 point cut 的区别

    在 Spring AOP 中, 所有的方法执行都是 join point. 而 point cut 是一个描述信息, 它修饰的是 join point, 通过 point cut, 我们就可以确定哪些 join point 可以被织入 Advice. 因此 join point 和 point cut 本质上就是两个不同纬度上的东西.
    advice 是在 join point 上执行的, 而 point cut 规定了哪些 join point 可以执行哪些 advice

    织入(Weaving)

    将 aspect 和其他对象连接起来, 并创建 adviced object 的过程.
    把切面应用到目标对象来创建新的代理对象的过程。
    根据不同的实现技术, AOP织入有三种方式:

    1. 编译器织入, 这要求有特殊的Java编译器.
    2. 类装载期织入, 这需要有特殊的类装载器.
    3. 动态代理织入, 在运行期为目标类添加增强(Advice)生成子类的方式.
      Spring 采用动态代理织入, 而AspectJ采用编译器织入和类装载期织入.
    引入(introduction)

    允许我们向现有的类添加新方法属性。就是把切面(也就是新方法属性:通知定义的)用到目标类中吗。
    在这里插入图片描述

    spring实现AOP两种方式
    1. 基于XML Schema的配置文件定义
    2. 通过@Aspect系列标注定义
    AOP步骤
    1. 定义切面类-aspect
    2. 定义连接点-pointcut
    3. 定义通知:五种通知
      1. 环绕通知:必须添加ProceedingJoinPoint
        在目标方法执行之前,执行之后都要执行(环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行)
      2. 前置通知Before advice:
        在目标方法执行之前执行
      3. 后置通知After returning advice:
        在目标方法执行之后执行
      4. 异常通知After throwing advice:
        目标方法执行后抛出异常才执行
      5. 最终通知After (finally) advice:
        在目标方法执行之后 都会执行的通知
    4. 配置切面
    通知选取规则
    1. 5大通知类型中,环绕通知功能最为强大,因为环绕通知,可以控制目标方法是否执行。
    2. 如果需要记录异常信息,使用异常通知。
    3. 其他通知,只能做记录工作,不能做处理
    切入点表达式
    1. within表达式 粗粒度的只能控制类,用于匹配指定类型内的方法执行
    2. Execution(返回值类型 包名.类名.方法名(参数列表)),用于匹配方法执行的连接点
    3. this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
    4. target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
    5. args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;
    示例

    Add(int a)

    1. 例子1:
    <aop:pointcut expression=
    "execution(int service.UserServiceImpl.add())" id="txPointcut"/>
    

    改切点表达式表示
    返回值为int 包名类名serviceUserServiceImpl 方法为add()的匹配规则

    1. 例子2:
    <aop:pointcut expression="execution(* service.*.add())" id="txPointcut"/>
    

    规则:返回值值任意, 包名service下子类的add(),只能包含一层,子孙类不行。

    1. 例子3:
    <aop:pointcut
    expression="execution(* service..*.add())" id="txPointcut"/>
    

    规则:方法返回值任意, service包下的所有子孙类的add()

    1. 例子4:
    <aop:pointcut expression=
    "execution(* service..*.add(int,String))" id="txPointcut"/>
    

    规则:返回值的类型任意 service子孙包下的add方法参数类型为int,String
    5. 例子5

    <aop:pointcut expression=
    "execution(* service..*.add(..))" 
    id="txPointcut"/>
    

    规则:返回值类型任意 service下的所有子孙类.add方法() (参数任意)
    6. 例子6:
    要求返回值为任意的,service包下的全部类的全部方法的任意参数

    Execution(* service..*.*(..))
    

    简化:

    execution(* service..*(..))
    
    为什么会有AOP
    1. 首先原始代码加一个事务控制,需要在每个方法中添加事务控制的代码,这样会有大量重复代码
    2. 使用静态代理的话(实现同一个接口,内部维护一个代理对象)方法多时,代码成本增加
    3. 动态代理(JDK动态生成了一个类去实现接口,隐藏了这个过程)前提目标类必须有实现的接口
    4. cglib(动态生成的子类继承目标的方式实现,在运行期动态的在内存中构建一个子类)前提cglib目标类不能为final
    5. AOP以此灵活选择JDK和cglib
      源码
      在这里插入图片描述
    AOP的调用原理
    1. 当spring容器解析到AOP标签时,开启启动AOP的相关配置
    2. 当解析到切入点表达式时,该表达式会进入spring内存中保留
    3. 当解析到切面类时,首先会为切面创建对象。并且根据切入点表达式,和通知的匹配关系进行绑定。
    4. 如果从容器中获取对象时,如果该对象与切入点表达式中的规则匹配。
      则会为其创建代理对象,如果该类实现了接口,则会为其创建JDK的代理,如果该类没有实现接口,则会采用cglib进行代理。代理对象创建完成后,交给用户使用。
    5. 当代理对象执行方法时,则会执行与切入点表达式绑定的通知方法。
    使用注解配置切入点
    1. 将切入点写入通知内部
      @Before(value=“execution(* service….(…))”)
      缺点:如果该切入点需要重复使用,则必须重复写多次
    2. 自定义方法编辑切入点
      @Pointcut(value=“execution(* service….(…))”)
      public void pointcut(){}
      优点:
      1. 可以实现切入点表达式的复用
      2. 方便表达式管理
    spring AOP用处
    1. 声明式事务管理
    2. Controller层的参数校验
    3. 实现数据库读写分离
    4. 执行方法前判断是否具有权限
    5. 对部分函数的调用进行日志记录。监控部分重要函数,若抛出指定的异常,可以以短信或邮件方式通知相关人员
    6. 信息过滤,页面转发
    7. 检测执行性能
    示例

    如初Action对象在执行actionSomeThing时会输出什么

    package com.example.demo;
    
    public class AOPTest {
        public void actionSomeThing(){
            System.out.println(1);
        }
    
    }
    @Aspect
    class AspectJTest{
        @Pointcut("execution(* com.example.demo.AOPTest*.*(..))")
        public void performance(){
    
        }
        @Before("performance()")
        public void before(){System.out.println(2);}
        @AfterReturning("performance()")
        public void afterReturn(){System.out.println(3);}
        @After("performance()")
        public void after(){System.out.println(4);}
        @Around("performance()")
        public Object around(ProceedingJoinPoint joinPoint){
            Object obj = null;
            try{
                System.out.println(5);
                joinPoint.proceed();
                System.out.println(6);
            }catch (Throwable e){
                e.printStackTrace();
            }
            return obj;
        }
    }
    

    521643

    @Pointcut("execution(* com.example.demo.AOPTest*.*(..))")
    

    定了一个切入点,正则表达式的意思是在执行com.bwf.web.Action类的所有方法时,将会发送performance切入点通知。
    @Before是在所拦截方法执行之前执行一段逻辑。
    @After 是在所拦截方法执行之后执行一段逻辑。
    @Around是可以同时在所拦截方法的前后执行一段逻辑。
    @AfterReturning在所拦截方法return后执行该注解的函数
    本例中,

    1. 首先要执行的是@Around注解的函数,所以,先打印5。
    2. obj = joinpoint.proceed();调用的就是actionSomeThing函数本身。那么在执行actionSomeThing函数前,首先要执行@Before注解函数,因此,再打印2
    3. 之后是performance函数执行,打印1
    4. 接下来执行around的最后一步输出,打印6
    5. 然后拦截函数执行完毕,调用@After注解函数,打印4
    6. 最后拦截函数return后,执行@AfterReturn注解函数,打印3
    执行顺序
    [Aspect1] around advise 1
    [Aspect1] before advise
    method
    [Aspect1] around advise2
    [Aspect1] after advise
    [Aspect1] afterReturning advise
    

    在这里插入图片描述

    注解配置切面参数说明
    1. 除了@Around外,每个方法里都可以加或者不加参数JoinPoint,如果有用JoinPoint的地方就加,不加也可以,JoinPoint里包含了类名、被切面的方法名,参数等属性,可供读取使用。
    2. @Around参数必须为ProceedingJoinPoint,pjp.proceed相应于执行被切面的方法。
    3. @AfterReturning方法里,可以加returning = “XXX”,XXX即为在controller里方法的返回值,本例中的返回值是“first controller”。
    4. @AfterThrowing方法里,可以加throwing = “XXX”,供读取异常信息
    异常通知示例
    /**
     * 能够接受异常信息,获取当前方法
     * 如果添加JoinPoint,必须位于第一位
     */
    @AfterThrowing(value = "performance()",throwing = "throwable")
    public void throwss(JoinPoint joinPoint,Throwable throwable){
        System.out.println("获取异常信息"+throwable.getMessage());
        System.out.println("获取异常类型"+throwable.getClass());
        System.out.println("当前执行的方法名为"+joinPoint.getSignature().getName());
        System.out.println("方法异常时执行.....");
    }
    执行
    @RequestMapping("/aoptest")
    public String tets() {
        aopTest.actionSomThing();//生效
        return "AAA";
    }
    输出
    获取异常信息/ by zero
    获取异常类型class java.lang.ArithmeticException
    当前执行的方法名为asdada
    方法异常时执行.....
    
    异常情况的执行顺序
    [Aspect1] around advise 1
    [Aspect1] before advise
    throw an exception
    [Aspect1] after advise
    [Aspect1] afterThrowing advise
    

    在这里插入图片描述

    遇到的问题service切入点不生效
    package com.example.demo.AOP;
    public interface AOPTest {
        public void actionSomThing();
    }
    @Service
    public class AOPTestImpl implements  AOPTest {
        public void actionSomThing(){
            System.out.println(1);
        }
    
    }
    @Component
    @Aspect
    public class AspectJTest{
        @Pointcut("execution(public * com.example.demo.AOP.*.*(..))")
        public void performance(){}
        @Before("performance()")
        public void before(){System.out.println(2);}
        @AfterReturning("performance()")
        public void afterReturn(){System.out.println(3);}
        @After("performance()")
        public void after(){System.out.println(4);}
        @Around("performance()")
        public Object around(ProceedingJoinPoint joinPoint){
            Object obj = null;
            try{
                System.out.println(5);
                 obj = joinPoint.proceed();
                System.out.println(6);
            }catch (Throwable e){
                e.printStackTrace();
            }
            return obj;
        }
    }
    @Autowired
    AOPTest aopTest;
    @RequestMapping("/")
    public String test() {
        new AOPTestImpl().actionSomThing();//不生效
        return "BBB";
    }
    
    @RequestMapping("/aoptest")
    public String tets() {
        aopTest.actionSomThing();//生效
        return "AAA";
    }
    

    当时琢磨了好久,后来经人点拨,AOP是是靠动态代理实现的,直接new的话相当于自力更生调用了原来的方法,而使用spring生成的对象就经过了动态代理这一步骤。

    多个环绕通知的执行规则

    在这里插入图片描述
    如果有多个环绕通知,则或先执行下一个通知,如果没有下一个通知,则会执行目标方法
    其结构是一种嵌套关系
    在这里插入图片描述
    示例:还是原来那个叭,改一下execution

    package com.example.demo.AOP;
    interface AOPTest {
        public void actionSomThing();
    }
    @Service
    public class AOPTestImpl implements  AOPTest {
        public void actionSomThing(){
            System.out.println(1);
        }
    
    }
    @Component
    @Aspect
    public class AspectJTest{
        @Pointcut("execution(public * com.example.demo.AOP.*.*(..))")
        public void performance(){}
        @Before("performance()")
        public void before(){System.out.println(2);}
        @AfterReturning("performance()")
        public void afterReturn(){System.out.println(3);}
        @After("performance()")
        public void after(){System.out.println(4);}
        @Around("performance()")
        public Object around(ProceedingJoinPoint joinPoint){
            Object obj = null;
            try{
                System.out.println(5);
                 obj = joinPoint.proceed();
                System.out.println(6);
            }catch (Throwable e){
                e.printStackTrace();
            }
            return obj;
        }
    }
    @Controller
    public class main {
        @RequestMapping("/aoptest2")
        public String tets(){//这个方法也会被配置的AOP切到
            aopTest.actionSomThing();
            return "BBB";
        }
    }
    
    1. 首先执行@Around注解的函数,进入第一个AOP事件,所以先打印5
    2. 执行方法内容前,执行@Before注解函数,打印2
    3. obj = joinpoint.proceed(),然后发现方法内容也是定义的切面,再次进入@Around注解的函数,进入第二个AOP事件的环绕通知,执行5
    4. joinpoint.proceed();,执行actionSomething函数前执行@Before注解函数,打印2
    5. actionSomething函数打印1
    6. 然后第二个环绕通知最后一步打印6
    7. 然后拦截函数执行完毕,调用@After注解函数,打印4
    8. 然后拦截函数return后,执行@AfterReturn注解函数,打印3
    9. actionSomeThing方法执行完毕,第二个AOP事件结束,在第一个环绕通知最后一步打印6
    10. 然后拦截函数执行完毕,调用@After注解函数,打印4
    11. 最后拦截函数return后,执行@AfterReturn注解函数,打印3
    5
    2
    5
    2
    1
    6
    4
    3
    6
    4
    3
    

    在这里插入图片描述
    但是这种情况是同一个方法多层被切面,AOP切面是同一个

    还有种情况是多个AOP切面

    把上个例子的数字打印换为具体的通知类型看着清楚些

    @Component
    @Aspect
    public class AspectJTest{
        @Pointcut("execution(public * com.example.demo.AOP.*.actionSomThing(..))")
        public void performance(){}
        @Before("performance()")
        public void before(){System.out.println("AspectJTest1-Before");}
        @AfterReturning("performance()")
        public void afterReturn(){System.out.println("AspectJTest1-AfterReturning");}
        @After("performance()")
        public void after(){System.out.println("AspectJTest1-After");}
        @Around("performance()")
        public Object around(ProceedingJoinPoint joinPoint){
            Object obj = null;
            try{
                System.out.println("AspectJTest1-AroundFirst");
                 obj = joinPoint.proceed();
                System.out.println("AspectJTest1-AroundSecond");
            }catch (Throwable e){
                e.printStackTrace();
            }
            return obj;
        }
    }
    @Component
    @Aspect
    public class AspectJTest2 {
        @Pointcut("execution(public * com.example.demo.AOP.*.actionSomThing(..))")
        public void performance(){}
        @Before("performance()")
        public void before(){System.out.println("AspectJTest2-Before");}
        @AfterReturning("performance()")
        public void afterReturn(){System.out.println("AspectJTest2-AfterReturning");}
        @After("performance()")
        public void after(){System.out.println("AspectJTest2-After");}
        @Around("performance()")
        public Object around(ProceedingJoinPoint joinPoint){
            Object obj = null;
            try{
                System.out.println("AspectJTest2-AroundFirst");
                 obj = joinPoint.proceed();
                System.out.println("AspectJTest2-AroundSecond");
            }catch (Throwable e){
                e.printStackTrace();
            }
            return obj;
        }
    }
    输出
    AspectJTest1-AroundFirst
    AspectJTest1-Before
    AspectJTest2-AroundFirst
    AspectJTest2-Before
    1
    AspectJTest2-AroundSecond
    AspectJTest2-After
    AspectJTest2-AfterReturning
    AspectJTest1-AroundSecond
    AspectJTest1-After
    AspectJTest1-AfterReturning
    

    但是这种情况项目重启之后也有可能是这种情况

    AspectJTest2-AroundFirst
    AspectJTest2-Before
    AspectJTest1-AroundFirst
    AspectJTest1-Before
    1
    AspectJTest1-AroundSecond
    AspectJTest1-After
    AspectJTest1-AfterReturning
    AspectJTest2-AroundSecond
    AspectJTest2-After
    AspectJTest2-AfterReturning
    

    也就是说aspect1和aspect2的执行顺序是未知的
    那么怎么制定aspect的执行顺序呢

    指定aspect的执行顺序
    1. 实现org.springframework.core.Ordered接口,实现它的getOrder()方法
    2. getaspect添加@Order注解,该注解全称为org.springframework.core.annotation.Order
      值越小越先执行
    @Component
    @Aspect
    @Order(5)
    public class AspectJTest{
        //
    }
    @Component
    @Aspect
    @Order(1)
    public class AspectJTest2 {
        //
    }
    AspectJTest2-AroundFirst
    AspectJTest2-Before
    AspectJTest1-AroundFirst
    AspectJTest1-Before
    1
    AspectJTest1-AroundSecond
    AspectJTest1-After
    AspectJTest1-AfterReturning
    AspectJTest2-AroundSecond
    AspectJTest2-After
    AspectJTest2-AfterReturning
    

    注意点

    • 如果在同一个aspect类中,针对同一个pointcut,定义了两个相同的advice(比如两个@Before),那么这两个advice的执行顺序是无法确定的,哪怕给两个advice添加了@Order注解也不行
    • 对于@Around环绕通知,不管它有没有返回值,但是必须要方法内部,调用一下 pjp.proceed();否则,Controller 中的接口将没有机会被执行,从而也导致了 @Before这个advice不会被触发。比如,我们假设正常情况下,执行顺序为”aspect2 -> apsect1 -> controller”
      比如我将AspectTest改为这样
    @Component
    @Aspect
    public class AspectJTest{
        @Pointcut("execution(public * com.example.demo.AOP.*.actionSomThing(..))")
        public void performance(){}
        @Before("performance()")
        public void before(){System.out.println("AspectJTest1-Before");}
        @AfterReturning("performance()")
        public void afterReturn(){System.out.println("AspectJTest1-AfterReturning");}
        @After("performance()")
        public void after(){System.out.println("AspectJTest1-After");}
        @Around("performance()")
        public Object around(ProceedingJoinPoint joinPoint){
            Object obj = null;
            try{
                System.out.println("AspectJTest1-AroundFirst");
    //             obj = joinPoint.proceed();
                System.out.println("AspectJTest1-AroundSecond");
            }catch (Throwable e){
                e.printStackTrace();
            }
            return obj;
        }
    }
    输出
    AspectJTest1-AroundFirst
    AspectJTest1-AroundSecond
    AspectJTest1-After
    AspectJTest1-AfterReturning
    

    可以看到@Before注解的函数没有执行,原方法也未执行
    在这里插入图片描述
    spring aop就是一个同心圆,要执行的方法为圆心,最外层的order最小。从最外层按照AOP1、AOP2的顺序依次执行doAround方法,doBefore方法。然后执行method方法,最后按照AOP2、AOP1的顺序依次执行doAfter、doAfterReturn方法。也就是说对多个AOP来说,先before的,一定后after。
    那不同的切面,顺序怎么决定呢,尤其是同格式的切面处理,譬如两个execution的情况,那spring就是随机决定哪个在外哪个在内了。
    所以大部分情况下,我们需要指定顺序,最简单的方式就是在Aspect切面类上加上@Order(1)注解即可,order越小最先执行,也就是位于最外层。像一些全局处理的就可以把order设小一点,具体到某个细节的就设大一点。

    参考:
    Spring AOP是什么?你都拿它做什么?
    Spring AOP 切入点表达式
    Spring boot中使用aop详解
    Spring AOP @Before @Around @After 等 advice 的执行顺序
    彻底征服 Spring AOP 之 理论篇

    展开全文
  • spring aop 通知的执行顺序

    千次阅读 2020-07-16 15:15:20
    1、通知的执行顺序。 2、多个切面的情况下,可以通过@Order指定先后顺序,数字越小,优先级越高。多个aop指定顺序时,order要加在class上面

    1、五种通知的执行顺序。

     2、多个切面的情况下,可以通过@Order指定先后顺序,数字越小,优先级越高。多个aop指定顺序时,order要加在class上面

     

    展开全文
  • Spring的五大通知类型

    千次阅读 2019-07-01 14:17:13
    1前置通知 在目标方法执行之前执行执行的通知 前置通知方法,可以没有参数,也可以额外接收一个JoinPoint,Spring会自动将该对象传入,代表当前的连接点,通过该对象可以获取目标对象 和 目标方法相关的信息。 ...

    1前置通知

    在目标方法执行之前执行执行的通知

    前置通知方法,可以没有参数,也可以额外接收一个JoinPoint,Spring会自动将该对象传入,代表当前的连接点,通过该对象可以获取目标对象 目标方法相关的信息

    注意,如果接收JoinPoint,必须保证其为方法的第一个参数,否则报错。(方法可接收参数JoinPoint jp )

    Object obj=jp.getTarget();//获取目标对象

    MethodSignature ms=(MethodSignature) jp.getSignature();//获取方法

    Method method=ms.getMethod();//获取方法,(导反射包的)

    ms.getReturnType();//返回类型

    ms.getExceptionTypes();//异常类型

    ms.getName();//方法名

    ms.getParameterTypes();//参数类型

    ms.getParameterNames();//参数名
                <!-- 前置通知 -->
                <aop:before method="before" pointcut-ref="pc01" />

     

    2环绕通知

    在目标方法执行之前和之后都可以执行额外代码的通知。

    在环绕通知中必须显式的调用目标方法,否则目标方法不会执行。

    这个显式调用时通过ProceedingJoinPoint来实现的,可以在环绕通知中接收一个此类型的形参,spring容器会自动将该对象传入,这个参数必须处在环绕通知的第一个形参位置

    **要注意,只有环绕通知可以接收ProceedingJoinPoint,而其他通知只能接收JoinPoint。


                <!-- 环绕通知 -->
                <aop:around method="around" pointcut-ref="pc02"/>

     

    环绕通知需要返回返回值,否则真正调用者将拿不到返回值,只能得到一个null。

        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("环绕通知around");
            Object obj = pjp.proceed();// 执行直接的被代理者的方法.不调用的话,则不会执行
            return obj;
        }

    环绕通知有

    控制目标方法是否执行、目标方法执行之前或之后执行额外代码、有控制是否返回值、甚至改变返回值

    的能力

    环绕通知虽然有这样的能力,但一定要慎用,要小心不要破坏了软件分层的“高内聚 低耦合”的目标。

    3 后置通知

    在目标方法执行之后执行的通知

    在后置通知中也可以选择性的接收一个JoinPoint来获取连接点的额外信息,但是这个参数必须处在参数列表的第一个。在后置通知中,还可以通过配置获取返回值


                <!-- 后置通知 -->
                <aop:after-returning method="afterreturning" pointcut-ref="pc01" returning="msg"/>


        public void afterreturning(JoinPoint jp, Object msg) {
            System.out.println("后置通知");
        }

    4 异常通知

    在目标方法抛出异常时执行的通知

     

    可以配置传入JoinPoint获取目标对象和目标方法相关信息,但必须处在参数列表第一位

    另外,还可以配置参数,让异常通知可以接收到目标方法抛出的异常对象


                <!-- 异常通知 -->
                <aop:after-throwing method="afterthrowing" pointcut-ref="pc01" throwing="e"/>


        public void afterthrowing(JoinPoint jp, Throwable e) {
            System.out.println("异常通知:" + e.getMessage());

        }

    5 最终通知

    是在目标方法执行之后执行的通知。和后置通知不同之处在于,后置通知是在方法正常返回后执行的通知,如果方法没有正常返-例如抛出异常,则后置通知不会执行。而最终通知无论如何都会在目标方法调用过后执行,即使目标方法没有正常的执行完成。另外,后置通知可以通过配置得到返回值,而最终通知无法得到

    最终通知也可以额外接收一个JoinPoint参数,来获取目标对象和目标方法相关信息,但一定要保证必须是第一个参数


                <!-- 最终通知 -->
                <aop:after method="after" pointcut-ref="pc01" />
        public void after(JoinPoint jp) {
            System.out.println("最终通知");
        }

    五种通知执行的顺序


                在目标方法没有抛出异常的情况下
                    *前置通知*
                    *环绕通知的调用目标方法之前的代码*//取决于配置顺序
                    目标方法
                    *环绕通知的调用目标方法之后的代码*
                    *后置通知*//取决于配置顺序
                    最终通知//最终通知 后置通知和环绕通知结束取决于配置反向顺序
                 在目标方法抛出异常的情况下:
                    *前置通知*
                    *环绕通知的调用目标方法之前的代码*//取决于配置顺序
                    目标方法 抛出异常 
                    异常通知
                    最终通知
                如果存在多个切面:
                    多切面执行时,采用了责任链设计模式。

    切面的配置顺序决定了切面的执行顺序,多个切面执行的过程,类似于方法调用的过程,在环绕通知的proceed()执行时,去执行下一个切面或如果没有下一个切面执行目标方法从而达成了如下的执行过程:

     

    如果目标方法抛出异常:

     

    五种通知的常见使用场景


                前置通知    记录日志(方法将被调用) 
                环绕通知    控制事务 权限控制
                后置通知    记录日志(方法已经成功调用)
                异常通知    异常处理 控制事务
                最终通知    记录日志(方法已经调用,但不一定成功)

    展开全文
  • 1.前置通知 在目标方法执行之前执行执行的通知。 前置通知方法,可以没有参数,也可以额外接收一个JoinPoint,Spring会自动将该对象传入,代表当前的连接点,通过该对象可以获取目标对象 和 目标方法相关的信息。 ...

    1.前置通知

    在目标方法执行之前执行执行的通知。

    前置通知方法,可以没有参数,也可以额外接收一个JoinPoint,Spring会自动将该对象传入,代表当前的连接点,通过该对象可以获取目标对象 和 目标方法相关的信息。

    注意,如果接收JoinPoint,必须保证其为方法的第一个参数,否则报错

    配置方法:

    2.环绕通知

    在目标方法执行之前和之后都可以执行额外代码的通知。

    在环绕通知中必须显式的调用目标方法,否则目标方法不会执行

    这个显式调用是通过ProceedingJoinPoint来实现的,可以在环绕通知中接收一个此类型的形参,spring容器会自动将该对象传入,这个参数必须处在环绕通知的第一个形参位置。

    **ProceedingJoinPoint时JoinPoint的子类,要注意,只有环绕通知可以接收ProceedingJoinPoint,而其他通知只能接收JoinPoint。

    配置方式:

    环绕通知需要返回返回值,否则真正调用者将拿不到返回值,只能得到一个null。

    环绕通知有以下能力

    • 控制目标方法是否执行
    • 目标方法执行之前或之后执行额外代码
    • 控制是否返回返回值
    • 改变返回值

    环绕通知虽然有这样的能力,但一定要慎用(因为在里面可以修改方法的返回值等信息),要小心不要破坏了软件分层的“高内聚 低耦合”的目标。

    3.后置通知

    在目标方法执行之后成功执行的通知。

    在后置通知中也可以选择性的接收一个JoinPoint来获取连接点的额外信息,但是这个参数必须处在参数列表的第一个。

    在后置通知中,还可以通过配置获取目标方法的返回值

     

     

    一定要保证JoinPoint处在参数列表的第一位,否则抛异常

     4.异常通知

    在目标方法抛出异常时执行的通知

    配置方法

    可以配置传入JoinPoint获取目标对象和目标方法相关信息,但必须处在参数列表第一位

    另外,还可以配置参数,让异常通知接收到目标方法抛出的异常对象

     5.最终通知

    是在目标方法执行之后执行的通知。和后置通知不同之处在于,后置通知是在方法正常返回后执行的通知,如果方法没有正常返-例如抛出异常,则后置通知不会执行。而最终通知无论如何都会在目标方法调用过后执行,即使目标方法没有正常的执行完成。另外,后置通知可以通过配置得到返回值,而最终通知无法得到。

    配置方式:

    最终通知也可以额外接收一个JoinPoint参数,来获取目标对象和目标方法相关信息,但一定要保证必须是第一个参数。

    6.五种通知执行位置

    7.五种通知执行的顺序

     7.1在目标方法没有抛出异常的情况下

    *前置通知*

    *环绕通知的调用目标方法之前的代码*//取决于配置顺序

    目标方法

    *环绕通知的调用目标方法之后的代码*

    *后置通知

    *最终通知//取决于配置顺序

    7.2在目标方法抛出异常的情况下

    *前置通知*

    *环绕通知的调用目标方法之前的代码*//取决于配置顺序

    目标方法 抛出异常

    *异常通知*

    *最终通知*//取决于配置顺序

    7.3如果存在多个切面

     多切面执行时,采用了责任链设计模式

    切面的配置顺序决定了切面的执行顺序,多个切面执行的过程,类似于方法调用的过程,在环绕通知的proceed()执行时,去执行下一个切面或如果没有下一个切面执行目标方法,从而达成了如下的执行过程:

    如果目标方法抛出异常

     8.五种通知的常见使用场景

    前置通知

    记录日志(方法将被调用)

    环绕通知

    控制事务 权限控制

    后置通知

    记录日志(方法已经成功调用)

    异常通知

    异常处理 控制事务

    最终通知

    记录日志(方法已经调用,但不一定成功)

    展开全文
  • 在spring中的AOP有通知类型,分别为前置通知、后置通知、环绕通知、最终通知、和异常处理 其中这次我需要讲的是环绕通知和前置通知的启动顺序问题,默认的是环绕通知先进行启动,然后再是前置通知 但是在一定...
  • spring aop的通知类型

    万次阅读 2015-07-20 10:41:44
    spring aop通知(advice)分成类: 前置通知、正常返回通知、异常返回通知、返回通知、环绕通知
  • Spring_动态代理_通知的执行顺序

    千次阅读 2019-07-25 23:33:33
    *环绕通知:是优先于普通通知执行,执行顺序; * *[普通前置] *{ * try{ * 环绕前置 * 环绕执行:目标方法执行 * 环绕返回 * }catch(){ * 环绕出现异常 * }finally{ * 环绕后置 * } *} * * *...
  • 、ReadWriteLock 六、Condition接口 简介 Condition接口的常见方法: . 使用单个Condition实例实现等待/通知机制: 使用多个Condition实例实现等待/通知机制: 使用Condition实现顺序执行 七、...
  • Spring - AspectJ 注解实现AOP的通知

    千次阅读 2016-12-16 16:40:46
    【1】AOP通知方法 通知方法可以简单理解为标注有某种注解的简单的 Java 方法,在目标方法执行时的某个位置(时机)进行执行。 AspectJ 支持 5 种类型的通知注解: @Before: 前置通知, 在方法执行之前执行 @After: ...
  • 消息的顺序问题 消息的重复问题 RocketMQ作为阿里开源的一款高性能、高吞吐量的消息中间件,它是怎样来解决这两个问题的?RocketMQ有哪些关键特性?其实现原理是怎样的? 关键特性及其实现原理 一、顺序...
  • SpringBoot+SpringAOP的通知实现

    千次阅读 2019-04-19 11:11:08
    首先增加Maven依赖 < dependency > ...可以看到执行顺序是 拦截器preHandle->Around Before->Before->Around return->Around After->After->After return->postHandle->afterCompletion
  • 消息的顺序问题 消息的重复问题 RocketMQ作为阿里开源的一款高性能、高吞吐量的消息中间件,它是怎样来解决这两个问题的?RocketMQ有哪些关键特性?其实现原理是怎样的? 关键特性及其实现原理 一、顺序...
  • RocketMQ中的顺序消息

    千次阅读 2019-04-14 15:18:14
    我们在项目开发过程中,有需要使用RocketMQ顺序消息的场景,该如何使用呢?顺序消息的原理是怎样的呢?本文进行了一些探讨。 一、顺序消息的定义 顺序消息(FIFO:First Input First Output)是一种严格按照顺序进行...
  • Oracle架构实现原理、含五大进程解析(图文详解)

    万次阅读 多人点赞 2016-06-04 12:25:49
    :数据库缓冲区缓存的大小会对性能产生至关重要的影响,具体需要多的Size才能成为最佳配比还要结合实际的生产环境而言。总体而言可以依据以下两点基本要求来判断: 1. 缓存应足够,以便能缓存所有被频繁访问的...
  • Spring AOP之多切面运行顺序

    千次阅读 2019-11-01 19:19:29
    当一个方法的执行被多个切面共同切的时候,环绕通知只影响当前切面的通知顺序,例如创建两个切面logUtil,validateUtil两个切面共同监视计算器类的加法运算,add(int a,int b);测试中,看切面工具类的名称首字母,...
  • 消息的顺序问题 消息的重复问题 RocketMQ作为阿里开源的一款高性能、高吞吐量的消息中间件,它是怎样来解决这两个问题的?RocketMQ有哪些关键特性?其实现原理是怎样的? 关键特性及其实现原...
  •  排序保证:有些场景需要按照产品的顺序进行处理比如单进单出从而保证数据按照一定的顺序处理,使用消息队列是可以的。 以上都是消息队列常见的使用场景,当然消息队列只是一个中间件,可以配合其他产品进行使用...
  • 初学springCloud(基础五大组件)

    万次阅读 多人点赞 2019-03-05 14:48:04
    当服务进行正常关闭的时候,服务方会发送一个REST请求给EurekaServer,通知“我已经下线” 服务剔除: 特殊情况下出现内存溢出或者网络波动严重使得服务无法正常工作,而服务注册中心并未收到服务下线的请求,...
  • (二)对于微服务,传统分布式事务存在的问题 在微服务架构中,传统分布式事务并不是实现数据一致性的最佳选择,主要有三问题: 问题一:对于微服务架构来说,数据访问变得更加复杂,为达到微服务间的松耦合和...
  • 你脑中想到的可能是这样: 你可能会采用这种方式保证消息顺序 假定M1发送到S1,M2发送到S2,如果要保证M1先于M2被消费,那么需要M1到达消费端被消费后,通知S2,然后S2再将M2发送到消费端。 这个模型存在的问题是,...
  • 一、五大过程组概述 1.1 过程的定义 1.2 五大过程组的由来 1.3 五大过程组49 1.3.1 启动 2 13% 1.3.2 规划 24 24% 1.3.3 执行 8 30% 1.3.4 监控 11 25% 1.3.5 收尾 1 8% 1.4 过程组间的输入输出概览 二、 工作...
  • OkHttp五大拦截器(自己专业整理)

    千次阅读 2018-10-23 12:06:56
     (五大拦截器) 一、RetryAndFollowUpInterceptor (重定向拦截器)  RetryAndFollowUpInterceptor 的拦截操作中做了这么几件事:  1、创建一个 StreamAllocation  2、发起请求  3、请求异常时会...
  • 通过对特定的锁进行释放而达到可定义的通知顺序。 如果实现合适,那么这种模式的执行代价是最小的。然而不可避免的要增加编码的复杂性,但是这个复杂性可以通过你得到的控制性抵消掉,如果你需要这样的控制,你可以...
  • RocketMQ顺序、重复问题

    千次阅读 2017-09-06 21:02:42
    消息的顺序问题 消息的重复问题   RocketMQ作为阿里开源的一款高性能、高吞吐量的消息中间件,它是怎样来解决这两个问题的?RocketMQ有哪些关键特性?其实现原理是怎样的?   关键特性及其实现原理
  • 顺序图(时序图)前言顺序顺序图的组成元素消息的分类顺序图提供动态视角例 饮料销售机问题域例 带界面的简易计算器应用程序框架结构MVC设计模式例 用户登录(有时间再完善)SSH包图 前言       &...
  • iOS APP启动函数调用顺序~详解

    千次阅读 2017-06-30 00:43:54
    、 AppDelegate 的代理方法 //app 启动完毕后就会调用 -  ( BOOL ) application: ( UIApplication * ) application didFinishLaunchingWithOptions: ( NSDictionary * ) launchOptions { ...
  • RocketMQ的顺序消费和事务消费

    万次阅读 2017-02-27 11:29:19
    1、一个topic默认有4个队列 2、一个生产者可以对多个主题去发送消息 3、三种消费 普通消费 顺序消费 事务消费
  • RocketMq顺序消息和事务消息(四)

    千次阅读 2018-11-28 17:19:00
    1.顺序消息 2.事务消息 正文 顺序消息 有很多场景需要顺序消息,比如先买票-&amp;gt;再上车;淘宝买东西时,先下订单-&amp;gt;付款-&amp;gt;发货;等等 此刻我们有个要求 :1.每个订单的消息要有序 2.多个...
  • 青岛大学2006年硕士研究生复试通知

    千次阅读 2006-04-13 10:52:00
    青岛大学2006年硕士研究生复试通知注意:今年我校不再寄发书面复试通知书,在复试资格查询系统中具有复试资格的考生,请按照此通知的要求进行复试。根据国家教育部“关于做好2006年全国研究生录取工作的通知(征求...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 80,283
精华内容 32,113
关键字:

五大通知顺序