Java基础-复习11-注解

一. 概述

1. 什么是注解(java.lang.annotation)?

注解提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联,注解可以让一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会影响代码的实际逻辑,仅仅起到辅助性的作用。

注解本质是一个继承了Annotation 的特殊接口,其具体实现类是Java 运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java 运行时生成的动态代理对象 $Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler 的 invoke 方法。该方法会从 memberValues 这个 Map 中索引出对应的值。而 memberValues 的来源是 Java 常量池。

2. 注解有什么作用?

  • 在编译时进行格式检查。如 @override 检查该方法重写的是否为父类方法
  • 生成文档。这是 java 最早提供的注解,常用的有@param @return 等
  • 跟踪代码依赖性,实现替代配置文件功能。如 @WebServlet 使得可以直接在 Servlet 源代码上注入配置信息,从而抛弃使用 web.xml 来配置 Servlet

二. 注解的区分

1. 元注解

java.lang.annotation 提供了四种元注解(专门注解其他的注解)

  • @Documented:表示是否将该注解在javadoc提取时添加到文档中
  • @Retention:定义该注解的生命周期
  • @Target:表示该注解可以用于哪些参数类型
  • @Inherited:是否允许子类继承该注解

2. 常见注解

  • @Override:检测是否正确重写父类方法
  • @Deprecated:检测是否过时
  • @SuppressWarnings:使用SuppressWarnings(“unchecked”) 抑制编译器警告
  • @FunctionalInterface:用来指定该接口是函数式接口

3. 自定义注解

使用 @Retention 注解时必须指定一个 RetentionPolicy 枚举类中的 value 变量

其中 @Retention(RUNTIME) 因为可以反射,所以最常用,默认是CLASS

public enum RetentionPolicy {
   
    SOURCE,//在编译结束之后丢弃,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。
    
    CLASS,//在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式
   
    RUNTIME//始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
}

使用 @Target 注解时,ElementType 枚举类提供以下 value 变量

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE
}
3.1 自定义标记注解

没有任何成员变量的注解称作为标记注解,@Override 就是一个标记注解

//类似定义接口
public @interface MyAnnotation{}
3.2 自定义元数据注解

自定义带有成员变量的注解叫做元数据注解

@Target({ElementType.METHOD}) //下面的例子需要的目标类型为方法
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation{
	//注解成员只能用public或默认(default)这两个访问权修饰
	//注解成员只能用基本数据类型和String、Enum、Class、annotations等数据类型
	String name();
	int age();
}

4. 反射注解信息

4.1 利用自定义注解注入基本信息到方法上

根据前面创建的 @MyAnnotation 注解,让一个方法的变量对应注解中的变量,此时注解上的信息和方法上的信息还是没有关联的

public class AddPerson{
	@MyAnnotation(name = "张三", age = 28)
	public void add(String name, int age){
		System.out.println(name);
		System.out.println(age);
	} 
}

利用反射把注解上的信息注入到方法上

public class Demo{
	@Test
	public void test() throws Exception{
		//反射出类的方法
		Class clazz = AddPerson.class;
		Method method = clazz.getMethod("add", String.class, int.class);

		//通过反射得到的方法获取注解上的信息
		MyAnnotation ma = method.getAnnotation(MyAnnotation.class);
		String name = ma.name();
		int age = ma.age();

		//得到的信息注入到方法上
		Object o = clazz.newInstance();
		method.invoke(o, name, age);
	}
}
4.2 利用自定义注解注入对象到属性或方法上

Person 类

public class Person {

    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

PersonDao 类

public class PersonDao {

    @InjectPerson(name = "李四", age = 30)
    private Person person;

    public Person getPerson() {
        return person;
    }

    @InjectPerson(name = "张三", age = 28)
    public void setPerson(Person person) {
        this.person = person;
    }
}

自定义注解 @InjectPerson

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface InjectPerson {
    String name();
    int age();
}

注入工具编写,test1() 为注入到方法上,test2() 为注入到属性上

public class InjectionUtils {

    @Test
    public void test1() throws Exception{

        //得到要注入的属性
        PropertyDescriptor pd = new PropertyDescriptor("Person", PersonDao.class);

        //得到要注入的属性的类型并创建实例对象
        Person person = (Person)pd.getPropertyType().newInstance();

        //得到属性的写方法
        Method writeMethod = pd.getWriteMethod();

        //反射出方法上声明的注解
        InjectPerson injectPerson = writeMethod.getAnnotation(InjectPerson.class);

        //得到注解中声明的信息,遍历
        Method[] methods = injectPerson.getClass().getMethods();

        for(Method m : methods){
            try {
                String name = m.getName();
                PropertyDescriptor pd1 = new PropertyDescriptor(name, Person.class);
                Method writeMethod1 = pd1.getWriteMethod();
                Object o = m.invoke(injectPerson, null);
                writeMethod1.invoke(person, o);
            }catch(Exception e){
                continue;
            }
        }

        //把填充了数据的person通过写方法invoke到personDao对象中
        PersonDao personDao = new PersonDao();
        writeMethod.invoke(personDao, person);

        System.out.println(personDao.getPerson().getName());
        System.out.println(personDao.getPerson().getAge());
    }
    
	@Test
    public void test2() throws Exception{

        Field field = PersonDao.class.getDeclaredField("person");

        Person person = (Person)field.getType().newInstance();

        InjectPerson injectPerson = field.getAnnotation(InjectPerson.class);

        Method[] methods = injectPerson.getClass().getMethods();

        for(Method m : methods){
            String name = m.getName();
            try {
                PropertyDescriptor pd1 = new PropertyDescriptor(name, Person.class);
                Method writeMethod1 = pd1.getWriteMethod();
                Object o = m.invoke(injectPerson, null);
                writeMethod1.invoke(person, o);
            }catch(Exception e){
                continue;
            }
        }

        PersonDao personDao = new PersonDao();
        field.setAccessible(true);
        field.set(personDao, person);

        System.out.println(personDao.getPerson().getName());
        System.out.println(personDao.getPerson().getAge());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值