Springboot系列文章(五):AOP处理

Springboot系列文章(五):AOP处理

现在遇到一个这样的情况。我们想在某个方法之后执行一些操作。或者某个方法之前执行一些操作。我们以前的操作,可能是在前面加代码就可以了。但这样如果有多处地方需要,那么我们就需要copy多次,如果后期移除,还要多个地方移除,比较麻烦。

AOP就可以轻松帮助我们解决这个问题。达到一种可配置的、可插拔的程序结构。

1. 快速入门

1.1 所需依赖

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

比如我们现在实现一个:调用方法前后,都打印一下请求信息,以及执行后的一些信息。不可能每一个方法都增加,所以我们运用aop

1.2 创建切面类

  1. 创建一个切面类,用于将请求的日志信息切入进去。

Spring Boot 中使用 AOP 非常简单,只要在类上加个@Aspect注解即可。@Aspect 注解用来描述一个切面类,@Component 注解让该类交给 Spring 来管理。


@Aspect
@Component
@Slf4j
public class LogInterceptor {
	....
}

请求信息

@Aspect
@Component
@Slf4j
public class LogInterceptor {

    public Object doInterceptor(ProceedingJoinPoint point) throws Throwable {
        // 计时
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 获取请求路径
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();
        // 生成请求唯一 id
        String requestId = UUID.randomUUID().toString();
        String url = httpServletRequest.getRequestURI();
        // 获取请求参数
        Object[] args = point.getArgs();
        String reqParam = "[" + StringUtils.join(args, ", ") + "]";
        // 输出请求日志
        log.info("request start,id: {}, path: {}, ip: {}, params: {}", requestId, url,
                httpServletRequest.getRemoteHost(), reqParam);
        // 执行原方法
        Object result = point.proceed();
        // 输出响应日志
        stopWatch.stop();
        long totalTimeMillis = stopWatch.getTotalTimeMillis();
        log.info("request end, id: {}, cost: {}ms", requestId, totalTimeMillis);
        return result;
    }
}

1.3 注解详解

2.但我们应该如何运用呢?

这里主要介绍几个常用的注解及使用:

@Pointcut: 定义切入点,供@Before或其他注解去使用
@Before : 目标方法执行之前执行
@After :目标方法执行之后必定执行,无论是否报错
@AfterReturning :目标方法有返回值且正常返回后执行
@AfterThrowing :目标方法抛出异常后执行
@Around :可以获取到目标方法的入参和返回值

1.通过execution表达式来给1个类或某些类定义切入点。

以execution(* com.zwl.project.controller. .*.*(. .))表达式为例,语法如下:

execution() 为表达式主体
第一个 * 号的位置:表示返回值类型,* 表示所有类型
包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.zwl.project.controller 包、子包下所有类的方法
第二个 * 号的位置:表示类名
*(…) :这个星号表示方法名,* 表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数


	/**
     * 执行拦截
     */
    @Around("execution(* com.zwl.project.controller.*.*(..))")
    public Object doInterceptor(ProceedingJoinPoint point) throws Throwable {
        // 计时
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 获取请求路径
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();
        // 生成请求唯一 id
        String requestId = UUID.randomUUID().toString();
        String url = httpServletRequest.getRequestURI();
        // 获取请求参数
        Object[] args = point.getArgs();
        String reqParam = "[" + StringUtils.join(args, ", ") + "]";
        // 输出请求日志
        log.info("request start,id: {}, path: {}, ip: {}, params: {}", requestId, url,
                httpServletRequest.getRemoteHost(), reqParam);
        // 执行原方法
        Object result = point.proceed();
        // 输出响应日志
        stopWatch.stop();
        long totalTimeMillis = stopWatch.getTotalTimeMillis();
        log.info("request end, id: {}, cost: {}ms", requestId, totalTimeMillis);
        return result;
    }
  1. 除了execution,还可以使用annotation,对使用到的注解进行切入。
	@Around("@annotation(authCheck)")
    public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable {
    }

表示,使用到这个注解,则会被切入执行。

3.还有这种写法

    @Pointcut("execution(* com.itcodai.course09.controller..*.*(..))")
    public void pointCut() {}

 @After("pointCut()")
    public void doAfter(JoinPoint joinPoint) {
 
        logger.info("====doAfter方法进入了====");
        Signature signature = joinPoint.getSignature();
        String method = signature.getName();
        logger.info("方法{}已经执行完", method);
    }

4. JointPoint

JointPoint对象包含了切入相关的很多信息。比如切入点的对象,方法,属性等。我们可以通过反射的方式获取这些点的状态和信息,用于追踪tracing和记录logging应用信息。

通过JpointPoint对象可以获取到下面信息

# 返回目标对象,即被代理的对象
Object getTarget();
# 返回切入点的参数
Object[] getArgs();
# 返回切入点的Signature
Signature getSignature();
# 返回切入的类型
 String getKind();

Proceedingjoinpoint 继承了 JoinPoint。是在JoinPoint的基础上暴露出 proceed 这个方法。proceed很重要,这个是aop代理链执行的方法。

1.暴露出这个方法,就能支持 around 这种切面
2.而其他的几种切面只能用到JoinPoint,这也是环绕通知和前置、后置通知方法的一个最大区别。
3.能决定是否走代理链还是走自己拦截的其他逻辑

point.proceed();//执行原方法,也表示放行。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值