需求
公司的项目中controller是使用json字符串作为入参,这样做对于开发来说十分方便,参数的内容前后端是解耦的,当然使用map也可以起到同样的效果,但是json总归用起来顺手一些。因为公司是自己定义了一套注解而没有使用spring,对于个人来说工程量太大,因此决定直接在spring基础上进行改造。
思路
其实这里实现起来并不复杂,可以使用aop的知识,在请求进入controller的方法之前对请求进行拦截,提取其中的json参数部分,然后填充到自己设置的入参中即可。
实现
首先在springboot项目中引入aop依赖,引入这个依赖之后默认就会开启aop
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
然后在controller中定义自己压迫的json入参,这里以登录操作为例(login方法中的json参数就是我们前端传进来的入参,由于登录操作设计生成并返回token,所以这个接了个response参数,可以忽略,重点在于如何填充json参数):
package com.sunlong.jz.controller;
import com.alibaba.fastjson.JSONObject;
import com.sunlong.jz.common.JzResponse;
import com.sunlong.jz.service.IJzLoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
/**
*
*
* @author SL
* @description: 登录
* @date 2021/11/11 15:46
**/
@RestController
@RequestMapping("/login")
public class JZLoginController {
@Autowired
private IJzLoginService loginService;
/**
* 登录验证 login
*
* @return LoginInfo 用户信息类(包含用户的ID,账号和密码和生成token字符串信息)
*/
@PostMapping
public JzResponse login(String json, HttpServletResponse response) {
JSONObject jsonObject = JSONObject.parseObject(json);
return loginService.login(jsonObject,response);
}
}
此时如果直接运行,json的值只会是null,因为spring无法识别这个参数,此时就要使用aop自己去实现注入,我们先准备好前端入参:
{
"username": "admin",
"password": "123456"
}//注意设置请求头Content-Type:application/json
然后到了重点,编写切面类:
package com.sunlong.jz.config;
import com.alibaba.fastjson.JSONObject;
import com.sunlong.jz.common.JzResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
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.io.BufferedReader;
import java.io.InputStreamReader;
/**
* @author SL
* @desc 创建AOP 用于实现自定义controller参数填充
* @Slf4j 必须结合lombok一起使用,否则无效
*/
@Slf4j
@Aspect
@Component
public class CommonAop { // 用于记录每个controller执行的耗时时间,毫秒级 private ThreadLocal<Long> timeLocal = new ThreadLocal<>();
/**
* 定义切入点,切入点为com.example.aop下的所有函数
*/
@Pointcut("execution(public * com.sunlong.jz.controller.*.*(..))")
public void point() {
}
/**
* @param joinPoint
* @throws Throwable
* @desc 前置通知:在连接点之前执行的通知
*/
@Around("point()")
public Object doBefore(ProceedingJoinPoint joinPoint) throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
try {
BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
StringBuilder responseStrBuilder = new StringBuilder();
int input;
boolean flag = false;
while ((input = streamReader.read()) != -1) {
char c = (char) input;
if (c == '{'){
flag = true;
}
if (flag) {
responseStrBuilder.append(c);
}
if (c == '}'){
flag = false;
}
}
JSONObject jsonObject = JSONObject.parseObject(responseStrBuilder.toString());
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
if (args[i] == null && jsonObject != null) {
args[i] = jsonObject.toJSONString();
break;
}
}
//执行
return joinPoint.proceed(args);
} catch (Exception e) {
e.printStackTrace();
}
return JzResponse.error("执行出错!");
}
}
这里要把切入点设定为controller下的所有接口方法,然后使用@Before注解标明是在controller方法运行前执行doBefore方法,至于方法的内容就比较直接了,直接获取request变量中的内容,然后提取json变量的部分,赋值给json参数就可以了。
注意我这里因为很明确json变量肯定是null所以才直接判断是null的。