Spring AOP

简介

  AOP(Aspect Oriented Program)即面向切面编程,将程序抽象成各个切面。AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

相关术语

(1)通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
(2)连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
(3)切点(PointCut): 可以插入增强处理的连接点。切入点表达式,也就是组成@Pointcut注解的值,是正规的AspectJ 5切入点表达式
(4)切面(Aspect): 切面是通知和切点的结合。拥有@Aspect注解的bean都将被Spring自动识别并用于配置Spring AOP
(5)引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。使用@DeclareParents注解来定义引入
(6)织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。

通知类型

@Before 前置通知,在连接点方法前调用
@Around 环绕通知,它将覆盖原有方法,但是允许你通过反射调用原有方法
@After 后置通知,在连接点方法后调用
@AfterReturning 返回通知,在连接点方法执行并正常返回后调用,要求连接点方法在执行过程中没有发生异常
@AfterThrowing 异常通知,当连接点方法异常时调用

SpringBoot中利用AOP实现日志记录

  添加AOP的依赖

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

  接下来实现个日志切面,当调用com.example.demo.controller包下面的方法时,记录对应日志。

package com.example.demo.aspect;

import java.util.Arrays;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Aspect
@Component
public class WebLogAspect {

    private Logger logger =  LoggerFactory.getLogger(WebLogAspect.class);

    @Pointcut("execution(public * com.example.demo.controller..*.*(..))")
    public void webLog(){}

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        logger.info("URL : " + request.getRequestURL().toString());
        logger.info("请求方式 : " + request.getMethod());
        logger.info("IP : " + request.getRemoteAddr());
        logger.info("方法 : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("参数 : " + Arrays.toString(joinPoint.getArgs()));
    }

    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        logger.info("返回内容 : " + ret);
    }

}

  controller包下面编写了一个测试controller,下面有个download方法,接下来我们测试访问对应接口,可以看到日志输出:

2021-11-08 10:42:56.837  INFO 52764 --- [nio-8080-exec-1] com.example.demo.aspect.WebLogAspect     : URL : http://127.0.0.1:8080/download
2021-11-08 10:42:56.837  INFO 52764 --- [nio-8080-exec-1] com.example.demo.aspect.WebLogAspect     : 请求方式 : GET
2021-11-08 10:42:56.837  INFO 52764 --- [nio-8080-exec-1] com.example.demo.aspect.WebLogAspect     : IP : 127.0.0.1
2021-11-08 10:42:56.838  INFO 52764 --- [nio-8080-exec-1] com.example.demo.aspect.WebLogAspect     : 方法 : com.example.demo.controller.TestContoller.downloadExampleExcel
2021-11-08 10:42:56.838  INFO 52764 --- [nio-8080-exec-1] com.example.demo.aspect.WebLogAspect     : 参数 : [org.apache.catalina.connector.ResponseFacade@285a7d26]
2021-11-08 10:42:56.872  INFO 52764 --- [nio-8080-exec-1] com.example.demo.aspect.WebLogAspect     : 返回内容 : null

切面优先级

  有时我们实现了多个切面,这就牵涉到切面的执行顺序。解决方法是定义每个切面的优先级,@Order(i)注解来标识切面的优先级
  在切入点前的操作,按order的值由小到大执行
  在切入点后的操作,按order的值由大到小执行
  WebLogAspect.java 我们设置优先级为3,再定义两个切面Test1Aspect.java

package com.example.demo.aspect;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;


@Aspect
@Component
@Order(1)
public class Test1Aspect {

    private Logger logger =  LoggerFactory.getLogger(Test1Aspect.class);

    @Pointcut("execution(public * com.example.demo.controller..*.*(..))")
    public void test1(){}

    @Before("test1()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        logger.info("测试1 before方法执行......" );
    }

    @AfterReturning(returning = "ret", pointcut = "test1()")
    public void doAfterReturning(Object ret) throws Throwable {
        logger.info("测试1 after方法执行......");
    }

}

  Test2Aspect.java

package com.example.demo.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Order(2)
public class Test2Aspect {

    private Logger logger =  LoggerFactory.getLogger(Test2Aspect.class);

    @Pointcut("execution(public * com.example.demo.controller..*.*(..))")
    public void test2(){}

    @Before("test2()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        logger.info("测试2 before方法执行......" );
    }

    @AfterReturning(returning = "ret", pointcut = "test2()")
    public void doAfterReturning(Object ret) throws Throwable {
        logger.info("测试2 after方法执行......");
    }

}

  调用接口,测试结果跟预期一致

2021-11-08 11:05:08.065  INFO 55112 --- [nio-8080-exec-1] com.example.demo.aspect.Test1Aspect      : 测试1 before方法执行......
2021-11-08 11:05:08.066  INFO 55112 --- [nio-8080-exec-1] com.example.demo.aspect.Test2Aspect      : 测试2 before方法执行......
2021-11-08 11:05:08.066  INFO 55112 --- [nio-8080-exec-1] com.example.demo.aspect.WebLogAspect     : URL : http://127.0.0.1:8080/download
2021-11-08 11:05:08.066  INFO 55112 --- [nio-8080-exec-1] com.example.demo.aspect.WebLogAspect     : 请求方式 : GET
2021-11-08 11:05:08.066  INFO 55112 --- [nio-8080-exec-1] com.example.demo.aspect.WebLogAspect     : IP : 127.0.0.1
2021-11-08 11:05:08.068  INFO 55112 --- [nio-8080-exec-1] com.example.demo.aspect.WebLogAspect     : 方法 : com.example.demo.controller.TestContoller.downloadExampleExcel
2021-11-08 11:05:08.068  INFO 55112 --- [nio-8080-exec-1] com.example.demo.aspect.WebLogAspect     : 参数 : [org.apache.catalina.connector.ResponseFacade@52a06205]
2021-11-08 11:05:08.092  INFO 55112 --- [nio-8080-exec-1] com.example.demo.aspect.WebLogAspect     : 返回内容 : null
2021-11-08 11:05:08.092  INFO 55112 --- [nio-8080-exec-1] com.example.demo.aspect.Test2Aspect      : 测试2 after方法执行......
2021-11-08 11:05:08.092  INFO 55112 --- [nio-8080-exec-1] com.example.demo.aspect.Test1Aspect      : 测试1 after方法执行......
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值