一、元注解
1、@Target(作用目标)
标注注解作用的范围
类型 | 说明 |
---|---|
ElementType.TYPE | 类,接口(包括注解类型)或enum声明 |
ElementType.FIELD | 字段,枚举常量 |
ElementType.METHOD | 方法声明 |
ElementType.PARAMETER | 参数声明 |
ElementType.CONSTRUCTOR | 构造器声明 |
ElementType.LOCAL_VARIABLE | 局部变量声明 |
ElementType.PACKAGE | 包声明 |
ElementType.LOCAL_VARIABLE | 局部变量 |
ElementType.ANNOTATION_TYPE | 注解 |
2、@Retention(保留策略)
表示在什么级别保存该注解信息
类型 | 说明 |
---|---|
RetentionPolicy.SOURCE | 注解仅存在于源码中,在class字节码文件中不包含 |
RetentionPolicy.CLASS | 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得 |
RetentionPolicy.RUNTIME | 注解会在class字节码文件中存在,在运行时可以通过反射获取到首 先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解。 |
3、@Documented
将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。相当与@see,@param 等。
4、@Inherited
Inherited 是继承的意思,但是他并不是说直接本身可以继承,而是说一个超类被@Inheritedh注解,
二、反射
此处为何会讲解反射,因为注解和反射是密切相关的,没有反射,无法完成注解功能的解析。
1、概念
class
Method
Field
Constructor
2、反射核心类
java.lang.Class; 类
java.lang.reflect.Constructor; 构造器方法
java.lang.reflect.Field; 属性
java.lang.reflect.Method; 方法
java.lang.reflect.Modifier; 修饰符
3、类加载三个阶段
1、源代码(source)
原代码不是指java文件,是class字节码文件
2、类加载(class)
class字节码文件经类加载器classloader加载到虚拟机内存中,类加载器解析class文件生成Class类型的对象
3、运行时(runtime)
newInstance()根据java类型生成对象
4、 获取class类对象三种方法
1、Class class=Class.forName("com.xx.xx.xx.Person");
2、Class<Person> class=Person.class;
3、Person p=new Person();
Class class=p.getClass();
三、解析注解(源码剖析)
写一个注解,自定义返回自己的toString方法。返回自己想要的格式
完整代码可以去此项目下查看cloud-annotation
1、创建自定义注解
注解名称 | 功能 |
---|---|
@JsonFormat | 格式化功能 |
@JsonIgnore | 忽略功能 |
@JsonProperty | 属性名 |
/**
* 作用于属性上,用户按要求格式化日期
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface JsonFormat {
String pattern() default "yyyy-MM-dd HH:mm:ss";
}
/**
* 作用在属性上,用于忽略一些属性
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface JsonIgnore {
}
/**
* 属性别名
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface JsonProperty {
String value(); //使用的时候必须给值
2、注解定义好后自定义方法toJSONString(类似于toString)方法
package com.xwb.springcloud.annotation.json;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
@SuppressWarnings("all")
public class JSON {
public static String toJSONString(Object object) {
StringBuffer json = new StringBuffer();
try {
//1、得到object的Class对象
Class<?> clazz = object.getClass();
//2、得到class里面的所有属性
Field[] fields = clazz.getDeclaredFields();
//3、循环遍历所有属性病打破属性的访问权限
int index = 0;
//得到属性长度
int length = fields.length;
json.append("{");
for (Field field : fields) {
index++;
//判断属性上是否有忽略的注解
if (field.isAnnotationPresent(JsonIgnore.class)) {
continue;
}
//私有变量可读
field.setAccessible(true);
//4得到属性名作为json的key
String name = field.getName();
//5、得到属性值作为json的value
Object value = field.get(object);
//判断属性上是否有更改名字的注解
if (field.isAnnotationPresent(JsonProperty.class)) {
//有的话取出注解上的名字
JsonProperty annotation = field.getAnnotation(JsonProperty.class);
name = annotation.value();
}
json.append("\"" + name + "\"");
if (value instanceof String) {
json.append(":\"" + value.toString() + "\"");
} else if (value instanceof Date) {
//此处如果是日期的话,则判断是否有日起格式化的注解
Date date = (Date) value;
if (field.isAnnotationPresent(JsonFormat.class)) {
JsonFormat format = field.getAnnotation(JsonFormat.class);
json.append(":\"" + parseDateToStr(date, format.pattern()) + "\"");
} else {
json.append(":\"" + date.getTime() + "\"");
}
} else {
json.append(":" + value.toString());
}
if (index != length) {
json.append(",");
}
}
json.append("}");
return json.toString();
} catch (Exception e) {
System.out.println(e.getCause());
}
return null;
}
public static String parseDateToStr(Date date, String format) {
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.format(date);
}
}
说明:
定义的注解,没有任何作用,但是想让注解发挥它的作用,就要定义相应的方法去解析他,然后让他发挥作用。
谁定义的注解谁去解析。而不是说自己定义的注解让java去解析,他是不识别的。
3、调用
public class TestJSON {
public static void main(String[] args) {
User user = new User(1, "张三", "宁夏银川", new Date());
String s = JSON.toJSONString(user);
System.out.println(s);
}
}
4、输出结果
{“user_name”:“张三”,“user_address”:“宁夏银川”,“birth”:“2021-11-12”}
示例
总结:注解本身没有任何作用,只是申明的而已,具体起作用是定义该注解的开发者想让其起到什么功能,然后再对应解析的方法中运用反射获取类、属性、方法、参数…上是否有该注解,有的话(xxx instanceof xxx)根据开发者自己的想法去实现该功能方法。
查找源码中的类或者方法使用的快捷键是【ctrl+n】
1、spring的@RequestBody注解
spring-webmvc-5.2.2.RELEASE.jar包下的RequestResponseBodyMethodProcessor类进行解析
关于该注解的详细说明请观看此博客@RequestBody
2、mybatis的@Mapper注解
mybatis-3.4.4.jar包下的MapperAnnotationBuilder类parse方法RequestResponseBodyMethodProcessor
关于@Mapper请看此博客@Mapper
3、spring的@Configuration
spring-context-5.2.2.RELEASE.jar包下的ConfigurationClassPostProcessor类
源码具体详解可以查看此博主博客@Configuration
4、springcloud的@FeignClient注解
spring-cloud-openfeign-core-2.2.1.RELEASE.jar包的FeignClientsRegistrar类
关于@FeignClient的源码解析请看此博客@FeignClient