前言
最近有这么个功能,由于使用了thymeleaf,多个页面遇到要使用同一个数据的问题,但是如果在每个Controller下都要向Model放数据,那么就有很多重复代码,不太优雅,所以想到了AOP,在进入方法前取到Model实例,向他增加数据即可。
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
doBefore中的代码也可以放入到@After下执行。
@Aspect
@Component
public class ResultAop {
@Pointcut("execution(public * com.he.edu.edu.controller.IndexController.*(..)))")
public void BrokerAspect(){
}
@Before("BrokerAspect()")
public void doBefore(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
if (arg instanceof Model){
Model model = (Model) arg;
model.addAttribute("test","存放公共参数");
}
}
}
@After("BrokerAspect()")
public void doAfter(JoinPoint joinPoint){
}
}
@Controller
public class IndexController {
Logger logger = LoggerFactory.getLogger(IndexController.class);
@GetMapping("test")
public String test(Model model){
logger.info("{}",model.getAttribute("test"));
return "test";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:text="${test}"></div>
</body>
</html>
这样,每个方法下的Model中都有了这份数据,取出渲染即可。
获取请求参数
我们可以通过RequestContextHolder获取url中的参数,RequestContextHolder就是一个典型的ThreadLocal应用,用于在当前线程中获取当前请求及其属性,如果要在service层中使用request,或者其他任何地方,都可以直接调用RequestContextHolder来获取request对象和response对象。
@Aspect
@Component
public class TestAop {
private Logger logger = LoggerFactory.getLogger(TestAop.class);
@Pointcut("execution(public * com.he.edu.edu.controller.IndexController.*(..)))")
public void BrokerAspect(){
}
@Before("BrokerAspect()")
public void doBefore(JoinPoint joinPoint){
ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
logger.info("URL参数={}",requestAttributes.getRequest().getQueryString());
}
}
@GetMapping("test")
public String test(Model model,@RequestParam("id") Integer id){
logger.info("id={}",id);
model.addAttribute("test",id);
return "test";
}
修改请求参数
可以利用@Around来修改请求参数,@Around功能非常强大,作用如下:
- 可以在目标方法之前增加逻辑,也可以在执行目标方法之后增加逻辑.
- 可以决定目标方法在什么时候执行,如何执行,也可以阻止目标目标方法执行.
- 可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值.
使用ProceedingJoinPoint时如果要改变参数,必须调用 proceed(Object[] var1)方法,传入新的参数数组,数组元素类型必须和目标方法相互对应,否则会报ClassCastException异常。
下面是将传入的id扩大十倍。
@Aspect
@Component
public class TestAop {
private Logger logger = LoggerFactory.getLogger(TestAop.class);
@Pointcut("execution(public * com.he.edu.edu.controller.IndexController.*(..)))")
public void BrokerAspect(){
}
@Around("BrokerAspect()")
public void doAround(ProceedingJoinPoint proceedingJoinPoint){
Object[] sourceObject =proceedingJoinPoint.getArgs();
sourceObject[1] =new Integer(((Integer) sourceObject[1])*10);
try {
proceedingJoinPoint.proceed(sourceObject);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
@GetMapping("test")
public String test(Model model,@RequestParam("id") Integer id){
logger.info("id={}",id);
model.addAttribute("test",id);
return "test";
}
修改返回值
proceed方法的返回值就是目标方法的返回值,我们可以拿到他做一些修改,或者返回新的数据。
下面加了ResponseBody注解,会将对象转换成json,将返回user1,user2,user3。
@GetMapping("listUser")
@ResponseBody
public List<String> listUser(){
return Stream.of("user1","user2","user3").collect(Collectors.toList());
}
在切入点进行修改,返回user4,user5,user6。
@Around("BrokerAspect()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint){
try {
Object proceed = proceedingJoinPoint.proceed();
return Stream.of("user4","user5","user6").collect(Collectors.toList());
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
打印带有指定注解方法的参数
首先自定义一个注解,最终将做到打印标有这个注解的请求参数。
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodLog {
}
@MethodLog
@GetMapping("log")
public String log(Model model,@RequestParam("id") Integer id){
model.addAttribute("test",id);
return "test";
}
@GetMapping("noLog")
public String noLog(Model model,@RequestParam("id") Integer id){
model.addAttribute("test",id);
return "test";
}
接下来取到目标方法,从方法中判断是否存有MethodLog注解,有的话就通过RequestContextHolder来获取请求参数。
@Before("BrokerAspect()")
public void doBefore(JoinPoint joinPoint) throws NoSuchMethodException {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature)signature;
Method targetMethod = methodSignature.getMethod();
if (((MethodSignature) joinPoint.getSignature()).getMethod().getDeclaredAnnotation(MethodLog.class)!=null){
ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
logger.info("URL参数={}",requestAttributes.getRequest().getQueryString());
}
}
访问不带注解方法时:
访问带有注解方法时: