二(8.3):Java中的注解
注解本身没有任何业务逻辑它只是写在类、方法、属性或者参数上的“元数据”用来描述数据的数据。它就像是一张“便签纸”贴在代码的各个角落等着其他人编译器或框架来读取。1. 注解的长相与结构我们在代码里看到的 Override、Autowired、Slf4j它们的底层其实都是一种特殊的接口。如果你自己去定义一个注解代码长这样// 1. 关键使用 interface 来定义publicinterfaceMyTag{// 注解的属性长得很像方法Stringvalue()default默认值;intage()default18;}而在使用它的时候就像在填表格MyTag(value正式员工,age25)publicclassEmployee{// ...}元注解Meta-AnnotationsJava 官方为了让我们能控制自己写的注解提供了几个专门用来修饰注解的注解叫做元注解。① Target限制这个标签能贴在哪里你得规定这个便签是贴在“人类”额头上还是贴在“手方法”上。ElementType.TYPE只能贴在类、接口、枚举上。ElementType.METHOD只能贴在方法上。ElementType.FIELD只能贴在属性字段上。② Retention限制这个标签能活多久生命周期这是注解最核心的属性它决定了注解的命运一共有三种寿命三种策略RetentionPolicy.SOURCE只活在源码期代码编译成 .class 文件后这个注解就消失了。代表作Override给编译器看的、Lombok 的 Slf4j编译完就卸磨杀驴。RetentionPolicy.CLASS活到字节码期默认注解会被保留到 .class 文件里但 JVM 启动加载它时会把它丢弃。RetentionPolicy.RUNTIME活到运行期系统运行起来后注解依然常驻内存。代表作Spring 的 Controller、Autowired。因为活得久所以运行期可以通过反射去读取它。3. 注解是如何生效的注解本身只是个死标签如果不去读它它和注释没有任何区别。能让它发挥魔法的是背后的“注解处理器”。它主要分为两大门派门派 A编译期动态修改Lombok 派寿命SOURCE 级别。原理Java 编译器在编译代码时会调用它的插件注解处理器。插件发现有这个注解就直接修改你的语法树或者生成新的代码。结果打出来的 .class 包里注解没了但多了很多行自动生成的代码。门派 B运行期反射解析Spring / Hibernate 派寿命RUNTIME 级别。原理程序启动后Spring 等框架会化身“检查员”利用 Java 反射机制 去遍历所有的类和方法寻找特定的标签。结果发现类上有 Controller就把它实例话放进容器发现属性上有 Autowired就自动把对象塞进去。4. 手写自定义注解工作中 90% 的自定义注解都是为了结合反射在运行时根据注解做逻辑处理模仿 Spring 的 Autowired 或 RequestMapping。步骤 1定义一个运行时注解importjava.lang.annotation.*;// 1. 限定贴在 字段 上Target(ElementType.FIELD)// 2. 必须保留到运行时否则反射读不到Retention(RetentionPolicy.RUNTIME)publicinterfaceMyAutowired{// 注解的属性看起来像方法使用时像变量Stringvalue()default;// 默认值为空字符串}步骤 2使用该注解publicclassUserService{MyAutowired(注入用户服务实现)// 给属性贴标签privateStringserviceName;publicvoidprintService(){System.out.println(服务名serviceName);}}步骤 3通过反射解析注解核心逻辑importjava.lang.reflect.Field;publicclassAnnotationProcessor{publicstaticvoidmain(String[]args)throwsIllegalAccessException{UserServiceuserServicenewUserService();// 获取所有字段包括私有字段Field[]fieldsuserService.getClass().getDeclaredFields();for(Fieldfield:fields){// 检查字段上是否贴了 MyAutowired 注解if(field.isAnnotationPresent(MyAutowired.class)){// 读取注解上的值MyAutowiredannotationfield.getAnnotation(MyAutowired.class);StringinjectedValueannotation.value();// 获取到 注入用户服务实现// 暴力破解私有权限把值设进去模拟依赖注入field.setAccessible(true);field.set(userService,injectedValue);}}// 输出结果服务名注入用户服务实现userService.printService();}}