一.注解基础回顾
@interface,使用这个关键字可以说明这是一个注解。
java.lang.annotation 提供了四种元注解,可以用来注解其他的注解,像Override这些注解都会用到它们,我们自定义注解的时候也会用到,四种注解如下:
@Target:说明注解用于什么地方。
@Retention:什么时候使用注解。
@Inherited:是否允许子类继承该注解
@Documented:注解是否将包含在JavaDoc中
1,@Target的参数可以是
ElementType.TYPE : 类、接口或枚举声明
ElementType.FIELD : 字段声明(包括枚举常量)
ElementType.METHOD : 方法声明
ElementType.PACKAGE : 包声明
ElementType.CONSTRUCTOR : 构造器声明
ElementType.ANNOTATION_TYPE : 注释类型声明,解释一下,例如Target本身就是一个注解,那么他自己的target就是ANNOTATION_TYPE,即target的target是ANNOTATION_TYPE。
这些参数类型也就是类类型可以获取的类型(别扭)。
2,@Retention定义注解的生命周期,可用参数为枚举类RetentionPolicy中的类型
RetentionPolicy.SOURC : 编译器会放弃注解, 也就是编译期一过,这些注解就没有作用了。
可以看到下面这些注解使用了SOURC ,从这些注解可以看出来,确实过了编译期就没什么用了,所以一般都是提示或校验会用到。
RetentionPolicy.CLASS : 注释将被编译器记录在类文件中,但是在运行时不需要被VM保留。这是默认行为。很少用到。
RetentionPolicy.RUNTIME : 注释将由编译器记录在类文件中,并在运行时由VM保留,因此可以反射性地读取它们,我们自定义的注解一般都需要用到他们。
二.注解的自定义及使用过程
我们先定义两个注解,分别是ElementType.METHOD和ElementType.FIELD类型
/**
* @author uiao
* @Title: 自定义方法类型的注解
* @date 2018/8/315:36
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface City {
//默认值,如果你在方法使用了默认值,但是没有填写内容就会给附上这个值
String code() default "010";
String name() default "北京";
}
/**
* @author uiao
* @Title: 自定义属性类型的注解
* @date 2018/8/316:11
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Phone {
String value() default "13261201263";
}
之后我们在Company这个类上使用我们自定义的Phone和City注解
/**
* @author uiao
* @Title: 注解的使用
* @date 2018/8/315:46
*/
public class Company {
@Phone(value = "12345")
private String phone;
private String cityName;
private String cityCode;
public Company(){}
public Company(String phone, String cityName, String cityCode) {
this.phone = phone;
this.cityName = cityName;
this.cityCode = cityCode;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
@City(name = "上海", code = "020")
public String getCityCode() {
return cityCode;
}
public void setCityCode(String cityCode) {
this.cityCode = cityCode;
}
@Override
public String toString() {
return "Company{" +
"phone='" + phone + '\'' +
", cityName='" + cityName + '\'' +
", cityCode='" + cityCode + '\'' +
'}';
}
}
测试使用注解
/**
* @author uiao
* @Title: 自定义注解测试
* @date 2018/8/315:39
*/
public class AnnotationTest {
private static Logger logger = Logger.getLogger(AnnotationTest.class);
public static void main(String[] args) {
Company company = injectAnnotationValue(Company.class);
// 打印输入注解后的值
System.out.println("打印注入注解后的值 ==>>");
System.out.println(company.toString());
}
public static Company injectAnnotationValue(Class> clazz) {
Company company = new Company();
// 遍历Class里使用的FIELD类型的注解
for (Field field : clazz.getDeclaredFields()) {
Phone phone = field.getAnnotation(Phone.class);
if (phone != null) {
System.out.println(clazz.getName() + "上使用了属性注解:" + field.getName() + ",注解的值为: " + phone.value());
// 给company实例赋上注解的值
company.setPhone(phone.value());
}
}
// 遍历Class里使用的METHOD类型的注解
for (Method method : clazz.getDeclaredMethods()) {
City city = method.getAnnotation(City.class);
if (city != null) {
System.out.println(clazz.getName() + "上使用了方法注解:" + method.getName() + ",注解的值为: "
+ " code: " + city.code()
+ ", name: " + city.name());
// 给company实例赋上注解的值
company.setCityCode(city.code());
company.setCityName(city.name());
}
}
return company;
}
}
打印结果
D:\Java\jdk1.8.0_111\bin\java ...
annotation.Company上使用了属性注解:phone,注解的值为: 12345
annotation.Company上使用了方法注解:getCityCode,注解的值为: code: 020, name: 上海
打印注入注解后的值 ==>>
Company{phone='12345', cityName='上海', cityCode='020'}
打印结果中可以看到我们将对象company的phone,cityCode,cityName的值都设置成了注解里的内容。
注意:注解类型必须要用在该用的地方,就是你自定义的注解类型是method,你就不可以在field上使用它,但是。。。我们最后是通过反射来做一些操作的,所以即使你只是在一个field上使用了一个注解,但是在使用反射获取到注解后,可以随意更改对象的所有field。
说白了注解只是给我们的类,属性或方法加打了个标签,java支持通过反射机制获取这个标签。至于获取了标签后我们可以做什么事情,就是自己说了算了,这也是多数框架只是改个注解就会起到很大作用的原因。
呃。。。下一篇会写通过实现HandlerMethodArgumentResolver接口,重写resolveArgument方法实现自定义参数注解。