jfinal 实现添加@JsonBody自动注入json数据

分享一个关于Json数据自动注入到controller方法参数的方案,有更好的方案大家可以一块讨论,抛砖引玉。

原理:

  1. 定义注解@JsonBody

  2. 全局拦截器解析注解

  3. 替换Controller 方法中带有@JsonBody的参数

定义注解:

@Documented
@Retention(RUNTIME)
@Target(PARAMETER)
public @interface JsonBody {
	/**
	 * 是否对Json内容根据注解进行校验
	 * @return
	 */
	public boolean validate() default true;
}

定义拦截器:

/**
 * 该拦截器使得json格式的数据也能作为action的参数
 * 
 * @author ThinkPad
 * @param <T>
 *
 */
public class JsonInterceptor implements Interceptor {
	private static final String jsonType = "application/json";

	@Override
	public void intercept(Invocation inv) {
		Controller controller = inv.getController();
		String contentType = controller.getRequest().getContentType();
		Parameter[] parameters = inv.getMethod().getParameters();
		JsonBody jsonBody = null;
		
		// 判断contentType 是否包含 application/json
		if (contentType != null && contentType.indexOf(jsonType) > -1) {
			for (int i = 0; i < parameters.length; i++) {
				jsonBody = parameters[i].getAnnotation(JsonBody.class);
				if (jsonBody != null) {
					Class<?> T = parameters[i].getType();
					Object result = null;
					try {
						result = JsonKit.parse(controller.getRawData(), T);
					} catch (Exception e) {
						throw new BadRequestException("Bad Request");
					}
					if (result != null && jsonBody.validate()) {
						ValidationUtil.checkValidation(result);
					}
					// 替换原先的参数
					inv.setArg(i, result);
				}
			}
		}
		inv.invoke();
	}

}

添加全局拦截器:

	@Override
	public void configInterceptor(Interceptors me) {
		//耗时拦截器
		me.addGlobalActionInterceptor(new ActionPerformanceLoggingInterceptor());
		// 异常处理拦截器
		me.addGlobalActionInterceptor(new AppExceptionInterceptor());
		// 认证信息拦截器
		me.addGlobalActionInterceptor(new AuthenInterceptor());
		//请求是否来自mobile,便于业务处理
		me.addGlobalActionInterceptor(new MobileInterceptor());
		//参数校验
		me.addGlobalActionInterceptor(new RequestParasValidator());
		//json 拦截,使得json参数也能作为action的参数,放在方法参数上
		me.addGlobalActionInterceptor(new JsonInterceptor());
		//异步方法
		me.addGlobalServiceInterceptor(new AsyncInterceptor());
		//对service或者dao等非controller层增加时间记录
		me.addGlobalServiceInterceptor(new LogExecutionTimeInterceptor());
	}

使用:

	@NoNeedLogin
	public void getToken(@JsonBody SysUser user) {
		String token = this.authenticationService.getToken(user.getUsername(), user.getPassword());
		renderResult(token);
	}

具体代码详见:

https://gitee.com/git_zhanglong/future

该工程持续集成jfinal相关解决方案,欢迎点赞关注

关注我的公众号,免费获取Java + Jfinal学习视频

image.png



评论区

HingLo

2020-03-13 14:16

方法不错,但是我有点小问题。1:parameterTypes[i].getAnnotation(JsonBody.class); 好像无法获取到注解信息。需要通过:inv.getMethod().getParameters()[i].getAnnotation(JsonBody.class); 来获取,2:官方提供的JsonKit工具无法将字符串转为JavaBean。即JsonKit.parse(controller.getRawData(), T); 需要替换

HingLo

2020-03-13 15:47

建议将如下三行移动到if条件中,提高效率一些:
String contentType = controller.getRequest().getContentType();
Class[] parameterTypes = inv.getMethod().getParameterTypes();
JsonBody jsonBody = null;

快乐的蹦豆子

2020-03-13 15:58

大体上这么个原理,我记得原来测试通过的, 如果哪个地方有点问题,可以稍微改改,需要增加什么逻辑都可以加上,抛砖引玉

HingLo

2020-03-13 16:15

@快乐的蹦豆子 对,方法非常的好用。原理也比较清晰易懂

registernet

2020-03-15 16:09

这个其实觉得通过覆盖controller的getBean/getModel方法去做更好,因为通过interceptor去做这个替换相当于jfinal已经帮你创建过一次入参对象了,现在把它丢了,有点浪费,不如帮你创建的时候就创建正确的对象.翻看源码发现转换是通过BeanGetter/ModelGetter类去转换的,BeanGetter/ModelGetter的创建在ParaProcessorBuilder类里,但有个问题,看这个类的101行代码,传递给BeanGetter/ModelGetter的class在这里取得的,p.getType()这个取法会丢掉运行时的泛型信息,所以通过覆盖controller的getBean/getModel方法实际上是目前是行不通的,如果这里用的是p.getParameterizedType(),然后BeanGetter/ModelGetter类的参数改成更通用一点的Type,Controller的对应getBean/GetModel方法也一样,不是Class,那这样就能做到了,

registernet

2020-03-15 16:13

Class类实现了Type接口,所以,如果做了上面的改变,不会影响现有代码,现有的所有实现依然是兼容的.

快乐的蹦豆子

2020-03-15 20:54

这是json,和你说的还不是一回事

快乐的蹦豆子

2020-04-12 11:23

根据@HingLo的反馈对代码做了一些调整,目前代码copy直接改改包名之类的就可以用

热门分享

扫码入社