JAVA 注解 介绍

一、来源是什么

从JDK1.5之后,开始支持注解。
注解的出现,提供配置的功能。往往使用XML来做配置的功能。但是XML意味着要读取文件,编写比较麻烦。
而很多场景下,class需要的是简单的配置,而且方便地使用。
注解在编译中,嵌入到字节码上。使用java的反射机制,获取到注解。这个机制,从语言层面进行配置支持,极大方便了
程序开发,并且被spring、dubbo、mybatis、netty等各大框架应用,在反射,动态代理,服务发现,依赖注入等各方面,
有广泛应用。减少了很多重复的模板代码,降低耦合性,开发和扩展。作为一个高级工程师,是必须要熟悉和应用的。

二、原理是什么

要使用注解,就要熟悉元注解(注解的注解)。
元注解有4个,

@Retention

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}
public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,
    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,
    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

标识这个注解怎么保存
SOURCE 表示只在代码中,意味着只是给人看;
CLASS 表示会编译到class文件中,但是运行时看不到;
RUNTIME 表示运行时能够发现。,反射只有在这种模式才能获取到注解。

@Documented

生产javadoc时,带上注解信息

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

@Targe

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}
public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,
    /** Field declaration (includes enum constants) */
    FIELD,
    /** Method declaration */
    METHOD,
    /** Formal parameter declaration */
    PARAMETER,
    /** Constructor declaration */
    CONSTRUCTOR,
    /** Local variable declaration */
    LOCAL_VARIABLE,
    /** Annotation type declaration */
    ANNOTATION_TYPE,
    /** Package declaration */
    PACKAGE,
    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,
    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

表示注解的范围,
比如TYPE,表示注解在class、interface、enum上,
FIELD,表示注解在成员变量上等等。

@Inherited

标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

Inherited只能注解在注解类上。
继承的规则:
1.子类继承父类的注解,包括类注解、method注解、Field注解
2.子类重写父类的继承,都会失去注解;
3.注解的继承不能应用在接口上

3个内置注解:

@Override - 检查该方法是否是重载方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings - 指示编译器去忽略注解中声明的警告。

从 Java 7 开始,额外添加了 3 个注解:
@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

三、如何自定义注解?

自定义注解,就是使用4个元注解了。

下面是例子

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

@Documented
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Deprecated
@Inherited
@MyAnnotation("11")
public @interface MyAnnotation {
    String value() default "";

    int code() default 10;

    Class<?> dd() default Integer.class;


}

注解要注意的点:
注解的配置,是依赖自定义成员变量。
成员变量的定义,比较奇特。
是一个无参数的方法,
方法名就是成员变量名;
返回值就是成员变量的数据类型;
可以设置默认值,使用default value;
数据类型只能是基础数据类型和String、class、enum,以及这些类型的数组类型

注解继承性的测试:

/**
 * 测试注解标记在父类的Inherited
 */
@MyAnnotation
public abstract class InheritedTest{
    @MyAnnotation
    public int liu = 0;

    @MyAnnotation
    public int liu1 = 0;

    public static void main(String[] args) {
        try {
            Class inheritedTest1 = Class.forName("www.hjq.cn.annotation.study.InheritedTest1");
            //子类的继承
            if (inheritedTest1.isAnnotationPresent(MyAnnotation.class)) {
                System.out.println("子类继承父类的注解");
            } else {
                System.out.println("子类不继承父类的注解");
            }

            Method methodFun1 = inheritedTest1.getMethod("fun1");
            if (methodFun1.isAnnotationPresent(MyAnnotation.class)) {
                System.out.println("子类重写方法继承父类方法的注解");
            } else {
                System.out.println("子类重写方法不继承父类方法的注解");
            }

            Method methodFun2 = inheritedTest1.getMethod("fun2");
            if (methodFun2.isAnnotationPresent(MyAnnotation.class)) {
                System.out.println("子类继承的方法继承父类方法的注解");
            } else {
                System.out.println("子类继承的方法不继承父类方法的注解");
            }

            Method methodFun3 = inheritedTest1.getMethod("fun3");
            if (methodFun3.isAnnotationPresent(MyAnnotation.class)) {
                System.out.println("子类实现的抽象方法继承父类抽象方法的注解");
            } else {
                System.out.println("子类实现的抽象方法不继承父类抽象方法的注解");
            }

            Field field = InheritedTest1.class.getField("liu");
//            field.setAccessible(true);
//            System.out.println(field);
            if (field.isAnnotationPresent(MyAnnotation.class)) {
                System.out.println("子类继承的field继承父类field的注解");
            } else {
                System.out.println("子类继承的field不继承父类field的注解");
            }

            Field field1 = InheritedTest1.class.getField("liu1");
//            field.setAccessible(true);
//            System.out.println(field);
            if (field1.isAnnotationPresent(MyAnnotation.class)) {
                System.out.println("子类重写的field继承父类field的注解");
            } else {
                System.out.println("子类重写的field不继承父类field的注解");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    @MyAnnotation("111")
    public void fun1() {
        System.out.println(111);
    }

    @MyAnnotation("222")
    public void fun2() {
        System.out.println(222);
    }

    @MyAnnotation("222")
    public abstract void fun3() ;
}


class InheritedTest1 extends InheritedTest {


    public int liu1 = 0;

    @Override
    public void fun1() {
        super.fun1();
    }

    @Override
    public void fun3() {

    }
}

结果:
子类继承父类的注解
子类重写方法不继承父类方法的注解
子类继承的方法继承父类方法的注解
子类实现的抽象方法不继承父类抽象方法的注解
子类继承的field继承父类field的注解
子类重写的field不继承父类field的注解
/**
 * 测试注解标记在接口的Inherited
 */
public class InheritedInterferTest implements I1{

    public static void main(String[] args) {
        try {
            Class InheritedTest1 = Class.forName("www.hjq.cn.annotation.study.InheritedInterferTest");

            if (InheritedTest1.isAnnotationPresent(MyAnnotation.class)) {
                System.out.println("类继承接口的注解");
            } else {
                System.out.println("类不继承接口的注解");
            }

            Method methodFun1 = InheritedTest1.getMethod("i1");
            if (methodFun1.isAnnotationPresent(MyAnnotation.class)) {
                System.out.println("类方法继承接口方法的注解");
            } else {
                System.out.println("类方法不继承接口方法的注解");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
//    @MyAnnotation
    public void i1() {

    }
}


@MyAnnotation
interface I1{
    @MyAnnotation
    void i1();

}
结果:
类不继承接口的注解
类方法不继承接口方法的注解

四、有哪些广泛的应用

spring上常用的注解有

@Component
@Controller
@Service
@Repository
@Scope 主要作用是解决创建的对象单实例还是多实例
@Autowired和@Resource是用来修饰字段、构造函数或者设置方法,并做注入的
@Bean
@Configuration
@ComponentScan
@Aspect 声明一个切面
@After 在方法执行之后执行(方法上) @Before 在方法执行之前执行(方法上) @Around 在方法执行之前与之后执行(方法上)
@PointCut 声明切点 在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)
@Value 为属性注入值
@Profile
@EnableAsync 
@Async
@EnableWebMvc

好多,要好好学习

关键还是自己,需要用到的时候,灵活使用。懂得自定义注解,配合上反射,具备极大的威力,就看我们实际上应用了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值