前面写了理解java注解一,但是我估计大家都不会太在意:不就是一些注解的概念之类的吗?不过,我还是觉得,基础很重要。我刚开始了解注解的时候,就很纳闷:一个注解,写个名字,代码就知道自己要干啥?你当这是人工智能啊!然后问其他小伙伴,他们也是一知半解,茫茫然然。后来去看注解讲解,慢慢猜明白了一些。
一个注解,看起来高大上,可以进行参数校验,数据转换,方法处理的过滤处理等等等等,实际上,不过是通过反射,在后台默默实现一遍罢了,这些代码才真是逻辑处理的无名英雄!
若果大家使用过java框架一段时间,都会或多或少的接触使用注解,对一些注解的作用也是耳熟能详。可能更多的人都是使用Spring全家桶,@RestController,@ResponseBody,@RequestBody,@Autowired,@Resource,@RequestParam,@Service,@Component,@Repository,@Mapper,@Configuration,都非常熟悉,用起来也很方便。
不过其中的原理都不甚了解,甚至一点开这些注解,可能都是一大坨,懒得去看,这些东西贸然去看,确实很头疼,我也没能力把这些讲清楚,我今天就讲个入门,后续大家有兴趣的话可以看下Spring源码深度解析,了解这些注解的实现。
这里我提一句哈,一些新手可能会犯这样的错误。
@RestController("/demo")
public class DemoController {
@PostMapping("test")
public DefaultResponse getInfoList(@RequestBody RequestVo requestVo){
return null;
}
}
@Data
public class RequestVo {
private String uuid ;
private Integer minAge;
private String maxAge;
// 学校id必须不为空
@NotNull(message = "学校id不能为空")
private String schoolId;
}
大家可以看到,这是一个简单的不能再简单的一个Spring的请求接收处理,通过@RequestBody 直接将json入参转换为对象RequestVo ,同时添加了校验schoolId 不能为空。
当然,细心的小伙伴已经发现,其实这个校验 @NotNull(message = "学校id不能为空") 并不能起作用,原因很简单,之前就说了,注解不是人工智能,不可能自己识别的,而且这个@NotNull 并没有和@RequestBody 并没有结合到一起,也没有直接嵌入到Spring里面,根本就不可能自动校验。不知道有没有小伙伴遇到这样的问题,测试提bug的时候还很纳闷,不过这种不仔细测试还真不好发现,哈哈,调节下气氛。
其实,打开这个@NotNull 就可以发现,它的使用是和另一个注解结合的,那就是@Valid,只有这样写,才真正的做了为空校验
@PostMapping("test")
public DefaultResponse getInfoList(@Valid @RequestBody RequestVo requestVo){
return null;
}
当然,这个@Valid封装的很经典,同一个接口多次请求的话,可以根据需要,进行不同的过滤规则,这个大伙有兴趣的可以看下哈~(主要就在@NotNull 中的groups定义处理)
扯得有点远了,开始正题。
下面写一个简单的注解获取。
注解颜色:
package com.example.annotion;
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Color {
String value();
}
这是一个很普通的注解,就是颜色,value 表明颜色的值
水果Apple ,水果自然是有颜色的,比如,你听到苹果,可能就能知道,哦,颜色是红色:
package com.example.vo;
import com.example.annotion.Color;
import lombok.Data;
@Data
public class Apple {
@Color(value = "red")
private String name;
private Double price;
}
注解的获取:
package com.example.demo;
import com.example.annotion.Color;
import com.example.vo.Apple;
import java.lang.reflect.Field;
public class MyTest {
public static void main(String[] args) throws Exception{
// 加载类
Class<?> appleClass = Class.forName("com.example.vo.Apple");
//这样也可以 Class<? extends Apple> appleClass = new Apple().getClass();
Field[] fields = appleClass.getDeclaredFields();
for (Field field:fields) {
field.setAccessible(true);
Color color = field.getAnnotation(Color.class);
if (color != null){ // 说明有Color 的注解
System.out.println("apple 的颜色是:" + color.value());
System.out.println(" 属性的名称是: " + field.getName());
}
break;
}
System.out.println("this is end.....");
}
}
输出结果:
Connected to the target VM, address: '127.0.0.1:58458', transport: 'socket'
apple 的颜色是:red
属性的名称是: name
this is end.....
Disconnected from the target VM, address: '127.0.0.1:58458', transport: 'socket'
Process finished with exit code 0
当然,大家看到这个例子的时候一定是感觉---垃圾,这么幼稚的获取操作,我也会!
是的,这个注解的解析很基础看,可能稍微学过一点java的人都能写出来。
但是,千遍万变,原理不变,基础这东西,往往很重要,很关键,你不自觉的就容易忽略掉很多。
下面我说一下之前工作中用到的一个注解处理:
一个项目,很多地方都需要文件上传,然后保存文件信息。前端传来一个页面,其中可能有许多需要保存的文件类,比如ImageVo,当然,这个肯定不能一个一个处理,那样太繁琐了,而且这种和业务关系不大的保存操作应该统一封装才最合理。
当然,你每次都取出其中的参数,然后传递到一个方法里也是可以的,但是,你每个请求都要取一遍参数吗?而且如果参数层级比较多呢?还要加判断等等等等,
当然,这个用注解+反射就可以很好的处理!
尽量这周写出来吧,之前太懒了,还是有必要些一些东西沉淀的,O(∩_∩)O哈哈~