• 【AOP】Spring AOP基础 + 实践 完整记录


    Spring AOP的基础概念

    =============================================================

     AOP(Aspect-Oriented Programming), 即 面向切面编程, 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角.
    在 OOP 中, 我们以类(class)作为我们的基本单元, 而 AOP 中的基本单元是 Aspect(切面)。

    ==============================================================

    基础概念图:

    有了上面这张原理图,那么关于AOP面向切面编程的核心几个概念,就可以顺利铺开了:

    1》join point【连接点】

    在spring aop中,认为原有代码中所有的方法都是join point。

    2》point cut【切点】

    在不改变原有代码的情况下,想多干点事情,那就需要定义point cut,切点。切点的任务是通过一组表达式来匹配要在哪个join point切入,并且匹配要在这个join point的什么位置切入。

    3》advice【增强逻辑】

    根据1,2切入了原有代码后,要做些什么事情?这多做的事情就是advice,也就是多处理的一些逻辑。比如,你的原有方法是对数据的保存方法,项目交付后,新需求是需要将这些保存操作在日志中记录下来,并且不能更改原有代码,这就是增强逻辑。

    advice增强逻辑你是准备放在原有代码之前还是之后,有以下几种:

    • before advice, 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)

    • after return advice, 在一个 join point 正常返回后执行的 advice

    • after throwing advice, 当一个 join point 抛出异常后执行的 advice

    • after(final) advice, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.

    • around advice, 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.

    4》weaving【织入】

    现在有了原有代码,有了新的增强逻辑。将这两部分代码连接在一起的过程,就是织入weaving。

    根据不同的实现技术, AOP织入有三种方式:

      • 编译器织入, 这要求有特殊的Java编译器.

      • 类装载期织入, 这需要有特殊的类装载器.

      • 动态代理织入, 在运行期为目标类添加增强(Advice)生成子类的方式.
        Spring 采用动态代理织入, 而AspectJ采用编译器织入和类装载期织入.

    5》Target【目标对象】

    原有代码和增强逻辑织入在一起,重新生成的就是目标对象Target,也叫adviced object.

    Spring Aop使用运行时代理的方式实现Aspect,因此adviced object是一个代理对象。

    在 Spring AOP 中, 一个 AOP 代理是一个 JDK 动态代理对象或 CGLIB 代理对象。】

    注意, adviced object 指的不是原来的类, 而是织入 advice 后所产生的代理类.

     【关于java中代理的概念,类型,区别和理解:

      https://www.cnblogs.com/hongcong/p/5806024.html

      https://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html

      可以去看上面两篇文章,我自己还没有心思去研究。

      但我记住了一句话:CGLIB方式不能代理final类。也就是说Spring AOP的切口不能切在final类上了

      】

    6》aspect【切面】

    aspect切面,是由point cut和advice组合而成的。既包含了连接点也就是切点的定义,也有增强逻辑的具体实现。

    ===========================================

    到这里,一个概念就顺利的出来了:Spring AOP就是负责实施切面的框架, 它将切面所定义的横切逻辑织入到切面所指定的连接点中.

    ===========================================

    Spring Aop的实现和使用的各种情况

    =======================================================

    要在Spring中通过注解方式使用AOP,需要下面两步:

    1》在配置文件中配置

       <!-- 自动扫描注解 -->
        <context:component-scan base-package="com.sxd" />
        <!--Spring aop 使用注解的方式-->
        <aop:aspectj-autoproxy />

    2》定义切面【aspect】

      切面包括 切点【point cut】 和  增强逻辑【advice】

      【下面aspect中已经显示了point cut的各种定义表达式   和  各种类的advice】

      【具体使用,应该按照实际使用逻辑选择性使用即可!!!】

    package com.sxd.aop;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    import java.util.Objects;
    
    
    /**
     * 切面定义
     * ①在类上定义@Aspect和@Component
     * ②@Pointcut()定义切点
     * ③adive方法上,声明在哪一个切点上切入 加入增强逻辑
     */
    @Aspect
    @Component
    public class Aspects {
    
        /**
         * execution()    表达式:用来匹配执行方法的连接点
         * 【..(两个点)代表零个或多个】
         * 【本例子中  第一个*代表方法的返回值类型可以为任何类型 如果本方法为void,也符合切入条件】
         * 【本例子中  第一个..代表controller包以及子包下】
         * 【本例子中  第二个*代表这个包下的任意类】
         * 【本例子中  第三个*代表这个包下的任意类中的任意方法】
         * 【本例子中  第二个..代表有无参数都可以被切入】
         *
         * args     如果想要切入的方法的参数要符合什么类型的话
         * 【本例子代表入参中,第一个参数类型需要为String类型的才会被切入,之后有零个入参或多个入参】
         *
         */
        @Pointcut(value = "execution(* com.sxd.controller..*.*(..)) && args(String,..)")
        public void pointCut1(){}
    
    
        /**
         * within()     表达式用于匹配这个包下的任意类
         */
        @Pointcut(value = "within(com.sxd.controller.*)")
        public void pointCut2(){}
    
        /**
         * this()       表达式限定了匹配这个类的实例下的方法
         */
        @Pointcut(value = "this(com.sxd.controller.MainController)")
        public void pointCut3(){}
    
        /**
         * bean()       匹配IOC容器中的bean的名称
         */
        @Pointcut(value = "bean(memberService)")
        public void pointCut4(){}
    
    
    
    
    
    
    
    
    
    
    
    
        /**
         * 下面是各种advice的展示
         *
         * 1》advice的执行优先级:  around方法执行前》before》【方法自己】》around方法执行后》after》afterReturning
         * 2》若有异常
         * advice的执行优先级:around方法执行前》before》【方法自己】》around方法执行后》after》afterReturning
         * 很奇怪为什么两次都是一样的执行优先级,为什么没有进afterThrowing().因为使用了around。
         * 3》注意:Spring AOP的环绕通知会影响到AfterThrowing通知的运行,不要同时使用!
         *
         * 4》注意:在切面的advice里面,一定不要让异常抛出去,影响原方法的执行和返回。
         * 5》JoinPoint 即原方法的入参
         * 6》returning即原方法的返回值
         * 7》如果原方法没有返回值,而这里的advice定义了returning,即使pointCut可以匹配上切点,也不会切入原方法
         */
    
    
        @Before("pointCut1()")
        public void justBefore(JoinPoint joinPoint){
            System.out.println("切入方法前");
        }
    
    
        @After("pointCut1()")
        public void justAfter(JoinPoint joinPoint){
           Object[] arr = joinPoint.getArgs();
           if(Objects.nonNull(arr) && arr.length >0){
               System.out.println((String)arr[0]);
               System.out.println((Integer)arr[1]);
           }
            System.out.println("切入方法后");
        }
    
        @AfterReturning(pointcut = "pointCut1()",returning = "returnVal")
        public  void justAfterReturn(JoinPoint joinPoint,Object returnVal){
            System.out.println(returnVal.toString());
            System.out.println("在方法执行完,并未抛异常,能正确返回值的情况下,在返回值之前切入");
        }
    
        @AfterThrowing(pointcut = "pointCut1()",throwing = "err")
        public  void justAfterThrow(JoinPoint joinPoint,Throwable err){
            System.out.println("在方法执行,抛异常的情况下,切入");
        }
    
        @Around("pointCut1()")
        public Object justAround(ProceedingJoinPoint proceedingJoinPoint){
            System.out.println("环绕型切入,方法执行前");
            Object a = null;
            try {
                 a =  proceedingJoinPoint.proceed();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            System.out.println("环绕型切入,方法执行后");
            return  a;
        }
    
    }

    3》上面两步 就把aop写完了 ,最后要测试一下各种情况

    package com.sxd.controller;
    
            import org.springframework.stereotype.Controller;
            import org.springframework.web.bind.annotation.RequestMapping;
            import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    public class MainController {
    
        @ResponseBody
        @RequestMapping("do")
        public String doSomething(){
            return "无参";
        }
    
        @ResponseBody
        @RequestMapping("do2")
        public String doSomething2(String a){
            return "有参"+a;
        }
    
        @ResponseBody
        @RequestMapping("do3")
        public String doSomething3(Integer a,String b){ return "整数"+a+"---字符串"+b; }
    
        @ResponseBody
        @RequestMapping("do4")
        public String doSomething4(String a,Integer b){
            return "字符串"+a+"---整数"+b;
        }
    
        @ResponseBody
        @RequestMapping("do5")
        public String doSomething5(String a,Integer b) throws Throwable{
            return "字符串转成数字"+Integer.parseInt(a)+"---整数"+b;
        }
    
        @ResponseBody
        @RequestMapping("do6")
        public void doSomething6(){
            System.out.println("无返回值的");
        }
    }
    View Code

    发请求  测试即可。

    ======================================================================

    补充:

      1》两个或多个位置定义切点

    方法1:

    @Pointcut(value = "(execution(* net.shopxx.xgn.controller.MemberCybController.cjtg(..))) or (execution(* net.shopxx.hunan.MemberCybController.cjtg(..)))")
        public void endbgDeal() {
        }

    方法2:

    @Pointcut(value = "execution(* net.shopxx.xgn.controller.MemberCybController.cjtg(..))")
        public void endbgDeal1(){
            
        }
        @Pointcut(value = "execution(* net.shopxx.hunan.MemberCybController.cjtg(..))")
        public void endbgDeal2(){
            
        }
        
        @Pointcut(value = "endbgDeal1() || endbgDeal2()")
        public void endbgDeal() {
        }
  • 相关阅读:
    学习opencv跟轮廓相关的
    opencv 连通域需要的函数解析
    【7.4】__new__和__init__的区别
    【7.3】属性描述符和属性查找过程
    【7.2】__getattr__、__getattribute__魔法函数
    【7.1】property动态属性
    【6.4】一个经典的参数错误
    【6.3】del语句和垃圾回收
    【6.2】==和is的区别
    【6.1】python中的变量是什么
  • 原文地址:https://www.cnblogs.com/sxdcgaq8080/p/7955851.html
Copyright © 2020-2023  润新知