Springboot引入AOP

AOP是Aspect Oriented Programming的缩写,意为面向切面编程。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是spring框架的一个重要内容,她通过对既有程序定义一个切入点(pointcut),然后在切入点前后切入不同的执行任务,常见使用场景有:打开/关闭数据库连接、打开/关闭事物、记录日志等等。基于AOP不会破坏原来的程序逻辑,因此她可以很好地对业务逻辑的各个不分进行抽离,从而使得业务逻辑各个部分的耦合度降低,提高程序的复用性,同时提高开发效率。

下面介绍下,aop的简单使用——统一处理web请求的日志:

1、在项目中引入AOP

引入方式同其他模块,在pom.xml中添加AOP依赖

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

在引入AOP模块之后,一般来讲,不用去做其他配置。spring.aop.auto属性默认是开启的,也就是说只要引入了AOP的依赖之后,默认已经增加了@EnableAspectJAutoProxy

2、创建一个简单的web请求处理

引入web模块

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

实现一个简单的web请求处理,见下图

img

3、实现web层的日志切面类:WebLogAspect

package com.example.demo.web;

import com.sun.istack.internal.logging.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import sun.nio.cs.ext.PCK;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalTime;
import java.util.Arrays;


@Aspect
@Component
public class WebLogAspect {
    private Logger logger = Logger.getLogger(getClass());

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

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        System.out.println("before : "+LocalTime.now());
        //接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        //记录请求内容
        logger.info("URL : "+request.getRequestURL().toString());logger.info("HTTP_METHOD : " + request.getMethod());
        logger.info("IP : " + request.getRemoteAddr());
        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));

    }

    @After("webLog()")
    public void doAfter() {
        logger.info("POSTPROCESSOR EXCUTED");

    }
    @AfterReturning(pointcut = "webLog()",returning = "result")
    public void doAfterReturning(Object result) {
        logger.info("RESPONSE:{}",result.toString());
    }
    @AfterThrowing(pointcut = "webLog()",throwing = "exception")
    public void doAfterThrowing(Exception exception) {
        logger.error("EXCEPTION:{}",exception);
    }

    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    	//方法要有返回,如果有多个参数ProceedingJoinPoint要放在第一个
        long start = System.currentTimeMillis();
        Object proceed = proceedingJoinPoint.proceed();
        long end = System.currentTimeMillis();
        logger.info("METHOD EXCUTED TIME:{}ms",end-start);
        return proceed;
    }
}

实现AOP的切面主要有一下几个因素:

1、使用@Aspect注解将一个Java类定义为切面类

2、使用@Pointcut定义一个切入点,规则表达式示例如下:

任意公共方法的执行:
    execution(public * (…))
  任何一个以“set”开始的方法的执行:
    execution(
set*(…))
  AccountService 接口的任意方法的执行:
    execution(* com.xyz.service.AccountService.(…))
  定义在service包里的任意方法的执行:
    execution(
com.xyz.service..(…))
  定义在service包和所有子包里的任意类的任意方法的执行:
    execution(* com.xyz.service….(…))
  定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行:
    execution(* com.test.spring.aop.pointcutexp…JoinPointObjP2.*(…))")

3、根据需要在切入点的不同位置切入指定内容

使⽤ @Before 在切⼊点开始处切⼊内容
    使⽤ @After 在切⼊点结尾处切⼊内容
    使⽤ @AfterReturning 在切⼊点return内容之后切⼊内容(可以⽤来对处理返回值做⼀些加⼯处理)
    使⽤ @Around 在切⼊点前后切⼊内容,并⾃⼰控制何时执⾏切⼊点⾃身的内容
    使⽤ @AfterThrowing ⽤来处理当切⼊内容部分抛出异常之后的处理逻辑

4、最后,请求localhost:8888/hello?name=Sam

img

控制台日志:

img

5、 多个切面的优先级问题

优化:AOP切⾯的优先级
由于通过AOP实现,程序得到了很好的解耦,但是也会带来⼀些问题,⽐如:我们可能会对Web层做
多个切⾯,校验⽤户,校验头信息等等,这个时候经常会碰到切⾯的处理顺序问题。
所以,我们需要定义每个切⾯的优先级,我们需要 @Order(i) 注解来标识切⾯的优先级。i的值越
⼩,优先级越⾼。假设我们还有⼀个切⾯是 CheckNameAspect ⽤来校验name必须为didi,我们为其设
置 @Order(10) ,⽽上⽂中WebLogAspect设置为 @Order(5) ,所以WebLogAspect有更⾼的优先
级,这个时候执⾏顺序是这样的:
  在 @Before 中优先执⾏ @Order(5) 的内容,再执⾏ @Order(10) 的内容
  在 @After 和 @AfterReturning 中优先执⾏ @Order(10) 的内容,再执⾏ @Order(5) 的内容

所以我们可以这样⼦总结:
  在切⼊点前的操作,按order的值由⼩到⼤执⾏
  在切⼊点后的操作,按order的值由⼤到⼩执⾏

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值