元注解
- 说简单点,就是 定义其他注解的注解 。 比如Override这个注解,就不是一个元注解。而是通过元注解定义出来的。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
- 这里面的 @Target @Retention 就是元注解。
- 元注解有六个:@Target(表示该注解可以用于什么地方)、@Retention(表示再什么级别保存该注解信息)、@Documented(将此注解包含再javadoc中)、@Inherited(允许子类继承父类中的注解)、@Repeatable(1.8新增,允许一个注解在一个元素上使用多次)、@Native(1.8新增,修饰成员变量,表示这个变量可以被本地代码引用,常常被代码生成工具使用)。
自定义注解
- 在Java中,类使用class定义,接口使用interface定义,注解和接口的定义差不多,增加了一个@符号,即@interface,代码如下:
public @interface EnableAuth { }
- 注解中可以定义成员变量,用于信息的描述,跟接口中方法的定义类似,代码如下:
public @interface EnableAuth { String name(); }
- 还可以添加默认值:
public @interface EnableAuth { String name() default "猿天地"; }
- 上面的介绍只是完成了自定义注解的第一步,开发中日常使用注解大部分是用在类上,方法上,字段上,示列代码如下:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface EnableAuth { }
- Target:用于指定被修饰的注解修饰哪些程序单元,也就是上面说的类,方法,字段
- Retention:用于指定被修饰的注解被保留多长时间,分别SOURCE(注解仅存在于源码中,在class字节码文件中不包含),CLASS(默认的保留策略,注解会在class字节码文件中存在,但运行时无法获取),RUNTIME(注解会在class字节码文件中存在,在运行时可以通过反射获取到)三种类型,如果想要在程序运行过程中通过反射来获取注解的信息需要将Retention设置为RUNTIME
- Documented:用于指定被修饰的注解类将被javadoc工具提取成文档
- Inherited:用于指定被修饰的注解类将具有继承性
Java中常用注解使用
- @Override 表示当前方法覆盖了父类的方法
- @Deprecated 表示方法已经过时,方法上有横线,使用时会有警告。
- @SuppressWarnings 表示关闭一些警告信息(通知Java编译器忽略特定的编译警告)
- @SafeVarargs (jdk1.7更新) 表示:专门为抑制“堆污染”警告提供的。
- @FunctionalInterface (jdk1.8更新) 表示:用来指定某个接口必须是函数式接口,否则就会编译出错。
Spring常用注解
- @Configuration把一个类作为一个IoC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean。
- @Scope注解 作用域
- @Lazy(true) 表示延迟初始化
- @Service用于标注业务层组件
- @Controller用于标注控制层组件@Repository用于标注数据访问组件,即DAO组件。
- @Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
- @Scope用于指定scope作用域的(用在类上)
- @PostConstruct用于指定初始化方法(用在方法上)
- @PreDestory用于指定销毁方法(用在方法上)
- @DependsOn:定义Bean初始化及销毁时的顺序
- @Primary:自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
- @Autowired 默认按类型装配,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。如下: @Autowired @Qualifier("personDaoBean") 存在多个实例配合使用
- @Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。
- @PostConstruct 初始化注解
- @PreDestroy 摧毁注解 默认 单例 启动就加载
注解与反射的结合
注解和反射经常结合在一起使用,在很多框架的代码中都能看到他们结合使用的影子。可以通过反射来判断类,方法,字段上是否有某个注解以及获取注解中的值, 获取某个类中方法上的注解代码示例如下:
Class<?> clz = bean.getClass();
Method[] methods = clz.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(EnableAuth.class)) {
String name = method.getAnnotation(EnableAuth.class).name();
}
}
通过isAnnotationPresent判断是否存在某个注解,通过getAnnotation获取注解对象,然后获取值。
示例
- 一个类的某些字段上被注解标识,在读取该属性时,将注解中的默认值赋给这些属性,没有标记的属性不赋值
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @Documented @Inherited public @interface MyAnno { String value() default "有注解"; }
- 定义一个类
@Data @ToString public class Person { @MyAnno private String stra; private String strb; private String strc; public Person(String str1,String str2,String str3){ super(); this.stra = str1; this.strb = str2; this.strc = str3; } }
- 这里给str1加了注解,并利用反射解析并赋值:
public class MyTest { public static void main(String[] args) { //初始化全都赋值无注解 Person person = new Person("无注解","无注解","无注解"); //解析注解 doAnnoTest(person); System.out.println(person.toString()); } private static void doAnnoTest(Object obj) { Class clazz = obj.getClass(); Field[] declareFields = clazz.getDeclaredFields(); for (Field field:declareFields) { //检查该字段是否使用了某个注解 if(field.isAnnotationPresent(MyAnno.class)){ MyAnno anno = field.getAnnotation(MyAnno.class); if(anno!=null){ String fieldName = field.getName(); try { Method setMethod = clazz.getDeclaredMethod("set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1),String.class); //获取注解的属性 String annoValue = anno.value(); //将注解的属性值赋给对应的属性 setMethod.invoke(obj,annoValue); }catch (NoSuchMethodException e){ e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } } } }
- 运行结果:
Person(stra=有注解, strb=无注解, strc=无注解) `` 当开发者使用了Annotation 修饰了类、方法、Field 等成员之后,这些 Annotation 不会自己生效,必须由开发者提供相应的代码来提取并处理 Annotation 信息。这些处理提取和处理 Annotation 的代码统称为 APT(Annotation Processing Tool)。 注解的提取需要借助于 Java 的反射技术,反射比较慢,所以注解使用时也需要谨慎计较时间成本。
下一节:注解和反射经常结合在一起使用,在很多框架的代码中都能看到他们结合使用的影子。