本章介绍的@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