SpringBoot中使用AOP

SpringBoot中使用AOP

AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。

对于我们开发中最常见的可能就是日志记录,事务处理,异常处理等等。。。

原理

    AOP是通过动态代理实现的,动态代理又分为两个部分:JDK动态代理和CGLIB动态代理,AOP功能的使用还是比较简单的,把相关bean注入到Spring容器中,编写好相应的Aspect类即可,以下两点需要记住:

1、AOP基于动态代理模式
2、AOP是方法级别

AOP术语

  • 切面(Aspect)
一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在Spring AOP中,切面可以使用基于模式或者基于@Aspect注解的方式来实现
  • 连接点(JoinPoint)

在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在SpringAOP中,一个连接点总是表示一个方法的执行

  • 通知(Advice)

在切面的某个特定的连接点上执行的动作。其中包括了AroundBeforeAfter等不同类型的通知。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链

注解描述
@Before 前置通知, 在方法执行之前执行
@After 后置通知, 在方法执行之后执行
@AfterReturn 返回通知, 在方法返回结果之后执行
@AfterThrowing 异常通知, 在方法抛出异常之后
@Around 环绕通知,围绕方法的执行
  • 切入点(PointCut)
匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时),切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法

常用切入点指示符

1、execution表达式:用于匹配方法执行的连接点,属于方法级别

语法:
execution(修饰符 返回值类型 方法名(参数)异常)

语法参数描述
修饰符 可选,如public,protected,写在返回值前,任意修饰符填*号就可以
返回值类型 必选,可以使用*来代表任意返回值
方法名 必选,可以用*来代表任意方法
参数 ()代表是没有参数,(..)代表是匹配任意数量,任意类型的参数,当然也可以指定类型的参数进行匹配,如要接受一个String类型的参数,则(java.lang.String), 任意数量的String类型参数:(java.lang.String..)等等。。。
异常 可选,语法:throws 异常,异常是完整带包名,可以是多个,用逗号分隔

符号

符号描述
* 匹配任意字符
.. 匹配多个包或者多个参数
+ 表示类及其子类

条件符

符号描述
&&、and
||
!

案例

  • 拦截com.dw.study包下的所有子包里的任意类的任意方法
    execution(* com.dw.study..*.*(..))
  • 拦截com.dw.study.Test2Controller下的任意方法
    execution(* com.dw.study.Test2Controller.*(..))
  • 拦截任何修饰符为public的方法
    execution(public * * (..))
  • 拦截com.dw.study下的所有子包里的以ok开头的方法
    execution(* com.dw.study..*.ok*(..))

2、@annotation

根据所应用的注解对方法进行过滤
语法
@annotation(注解全路径)
实例
对用了com.dw.annotations.Test注解的所有方法进行拦截
@annotation(com.dw.annotations.Test)
  • 目标对象(Target Object)
被一个或者多个切面所通知的对象。也被称做被通知对象。既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理对象
 
 
 

maven 依赖:

 <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-aop</artifactId>
         <version>2.2.2.RELEASE</version>
 </dependency>

使用:

@Aspect
@Component
public class MyCustomerAspect {

    /**
     * 定义切面,com.dengwei.springaopdemo.controller下面所有的方法进行拦截
     */
    @Pointcut("execution(public * com.dengwei.springaopdemo.controller.*.*(..))")
    public void myAspect(){}

    /**
     * 对于注解了 @MethodAspect的方法进行拦截
     */
    @Pointcut("@annotation(com.dengwei.springaopdemo.annotation.MethodAspect)")
    public void testMyAnnotationAspect() {}


    /**
     * 前置通知,在方法执行之前执行
     * @param joinPoint
     * @throws Throwable
     */
    @Before("myAspect()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        System.out.println(1 / 0);
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 记录下请求内容
        System.out.println("URL : " + request.getRequestURL().toString());
        System.out.println("HTTP_METHOD : " + request.getMethod());
        System.out.println("IP : " + request.getRemoteAddr());
        System.out.println("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        System.out.println("ARGS : " + Arrays.toString(joinPoint.getArgs()));
    }

    /**
     * 前置通知,在方法执行之前执行
     * @param joinPoint
     */
    @Before("testMyAnnotationAspect()")
    public void doBeforeForAnnotation(JoinPoint joinPoint) {
        // 接收到请求,记录请求内容
        System.out.println("我是注解切面,被成功执行了");
    }

    /**
     * 后置最终通知,final增强,不管是抛出异常或者正常退出都会执行
     * @param joinPoint 连接点
     */
    @After("myAspect()")
    public void doAfter(JoinPoint joinPoint){
        System.out.println("方法最后执行.....");
    }


    /**
     * 后置通知,方法返回结果后执行
     *returning属性指定连接点方法返回的结果放置在returnResult变量中
     * @param returnResult 返回结果
     * @param joinPoint 连接点
     */
    @AfterReturning(returning = "returnResult", pointcut = "myAspect()")
    public void doAfterReturning(JoinPoint joinPoint, Object  returnResult){
        // 处理完请求,返回内容
        System.out.println("方法的返回值 : " + returnResult);
    }

    /**
     * 后置通知,当方法抛出异常时执行
     * @param joinPoint 连接点
     * @param runtimeException 当切点方法抛出runtimeException时执行
     */
    @AfterThrowing(value = "myAspect()", throwing = "runtimeException")
    public void doThrowsIng(JoinPoint joinPoint, RuntimeException runtimeException){
        System.out.println("方法异常时执行了我.....");
    }


    /**
     * 环绕通知,环绕增强,相当于MethodInterceptor
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("myAspect()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("注解方式AOP拦截开始进入环绕通知.......");
        Object proceed = joinPoint.proceed();
        System.out.println("准备退出环绕......");
        return proceed;
    }


}

自定义注解:

/**
 * @Author dw
 * @ClassName MethodAspect
 * @Description 注解在方法上
 * @Date 2020/7/10 14:39
 * @Version 1.0
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodAspect {

}

controller:

@RestController
public class TestAopController {

    @RequestMapping("/first")
    public Object first() {
        return "first controller";
    }

    @RequestMapping("/doError")
    public Object error() {
        return "如果报错了,是否会打印我";
    }

    @RequestMapping("/annotationAspectTest")
    @MethodAspect
    public Object annotationAspectTest(){
        return "测试注解切面";
    }

}

 

原文地址:https://www.cnblogs.com/dw3306/p/9615197.html