• Spring系列.AOP使用


    AOP简介

    利用面向对象的方法可以很好的组织代码,也可以继承的方式实现代码重用。但是项目中总是会出现一些重复的代码,并且不太方便使用继承的方式把他们重用管理起来,比如说通用日志打印,事务处理和安全检查等。我们可以将这些代码封装起来,做成通用模块,但是还是需要在代码中每处需要的地方进行显示调用,使用起来不方便。这是时候就是利用AOP的时候。

    AOP是一种编程范式,用来解决特定的问题,不能解决所有问题,可以看做是OOP的补充,常见的编程范式还有:

    • 面向过程编程;
    • 面向对象编程;
    • 面向函数编程(函数式编程);
    • 事件驱动编程(GUI开发中比较常见);
    • 面向切面编程

    AOP的常见使用场景

    • 性能监控,在方法调用前后记录调用时间,方法执行太长或超时报警;
    • 缓存代理,缓存某方法的返回值,下次执行该方法时,直接从缓存里获取;
    • 软件破解,使用AOP修改软件的验证类的判断逻辑;
    • 记录日志,在方法执行前后记录系统日志;
    • 工作流系统,工作流系统需要将业务代码和流程引擎代码混合在一起执行,那么我们可以使用AOP将其分离,并动态挂接业务;
    • 权限验证,方法执行前验证是否有权限执行当前方法,没有则抛出没有权限执行异常,由业务代码捕捉;
    • 事务处理 。

    Spring AOP相关概念

    • AOP:这种在运行时(或者编译时或者加载时),动态地将某些公共代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程;
    • 切面(Aspect):A modularization of a concern that cuts across multiple classes。在Spring中切面就是一个标注@AspectJ的类,不要想得太复杂;
    • 连接点(Joinpoint):方法执行过程中的某个点,是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为;
    • 通知(advice):描述切面要完成什么工作,以及在什么时间点进行工作;
    • Pointcut:用来匹配一组连接点,并且pointcut会关联advice,在pointcut匹配的连接点执行的时候,advice代码会被执行;
    • Introduction
    • Target object:被织入切面的对象;
    • AOP proxy : 包装了切面代码和target代码的对象,Spring中支持JDK动态代理和
      CGLIB,默认使用JDK动态代理,但是如果被代理的类没有实现接口,或者用户强制使用CGLIB,那么Spring会使用CGLIB代理;
    • Weaving:将切面代码添加到目标代码的过程,织入的类型有编译时织入,加载时织入和运行时织入(Spring是运行时织入)

    SpringAOP可以应用5种类型的通知:

    • 前置通知(Before):在目标方法被调用之前调用通知功能。
    • 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么。(不管执行是否成功都执行都执行)
    • 返回通知(After-returning):在目标方法成功执行之后调用通知。
    • 异常通知(After-throwing):在目标方法抛出异常后调用通知。
    • 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

    Spring AOP相关

    开启Aop

    
    //自动选择合适的AOP代理
    //传统xml这样配置:<aop:aspectj-autoproxy/>
    
    //exposeProxy = true属性设置成true,意思是将动态生成的代理类expose到AopContext的ThreadLocal线程
    //可以通过AopContext.currentProxy();获取到生成的动态代理类。
    
    //proxyTargetClass属性设置动态代理使用JDK动态代理还是使用CGlib代理,设置成true是使用CGlib代理,false的话是使用JDK动态代理
    
    //注意:如果使用Spring Boot的话,下面的配置可以不需要。AopAutoConfiguration这个自动配置类中已经自动开启了AOP
    //默认使用CGLIB动态代理,Spring Boot配置的优先级高于下面的配置
    
    @Configuration
    @EnableAspectJAutoProxy(exposeProxy = true,proxyTargetClass = false)
    public class AopConfig {
    
    }
    
    
    

    如果使用传统的配置方式的话,可按如下配置开启AOP功能。

    <aop:aspectj-autoproxy/>
    

    定义一个Aspect

    Aspects (classes annotated with @Aspect) can have methods and fields, the same as any other class. They can also contain pointcut, advice, and introduction (inter-type) declarations.

    可以使用普通Bean的定义方式,或者加@Aspect注解的方式定义。一旦一个类被标注成切面类,它就不会成为其他切面的代理对象。

    定义一个PointCut

    切面表达式可以由指示器,通配符和运算符组成

    1. 指示器(Designators)
    • 匹配方法 execution() (重点掌握...)
    • 匹配注解 @target() @args() @within() @annotation()
    • 匹配包/类型 within()
    • 匹配对象 this() bean() target()
    • 匹配参数 args()
    1. Wildcards(通配符)
    • *匹配任意数量的字符
    • +匹配指定类及其子类
    • .. 一般用于匹配任意参数的子包或参数
    1. Operators(运算符)
    • && 与操作符
    • || 或操作符
    • ! 非操作符

    下面给出一个定义PointCut的例子

    package com.csx.demo.spring.boot.aspect;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    @Component
    @Aspect
    public class MyAspect {
    
        //PointCut匹配的方法必须是Spring中bean的方法
        //Pointcut可以有下列方式来定义或者通过&& || 和!的方式进行组合.
        //下面定义的这些切入点就可以通过&& ||组合
    
        private static Logger logger = LoggerFactory.getLogger(MyAspect.class);
    
        //*:代表方法的返回值可以是任何类型
        //整个表达式匹配controller包下面任何的的echo方法,方法入参乐意是任意
        @Pointcut("execution(* com.csx.demo.spring.boot.controller.*.echo(..))")
        public void pointCut1(){}
    
        //代表echo方法必须有一个参数 参数的类型可以是任意类型
        @Pointcut("execution(* com.csx.demo.spring.boot.controller.*.echo(*))")
        public  void pointCut2(){}
    
        //代表echo方法必须有两个参数,第一个类型任意,第二个类型必须是String
        @Pointcut("execution(* com.csx.demo.spring.boot.controller.*.echo(*,String))")
        public void pointCut3(){}
    
        //contrller包及其子包下面的任意类的任意方法
        //需要注意的是with和@with都是正对包级别的
        @Pointcut("within(com.csx.demo.spring.boot.controller..*)")
        public void pointCut4(){}
    
        //使用RestController这个注解标注任意类的任意方法
        @Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
        public void pointCut5(){}
    
        //用法和@Within类似
        @Pointcut("@target(org.springframework.web.bind.annotation.RestController)")
        public void pointCut10(){}
    
        //MyService这个接口实现类的任何方法
        //如果MyService是一个类的话,那匹配这个类内部的所有方法
        @Pointcut("this(com.csx.demo.spring.boot.service.MyService)")
        public void pointCut6(){}
    
        @Pointcut("this(com.csx.demo.spring.boot.service.MyServiceImpl)")
        public void pointCut7(){}
    
        //某个bean内部的所有方法
        @Pointcut("bean(myServiceImpl)")
        public void pointCut8(){}
    
        //@within和@target针对类的注解,@annotation是针对方法的注解
        //匹配任何标注GetMaping注解的方法
        @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
        public void pointCut9(){}
    
        //匹配只有一个参数,参数类型是String的方法
        @Pointcut("args(String)")
        public void pointCut11(){}
    
    
        @Before("pointCut1()")
        public void befor(){
            logger.info("前置通知vvvv...");
            logger.info("我要做些事情...");
        }
    
        @After("pointCut1()")
        public void after(){
            logger.info("后置通知");
        }
    
        @AfterReturning("pointCut1()")
        public void afterReturn(){
           logger.info("后置返回");
        }
    
         //目标方法抛出相关异常后通知
        @AfterThrowing("pointCut1()")
        public void afterThrowing(){
            logger.info("后置异常");
        }
    
        @Around("pointCut1()")
        public void around(ProceedingJoinPoint point) throws Throwable {
            logger.info("环绕通知...");
            logger.info("我要做些事情...");
            point.proceed();
            logger.info("结束环绕通知");
        }
    
    }
    
  • 相关阅读:
    [BZOJ3172]单词
    [BZOJ2434]阿狸的打字机
    [BZOJ1195]最短母串
    [codeforces743E]Vladik and cards
    [BZOJ2553]禁忌
    [BZOJ1009]GT考试
    [BZOJ3507]通配符匹配
    [BZOJ4027]兔子与樱花
    test20190308
    Luogu P2742 模板-二维凸包
  • 原文地址:https://www.cnblogs.com/54chensongxia/p/13139671.html
Copyright © 2020-2023  润新知