在学习springMVC的时候,控制层方法参数上只需要加一个@RequestBody注解,然后就可以把前端传递get参数、post表单参数、Json数据转换成Java参数。这让我觉得非常神奇,非常厉害,为了学习,于是我打算实现一个这样的小小功能。
原理应该使用springAop功能对执行方法进行拦截,然后改变方法的执行参数。
一、先编写一个将普通字段值转换成Bean的类
public class parse {
public static <T> T read(Map<String,String> map, Class<T> clazz) throws Exception {
Constructor<T> constructor = clazz.getConstructor();
Method[] methods = clazz.getMethods();
T object =constructor.newInstance();
for (String key : map.keySet()){
for (Method method : methods) {
if (method.getName().contains("set"+key.replaceFirst(key.substring(0,1),key.substring(0,1).toUpperCase(Locale.ROOT)))) {
if (method.getParameterTypes()[0] == String.class) {
method.invoke(object, map.get(key));
}
}
}
}
return object;
}
}
为了方便且容易实现,这里我使用的map作为参数。这里转换的JavaBean需要有无参构造方法,反射生成对象使用的是setter字段映射。
二、写一个注解来标记
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Request {
boolean required() default false;
}
三、编写切面
@Aspect
@Component
public class aspectj2 {
@Pointcut("execution(* com.ams.service.serviceImpl.user.*.*(..))")
private void pointCut(){
}
@Around("pointCut()")
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
Method method = methodSignature.getMethod();
Annotation[][] annotations= method.getParameterAnnotations();//获取方法参数上的注解
Object obj=null;
Map<String,String> map = new HashMap<>();
map.put("password","123");
log4j.getLogger().info(Arrays.toString(proceedingJoinPoint.getArgs()));
for (int i = 0; i < method.getParameterCount(); i++)
for (Annotation annotation : annotations[i]) {
Request request = (Request) annotation;
if (request.required()) {
map.put("username","true");
//替换执行参数值
proceedingJoinPoint.getArgs()[i] = parse.read(map, method.getParameterTypes()[i]);
}
else {
map.put("username","false");
proceedingJoinPoint.getArgs()[i] = parse.read(map, method.getParameterTypes()[i]);
}
}
obj=proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
return obj;
}
}
当这个aop环绕通知执行的时候,如果执行方法参数上有@Request注解,那么这个参数就会发生变化,这里为了简单方便我使用的是Map对象作为数据来源,实际上数据来源一般是前端传递过来的字节流或者字符流。
四、测试
//service层的方法
public void t1(@Request(required = true)user user1, @Request user user2,user user3) {
log4j.getLogger().info(user1);
log4j.getLogger().info(user2);
log4j.getLogger().info(user3);
}
@Test
public void t8() {
userService.t1(new user("user1","2342"),new user("user2","2333"),new user("zhang","12345"));
}
对比发现方法参数前后发生了变化 。当然实现的这个功能非常简陋,实际情况是非常非常复杂。