前言
大家好,一直以来我都本着 用最通俗的话理解核心的知识点, 我认为所有的难点都离不开 基础知识 的铺垫
适合人群
- 学完Java基础
- 想通过Java快速构建web应用程序
- 想学习或了解SpringBoot
大佬可以绕过 ~
背景
如果你是一路看过来的,很高兴你能够耐心看完。之前带大家学了Springboot
基础部分,对基本的使用有了初步的认识, 接下来的几期内容将会带大家进阶使用,会先讲解基础中间件
的使用和一些场景的应用,或许这些技术你听说过,没看过也没关系,我会带大家一步一步的入门,耐心看完你一定会有收获
~
情景回顾
上期带大家学习了日志
的集成, 本期将带大家学习SpringBoot
中如何进行请求拦截
,同样的,我们集成到Springboot
中。最近github可能会被墙,所以我把源码放到了国内gitee上,本节我们依然使用上期的代码
项目源码(持续更新⭐️)
为什么要进行请求拦截
首先大家可以想一下这个问题,我们为什么要去拦截?当你想清楚了这个问题后,你就知道用它要做啥了。
比如我想知道前端传过来的参数是啥,或者我返回给它啥结果,那么你可以去拦截请求和请求响应结果, 这就是所谓记录请求日志
。再比如,我想要对所以请求进行验证,比如签名验证
,通过某个签名参数去验证请求是否通过。其实请求拦截不止在服务端,其实前端也有这样的拦截,比如前端拦截请求发生前
,对参数统一加签名
,统一加请求头,对响应结果
做拦截处理,未登录
跳登录页等等,都是统一处理。
这样的好处是什么呢?这样对代码的维护而言比较友好,减少了很多繁琐的代码,易扩展
拦截请求日志
我们以记录请求日志为例,带大家熟悉一下如何去做拦截。首先我们知道,请求过来的时候,首先最先进入的就是我们的控制器层,所以我们很清楚拦截的地方在Controller
,所以我们只要在它进入方法之前做处理就好了。
我们回顾一下,怎么样可以拦截一个方法呢?我们可以通过反射
的机制去拦截,但是我们有更好的方式,我们可以利用Spring
提供的aop
去拦截。好,知道实现的方式了,我们现在就去实现它,如果你还不知道aop
和反射
可以阅读我的这篇文章,你一定会有所收获~
这是一篇长文,可能需要花点时间~
实现 WebLog
修改pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--Hutool Java工具包--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>4.5.7</version> </dependency> 复制代码
lombok
这个小工具之前没给大家介绍,它用起来很方便,帮我们省略了写getter和setter
方法, 举个例子
@Data public class WebLog { /** * 请求参数 */ private Object params; /** * URI */ private String uri; /** * URL */ private String url; /** * 请求类型 */ private String method; /** * 操作时间 */ private Long startTime; /** * 消耗时间 */ private Integer spendTime; /** * 根路径 */ private String basePath; /** * 请求返回的结果 */ private Object response; /** * IP地址 */ private String ip; } 复制代码
可以试试实例化后,它会自动的出现了get和set
方法
获取请求对象
首先获取当前请求对象。通过如下方法获取:
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); 复制代码
这样我们就得到了Request
对象,有没有发现,这个跟我们之前在讲Controller
的时候提到的请求对象是一样的
那怎么获取请求参数呢?我们知道参数都写在控制器的方法参数上,所以通过反射机制可以拿到:
MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); 复制代码
这是获取方法对象,在aop
中,我们可以通过切点获取参数
joinPoint.getArgs() 复制代码
然后对它进行遍历即可,响应结果可以通过执行结果获取
Object result = joinPoint.proceed() 复制代码
完整案例
这里给大家展示完整案例,不然看起来有点懵逼
@Aspect @Component @Order(1) public class WebLogFilter { private static final Logger LOGGER = LoggerFactory.getLogger(WebLogFilter.class); @Pointcut("execution(public * com.example.app.controller.*.*(..))") public void webLog() { } @Before("webLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { } @AfterReturning(value = "webLog()", returning = "ret") public void doAfterReturning(Object ret) throws Throwable { } @Around("webLog()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.currentTimeMillis(); //获取当前请求对象 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); //记录请求信息 WebLog webLog = new WebLog(); Object result = joinPoint.proceed(); Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); long endTime = System.currentTimeMillis(); String urlStr = request.getRequestURL().toString(); webLog.setBasePath(StrUtil.removeSuffix(urlStr, URLUtil.url(urlStr).getPath())); webLog.setIp(getIp(request)); webLog.setMethod(request.getMethod()); webLog.setParams(getParameter(method, joinPoint.getArgs())); webLog.setResponse(result); webLog.setSpendTime((int) (endTime - startTime)); webLog.setStartTime(startTime); webLog.setUri(request.getRequestURI()); webLog.setUrl(request.getRequestURL().toString()); LOGGER.info("{}", JSONUtil.parse(webLog)); return result; } /** * 根据方法和传入的参数获取请求参数 */ private Object getParameter(Method method, Object[] args) { List<Object> argList = new ArrayList<>(); Parameter[] parameters = method.getParameters(); for (int i = 0; i < parameters.length; i++) { //将RequestBody注解修饰的参数作为请求参数 RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class); if (requestBody != null) { argList.add(args[i]); } //将RequestParam注解修饰的参数作为请求参数 RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class); if (requestParam != null) { Map<String, Object> map = new HashMap<>(); String key = parameters[i].getName(); if (!StringUtils.isEmpty(requestParam.value())) { key = requestParam.value(); } map.put(key, args[i]); argList.add(map); } } if (argList.size() == 0) { return null; } else if (argList.size() == 1) { return argList.get(0); } else { return argList; } } public static String getIp(HttpServletRequest req){ String ip = req.getHeader("Origin"); if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = req.getHeader("x-forwarded-for"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = req.getHeader("Proxy-Client-IP"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = req.getHeader("X-Real-IP"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = req.getRemoteAddr(); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ ip = "127.0.0.1"; } String[] ips = ip.split(","); LOGGER.info("抓取的ip为:{}",ips[0]); return ips[0].trim(); } } 复制代码
这样我们就把请求的信息发放到WebLog
对象里了,后期如果你想存到数据库,就可以直接塞了,可以做成报表,你也可以记录到log
文件里,在开发的时候,控制台也方便调试,是不是很nice
,甚至到了系统业务慢慢增多的时候,你可以把它收集到专门的日志系统里。
结束语
本期到这里就结束了,总结一下,本节主要讲了如何去通过aop拦截请求
,做了一个请求日志的小功能,大家可以自行试一下。也可以举一反三,不一定拦截请求,你也可以拦截Service
做一些事情,或是自定义的注解
,反正能做的事情很多,工具是死的,人是活的,所以多去思考,换做是你,有什么好的方式可以提高开发效率和系统健壮
下期预告
之前我们讲mybatis
的时候,其实还有一个比较重要的知识点没讲,就是我们的事务处理
, 下期将会带大家学习一下,没听过没关系, 我会一步步从入门讲起, 涉及的理论可能较多。
下期见, 关注我,不迷路~