Spring Boot 配置 AOP

配置 AOP

1. AOP简介

要介绍面向切面编程(Aspect-Oriented Programming,AOP) ,需要首先考虑这样一个场景:公司有一个人力资源管理系统目前已经上线,但是系统运行不稳定,有时运行得很慢,为了检测出到底是哪个环节出问题了,开发人员想要监控每一个方法的执行时间,再根据这些执行时间判断出问题所在。当问题解决后,再把这些监控移除掉。系统目前已经运行,如果手动修改系统中成千上万个方法,那么工作量未免太大,而且这些监控方法以后还要移除掉;如果能够在系统运行过程中动态添加代码,就能很好地解决这个需求。这种在系统运行时动态添加代码的方式称为面向切面编程(AOP)。Spring框架对AOP提供了很好的支持。在AOP中,有一些常见的概念需要读者了解。

  • Joinpoint (连接点):类里面可以被增强的方法即为连接点。例如,想修改哪个方法的功能,那么该方法就是一个连接点。
  • Pointcut(切入点):对Joinpoint进行拦截的定义即为切入点。例如,拦截所有以insert 开始的方法,这个定义即为切入点。
  • Advice (通知):拦截到Joinpoint 之后所要做的事情就是通知。例如,上文说到的打印日志监控。通知分为前置通知、后置通知、异常通知、最终通知和环绕通知。
  • Aspect ( 切面): Pointcut 和Advice的结合。
  • Target (目标对象):要增强的类称为Target。

2. Spring Boot支持

Spring Boot 在Spring 的基础上对AOP的配置提供了自动化配置解决方案spring-boot-starter-aop,使开发者能够更加便捷地在Spring Boot项目中使用AOP。配置步骤如下。

首先在Spring Boot Web项目中引入spring- boot-starter-aop依赖,代码如下:

<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>

然后在org.sang.aop.service包下创建UserService类,代码如下:

@Service
public class UserService {
    public String getUserById(Integer id){
        System.out.println("get...");
        return "user";
    }
    public void deleteUserById(Integer id){
        System.out.println("delete...");
    }
}

接下来创建切面,代码如下:

@Component
@Aspect
public class LogAspect {
    @Pointcut("execution(* com.example.*.*(..))")
    public void pc1() {
    }

    @Before(value = "pc1()")
    public void before(JoinPoint jp) {
        String name = jp.getSignature().getName();
        System.out.println(name + "方法开始执行...");
    }

    @After(value = "pc1()")
    public void after(JoinPoint jp) {
        String name = jp.getSignature().getName();
        System.out.println(name + "方法执行结束...");
    }

    @AfterReturning(value = "pc1()", returning = "result")
    public void afterReturning(JoinPoint jp, Object result) {
        String name = jp.getSignature().getName();
        System.out.println(name + "方法返回值为: " + result);
    }

    @AfterThrowing(value = "pc1 ()", throwing = "e")
    public void afterThrowing(JoinPoint jp, Exception e) {
        String name = jp.getSignature().getName();
        System.out.println(name + "方法抛异常了,异常是: " + e.getMessage());
    }

    @Around("pc1()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        return pjp.proceed();
    }
}

代码解释:

  • @Aspect注解表明这是一个切面类。

  • 第4~6行定义的pcl方法使用了@Pointcut注解,这是一个切入点定义。execution 中的第一个 * 表示方法返回任意值,第二个 * 表示service 包下的任意类,第三个 * 表示类中的任意方法,括号中的两个点表示方法参数任意,即这里描述的切入点为service 包下所有类中的所有方法。

  • 第 8~12行定义的方法使用了@Before注解,表示这是一个前置通知,该方法在目标方法执行之前执行。通过JoinPoint参数可以获取目标方法的方法名、修饰符等信息。

  • 第14~18行定义的方法使用了@After注解,表示这是一个后置通知,该方法在目标方法执行之后执行。

  • 第 20~24行定义的方法使用了@AfterReturning注解,表示这是一个返回通知,在该方法中可以获取目标方法的返回值。@AfterReturmning 注解的returning参数是指返回值的变量名,对应方法的参数。注意,在方法参数中定义了result 的类型为Object,表示目标方法的返回值可以是任意类型,若result 参数的类型为Long,则该方法只能处理目标方法返回值为Long的情况。

  • 第26~30行定义的方法使用了@AfterThrowing注解,表示这是一个异常通知,即当目标方法发生异常时,该方法会被调用,异常类型为Exception 表示所有的异常都会进入该方法中执行,若异常类型为ArithmeticException,则表示只有目标方法抛出的ArithmeticException异常才会进入该方法中处理。

  • 第32~35行定义的方法使用了@Around注解,表示这是一一个环绕通知。环绕通知是所有通知里功能最为强大的通知,可以实现前置通知、后置通知、异常通知以及返回通知的功能。目标方法进入环绕通知后,通过调用ProceedingJoinPoint对象的proceed方法使目标方法继续执行,开发者可以在此修改目标方法的执行参数、返回值等,并且可以在此处理目标方法的异常。

配置完成后,接下来在Controller 中创建接口,分别调用UserService中的两个方法,即可看到LogAspect中的代码动态地嵌入目标方法中执行了。UserController 类的定义如下:

@RestController
public class UserController {
    @Autowired
    UserService userService;

    @GetMapping("/getUserById")
    public String getUserById(Integer id) {
        return userService.getUserById(id);
    }

    @GetMapping("/deleteUserById")
    public void deleteUserById(Integer id) {
        userService.deleteUserById(id);
    }
}

当访问/getUserById 接口时,打印日志,如图所示

getUserById方法开始执行...
get...
getUserById方法返回值为: user
getUserById方法执行结束...

你可能感兴趣的:(Spring,Boot,java)