1.Aop maven 仓库路径,这里使用的spring boot 是2.4.5所有aop对应的版本是也是2.4.5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.编写Aop切入类指向接口所在的类。
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.poi.util.IOUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Aspect
@Component
public class RequestAspect {
private final Logger logger = LoggerFactory.getLogger(RequestAspect.class);
//切入点描述 这个是controller包的切入点
@Pointcut("execution(public * com.dh.test.controller..*.*(..))")
public void controllerLog() {
}
//在切入点的方法run之前要干的
@Before("controllerLog()")
public void logBeforeController(JoinPoint joinPoint) throws IOException {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
logger.info("请求的URL : " + request.getRequestURL().toString());
Enumeration<String> headerNames = request.getHeaderNames();
Map<String, String> map = new HashMap<>();
if (headerNames.hasMoreElements()) {
String headName = headerNames.nextElement();
map.put(headName, request.getHeader(headName));
}
logger.info("获取到头部信息为:" + map);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
IOUtils.copy(request.getInputStream(), outputStream);
logger.info("请求的body参数为:" + new String(outputStream.toByteArray()));
}
}
3.编写一个controller,UserVo创建一个接受参数
@RestController
public class TestController {
@RequestMapping("test.get")
public List<User> getUser(@RequestBody UserVo userVo) {
return new ArrayList<>();
}
}
public class UserVo {
//测试 可以什么都不放
}
4.启动项目使用postman测试接口,但是报错了。java.io.IOException: Stream closed,这里报错是因为sevlert里面的body是流为inputStream读一次就结束了。ResponseBody是读一次,自定义的aop再通过sevlert去读的时候就已经关闭了无法读取。
5.解决不能重复读的问题,需要重写servlet方法的getInputStream和getReader方法因为在后面使用的时候不知道具体使用那个防止出错两个都重写。
import org.apache.poi.util.IOUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
public class RequestWarp extends HttpServletRequestWrapper {
// 定义局部变量保存流,每次读取都没有问题
private byte[] body;
private final HttpServletRequest request;
public RequestWarp(HttpServletRequest request) {
super(request);
this.request = request;
// 创建的时候 就把值设置好
this.getInputStream();
}
@Override
public ServletInputStream getInputStream() {
// 判断 防止重复执行
if (null == this.body) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
IOUtils.copy(request.getInputStream(), outputStream);
} catch (IOException e) {
e.printStackTrace();
}
this.body = outputStream.toByteArray();
}
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() {
return byteArrayInputStream.read();
}
};
}
/**
* 这个方法也是必须重写的 具体使用的时候 可能调用这个
*/
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
6.重写之后就是使他生效,需要借助过滤取将重写之后的方法生效。
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")
public class RequestFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
RequestWarp requestWarp = new RequestWarp(request);
// 这里一定是要用 修改后request 不然还是使用原始的request 无法获取两次
filterChain.doFilter(requestWarp, servletResponse);
}
}
7.最后不要忘记了Spring boot启动类加上过滤器的扫描,不然不会生效。
@ServletComponentScan(basePackages = "com.dh.test")
8.再次用postman请求,请求正常,然后看到后台日志也是正常打印了。
?
9.到此已经能aop能正常切入controller里面了,可能在想用过滤器为啥用这个,只是针对当前的使用是用过滤器就足够了。但是深入获取当前方法名,参数什么的在这个基础上只需要通过aop JoinPoint就可以快速的扩展了
|