注解与反射

1.元注解

元注解是用于解释注解的注解,java中提供了四个元注解,分别是:

  • @Target 用于描述注解的作用范围,可用于字段、方法、类等
  • @Retention 用于描述注解的生命周期(Source < class < runtime)
  • @Inherited 用于描述该注解可以在类中继承
  • @Document 用于在javadoc中使用,如果加上该注解,则在javadoc中显示注解修饰的内容。

2.自定义注解

public class TestMyAnnotation {
    @FirstAnnotation(name = "zhangsan")
    private String name;
    @SecondAnnotation("age")
    private int age;

}

@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD, ElementType.METHOD})
@interface FirstAnnotation{
    String name();
    int age() default 0;
}

@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD, ElementType.METHOD})
@interface SecondAnnotation{
    String value();
}

Note:

  1. 当注解的字段定义不是value时,使用注解必须带上字段的名称;
  2. 当注解的字段定义为value时,使用注解可以不带上字段的名称;
  3. 当注解的字段定义没有默认值时,使用注解必须要给该字段复制;

3.解析注解——反射

解析注解需要使用java的反射技术,反射可以在程序运行期间获取类的任何内部信息并可以操作对象累不得属性和方法。

3.1 获取class对象的三种方式

  1. 对象.class;
  2. Class.forName(“类的全路径名称”)
  3. 类名.getClass()
    public class TestReflection {
     public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
         Animal a = new Dog();
         Class clazz1 = a.getClass();
         System.out.println("clazz1:" + clazz1.hashCode());
         Class clazz2 = Class.forName("com.test.reflection.Dog");
         System.out.println("clazz2:" + clazz2.hashCode());
         Class clazz3 = Dog.class;
         System.out.println("clazz3:" + clazz3.hashCode());
     }
    }
    class Animal{
    }
    class Dog extends Animal{
    }
    

    上述三个输出的哈希值是一样的,因此表明一个加载的类在JVM中只会有一个class实例。

    3.2反射的应用

package com.test.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

public class ReflectionDemo {

    public static void main(String[] args) throws Exception {

        Class personClass = Class.forName("com.test.reflection.Person");

        //使用反射获取所有的构造函数 包括私有的
        Arrays.stream(personClass.getDeclaredConstructors()).forEach(constructor -> System.out.println(constructor));

        //通过有参构造函数创建对象
        Constructor constructor = personClass.getDeclaredConstructor(String.class, int.class);
        Object obj1 = constructor.newInstance("tom", 34);
        Person p = (Person) obj1;
        System.out.println(p.toString());

        //通过无参构造函数创建对象
        Object obj = personClass.newInstance();
        System.out.println("obj:" + obj);

        //使用反射获取所有方法
        Arrays.stream(personClass.getDeclaredMethods()).forEach(method -> System.out.println(method));
        //获取walk方法并执行该方法
        Method walkMethod = personClass.getDeclaredMethod("walk");
        walkMethod.invoke(obj);

        //获取setAge方法并执行该方法
        Method setAge = personClass.getDeclaredMethod("setAge", int.class);
        setAge.invoke(obj, 33);
        System.out.println(obj);

        //使用反射获取所有字段
        Arrays.stream(personClass.getDeclaredFields()).forEach(field -> System.out.println(field));
        Field field = personClass.getDeclaredField("name");
        //由于私有属性 开启访问权限
        field.setAccessible(true);
        field.set(obj, "zzzz");
        System.out.println(obj);
    }
}
class Person {
    private String name;
    private int age;

    Person() {
    }
    private Person(String name) {
        this.name = name;
    }
    Person(int age) {
        this.age = age;
    }
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void walk() {
        System.out.println("人可以走路");
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "name:" + name + ",age:" + age;
    }
}

结果输出如下:

com.test.reflection.Person(java.lang.String,int)
com.test.reflection.Person(int)
private com.test.reflection.Person(java.lang.String)
com.test.reflection.Person()
name:tom,age:34
obj:name:null,age:0
public java.lang.String com.test.reflection.Person.toString()
public void com.test.reflection.Person.setAge(int)
public void com.test.reflection.Person.walk()
public int com.test.reflection.Person.getAge()
人可以走路
name:null,age:33
private java.lang.String com.test.reflection.Person.name
private int com.test.reflection.Person.age
name:zzzz,age:33

3.3简写@Autowired 的自定义

package com.wizard.develop;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD})
@interface MyAutowired {
    String value() default "";
}
public class TestMyAutowired {

    @MyAutowired
    private Test test;

    public Test getTest() {
        return this.test;
    }
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class clazz = Class.forName("com.wizard.develop.TestMyAutowired");
        Object object = clazz.newInstance();
        Arrays.stream(clazz.getDeclaredFields()).forEach(field -> {
            MyAutowired myAutowired = field.getAnnotation(MyAutowired.class);
            if (null != myAutowired) {
                try {
                    //获取Test的class文件 并生成对象
                    Class fieldClazz = field.getType();
                    field.setAccessible(true);
                    Object o = fieldClazz.newInstance();
                    field.set(object, o);
                    System.out.println("from myAutowired:" + o);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                TestMyAutowired testMyAutowired = (TestMyAutowired)object;
                System.out.println("from get method myAutowired:" + testMyAutowired.getTest());
            }
        });
    }

}
class Test {
    private int number;
}

上述输出的结果相同,说明通过反射的方式将对象成功的注入了进去,这里对于注解的解析写在了main方法中。实际上Spring的道理是一样的,在启动服务的时候也是通过启动main方法方式,不断扫描各个类上的注解,将对象注入到相应的类中。 理解:注解实际上可以理解为做一个标记。例如在某个类中的某个字段打上注解,标记这个字段要被做一些行为。