注解

什么是注解

用一个词就可以描述注解,那就是元数据,即一种描述数据的数据。所以,可以说注解就是源代码的元数据

元注解

JDK1.5之后内部提供的注解

  1. @Deprecated 意思是“废弃的,过时的
  2. @Override 意思是“重写、覆盖
  3. @SuppressWarnings 意思是“压缩警告
  4. @Documented –注解是否将包含在JavaDoc中
  5. @Target? –注解用于什么地方
  6. @Inherited – 是否允许子类继承该注解
  7. @Retention –什么时候使用该注解
  8. @Repeatable  (since jdk1.8)-多次赋值

  9. @SafeVarargs(since jdk1.7)

  10. @FunctionalInterface (since jdk1.8)-函数式接口

 

@Retention

定义注解的生命周期

  • RetentionPolicy.SOURCE

编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。

  • RetentionPolicy.CLASS

在类加载的时候丢弃。在字节码文件的处理中有用注解默认使用这种方式

  • RetentionPolicy.RUNTIME

始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

@Target

表示该注解用于什么地方。如果不明确指出,该注解可以放在任何地方。以下是一些可用的参数。需要说明的是:属性的注解是兼容的,如果你想给7个属性都添加注解,仅仅排除一个属性,那么你需要在定义target包含所有的属性。

  • ElementType.TYPE:用于描述类、接口或enum声明
  • ElementType.FIELD:用于描述实例变量
  • ElementType.METHOD
  • ElementType.PARAMETER
  • ElementType.CONSTRUCTOR
  • ElementType.LOCAL_VARIABLE
  • ElementType.ANNOTATION_TYPE
  • ElementType.PACKAGE 用于记录java文件的package信息

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

@Inherited

Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解
说的比较抽象。代码来解释

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}


@Test
public class A {}


public class B extends A {}

@Other

public class C extends A

{}

 

注解 Test 被 @Inherited 修饰,之后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。

但是C不拥有@Test注解,因为被@Other注解应用了

@Repeatable

在JDK1.8之前注解同时只能被应用一次,应用2次会编译错误。

什么样的注解会多次应用呢?通常是注解的值可以同时取多个。
举个例子,一个人他既是程序员又是产品经理,同时他还是个画家。

@interface Persons {
    Person[]  value();
}

@Repeatable(Persons.class)
@interface Person{
    String role default "";
}


@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{

}

注意上面的代码,@Repeatable 注解了 Person。而 @Repeatable 后面括号中的类相当于一个容器注解

什么是容器注解呢?就是用来存放其它注解的地方。它本身也是一个注解

我们再看看代码中的相关容器注解。

@interface Persons {
    Person[]  value();
}

按照规定,它里面必须要有一个 value 的属性,属性类型是一个被 @Repeatable 注解过的注解数组,注意它是数组。
如果不好理解的话,可以这样理解。Persons 是一张总的标签,上面贴满了 Person 这种同类型但内容不一样的标签。把 Persons 给一个 SuperMan 贴上,相当于同时给他贴了程序员、产品经理、画家的标签。
我们可能对于 @Person(role=”PM”) 括号里面的内容感兴趣,它其实就是给 Person 这个注解的 role 属性赋值为 PM .

@SafeVarargs

参数安全类型注解。它的目的是提醒开发者不要用参数做一些不安全的操作,它的存在会阻止编译器产生 unchecked 这样的警告。它是在 Java 1.7 的版本中加入的。

@SafeVarargs // Not actually safe!
    static void m(List<String>... stringLists) {
    Object[] array = stringLists;
    List<Integer> tmpList = Arrays.asList(42);
    array[0] = tmpList; // Semantically invalid, but compiles without warnings
    String s = stringLists[0].get(0); // Oh no, ClassCastException at runtime!
}

上面的代码中,编译阶段不会报错,但是运行时会抛出 ClassCastException 这个异常,所以它虽然告诉开发者要妥善处理,但是开发者自己还是搞砸了。

@FunctionalInterface

函数式接口注解,这个是 Java 1.8 版本引入的新特性。函数式编程很火,所以 Java 8 也及时添加了这个特性。

函数式接口 (Functional Interface) 就是一个具有一个方法的普通接口。

注解定义

定义

import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;


@Retention(RetentionPolicy.RUNTIME )
@Target(ElementType.TYPE)
public @interface Controller {
    String name() default "";
}

注解不支持继承

注解是不支持继承的,因此不能使用关键字extends来继承某个@interface,但注解在编译后,编译器会自动继承java.lang.annotation.Annotation接口

属性

注解内部可以定义一些属性。

语法:类型 属性名()  [ default 默认值 ];

@Retention(RetentionPolicy.RUNTIME )
@Target(ElementType.TYPE)
public @interface Controller {
    String name() default "";
}

 其实从代码的写法上来看,注解更像是一种特殊的接口,注解的属性定义方式就和接口中定义方法的方式一样,而应用了注解的类可以认为是实现了这个特殊的接口。

注解属性支持以下类型:

  • 所有基本类型(int,float,boolean,byte,double,char,long,short)

  • String

  • Class

  • enum

  • Annotation

  • 上述类型的数组

注解的属性必须有明确的值,要么使用默认值,要么在应用注解时指定值。

注解合并

每个开发人员都应该有过这样的经历:在编写某个类或接口的时候,需要声明Spring本身的注解(@Controller、@Service,@Dao),又需要声明自己公司编写的注解来完成公司的独特业务,然后就悲剧了,一个类上边声明了五六个注解,茫茫然不知所云。注解本身是好的,它可以替我们完成一些事情。但和XML一样,过度使用就编程了一种灾难。

     于是,一种新的替代方案出现了,那就是组合注解。比较经典的组合注解就是SpringBoot的@SpringBootApplication注解。
 

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Configuration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
 
 
    Class<?>[] exclude() default {};
 
 
    String[] excludeName() default {};
 
 

   //spring 在解析此处时,做特殊处理
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};
 
    //spring 在解析此处时,做特殊处理
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};
 
}

 

注解应用


import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;


enum EnumType
{
    First ,
        Second
        
}

@interface Reference
{
    
}

@interface Test
{
    String value();
}

@Retention(RetentionPolicy.RUNTIME )
@Target(value={ElementType.TYPE,ElementType.METHOD})
public @interface RequestMapping {

    //基本类型
    int size() default 0;
    //String
    String name() default "";
    //枚举
    EnumType Order() default EnumType.First;
    //Class类
    Class<?> ClassType() default Void.class;
    //注解
    Reference Reference() default @Reference;
    //数组
    int[] Ids() default {1,3,4};

}


@RequestMapping(
        size = 5
        ,name="jack"
        ,Order=EnumType.Second
        ,ClassType=Integer.class
        ,Reference=@Reference
        ,Ids={4,5,7}
        )

@Test("mytest") //当注解仅有一个名称为value的属性时,可以省略属性名称
class AAA
{
    
}

注解提取

通过反射可以获取注解的信息。

首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解

public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}

然后通过 getAnnotation() 方法来获取 Annotation 对象。

public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}


//AA:class
//P3:@Merge的属性
AA.class.getAnnotation(Merge.class).P3();

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值