Spring AOP中@Aspect拦截介绍(一)

本章介绍的@Aspect拦截用的非注解方式,而是通过切入点@Pointcut指定要拦截的目录,本章节实现了拦截请求和返参后对其进行修改,类似于过滤器的作用

1.第一步先先创建一个参数类用于传参测试

User.java

package com.example.springboot.cheng.domain;  //这行地址不需要复制

import lombok.Data;

@Data
public class User {

    private String name;

    private Integer age;

    private String email;

}

2.第二步在controller层创建一个测试类用于测试,对于返回类型AjaxResult这个是自己项目封装的 ,可以自由修改为String也可以

AspectTestController.java

package com.example.springboot.controller; //这行地址不需要复制

import com.example.springboot.cheng.domain.User;
import com.example.springboot.common.core.domain.AjaxResult;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/atc")
public class AspectTestController {

    /**
     * 用于测试拦截有参方法
     * @param user
     * @return
     */
    @PostMapping
    public AjaxResult achieve(@RequestBody User user){
        System.out.println("方法执行-----------");
        return AjaxResult.success(user);
    }

    /**
     * 用于测试拦截无参的方法
     * @return
     */
    @GetMapping
    public AjaxResult achieve(){
        System.out.println("方法执行-----------");
        return AjaxResult.success();
    }

}

3.最后一步,创建一个拦截器

TestAspect.java

package com.example.springboot.common.aspectj; //这行地址不需要复制

import com.example.springboot.cheng.domain.User;
import com.example.springboot.common.core.domain.AjaxResult;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
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;

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

/**
 * @description 使用该切面拦截时,有两种方式,选择其中一种注掉另一种即可,
 * 建议使用第二种,因为可以拦截后修改传入参数:
 * 1.@Before+@After
 * 2.@Around
 */
@Aspect
@Component
public class TestAspect {

    private static final Logger logger = LoggerFactory.getLogger(TestAspect.class);

    ThreadLocal<Long> startTime = new ThreadLocal<>();

    /**
     * 第一个*代表返回类型不限
     * 第二个*代表所有类
     * 第三个*代表所有方法
     * (..) 代表参数不限
     * com.example.springboot.controller 测试的controller层
     */
    @Pointcut("execution(public * com.example.springboot.controller.*.*(..))")
    public void pointCut(){};


    //@Before(value = "pointCut()")
    public void before(JoinPoint joinPoint){
        System.out.println("方法执行前执行......before");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        logger.info("<=====================================================");
        logger.info("请求来源: =》" + request.getRemoteAddr());
        logger.info("请求URL:" + request.getRequestURL().toString());
        logger.info("请求方式:" + request.getMethod());
        logger.info("响应方法:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("请求参数:" + Arrays.toString(joinPoint.getArgs()));
        logger.info("连接点的方法签名对象:"+joinPoint.getSignature());
        logger.info("连接点所在的目标对象:"+joinPoint.getTarget());
        logger.info("代理对象:"+joinPoint. getThis());
        logger.info("------------------------------------------------------");
        startTime.set(System.currentTimeMillis());
    }

    // 定义需要匹配的切点表达式,同时需要匹配参数
    /**
     * @description 要拦截修改参数的值只有使用这个方法,Around相当于before+after
     * @param pjp
     * @param arg 类型可以根据pointCut指定切点类下的方法确定,也可以使用统一的Object,也可以不写参数
     * @return
     * @throws Throwable
     */
    @Around("pointCut() && args(arg)")
    public Object around(ProceedingJoinPoint pjp, Object arg) throws Throwable{
        logger.info("入参:{}",arg);
        logger.info("方法环绕start...around");

        //修改入参,让传入年龄参数减去5
        User user = (User)pjp.getArgs()[0];
        user.setAge(user.getAge()-5);
        Object[] objects = new Object[]{user};

        Object object = pjp.proceed(objects);
        logger.info("方法环绕end...around");
        logger.info("出参:{}",object.toString());
        return object;
    }

    //@After("within(com.example.springboot.controller.*)")
    public void after(){
        System.out.println("方法之后执行...after.");
    }

    /**
     *
     * @param AjaxResult  rst 该参数类型需要与测试的Controller层的返回值类型一致,否则不生效,也就是找不到
     *            该测试中的AjaxResult是测试项目中封装好的出参
     */
    //@AfterReturning(pointcut="pointCut()",returning = "rst")
    public void afterRunning(AjaxResult rst){
        if(startTime.get() == null){
            startTime.set(System.currentTimeMillis());
        }
        System.out.println("方法执行完执行...afterRunning");
        logger.info("耗时(毫秒):" +  (System.currentTimeMillis() - startTime.get()));
        logger.info("返回数据:{}", rst);
        logger.info("==========================================>");
    }

    @AfterThrowing("within(com.example.springboot.controller.*)")
    public void afterThrowing(){
        System.out.println("异常出现之后...afterThrowing");
    }


}

4.注意

4.1 关于第二点中的返回参数类型AjaxResult

如果想要学习或者加上该封装结构,可以在笔者的项目笔记目录下寻找“项目返参结构封装”就可以直接复制了,当然也可以自己返回一个String或者Object类型先测试用着

4.2 本测试中注掉的注解

本测试例子中已经把 @Before和@After和@AfterReturning注掉了,只使用的@Around,可以直接用postman调用测试的,测完还可以放开前面三个注解,再把@Around注掉再测就可以了。

4.2 @Around测试无参方法的问题

无参的那个测试不能用@Around测试,想要用@Around测试需要改一下,@Around("pointCut() && args(arg)")改为@Around("pointCut() "),下面的入参和修改参数部分去掉即可

5.测试

拦截测试1.png
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容