1.注解是什么?

注解(Annotation)也叫元数据,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。其实说白就是代码里的特殊标志,这些标志可以在编译,类加载,运行时被读取,并根据这些信息执行相应的处理,以便于其他工具补充信息或者进行部署。

元数据(Metadata),又称中介数据、中继数据,为描述数据的数据(data about data),主要是描述数据属性(property)的信息,用来支持如指示存储位置、历史数据、资源查找、文件记录等功能。

如果说注释是写给人看的,那么注解就是写给程序看的。它更像一个标签,贴在一个类、一个方法或者字段上。它的目的是为当前读取该注解的程序提供判断依据。比如程序只要读到加了@Test的方法,就知道该方法是待测试方法,又比如@Before注解,程序看到这个注解,就知道该方法要放在@Test方法之前执行。

1.1 注解的类型

一般常用的注解可以分为三类:

  1. 元注解,元注解是用于定义注解的注解,包括@Retention(标明注解被保留的阶段)、@Target(标明注解使用的范围)、@Inherited(标明注解可继承)、@Documented(标明是否生成javadoc文档)。

    1. public enum RetentionPolicy {
    2. //此注解类型的信息只会记录在源文件中,编译时将被编译器丢弃,也就是说不会保存在编译好的class文件中
    3. //例如:@SuppressWarnings
    4. SOURCE,
    5. //编译器将注解记录在class文件中,但不会加载到JVM中。如果一个注解声明没指定范围,则系统默认值就是
    6. //例如:@Override
    7. CLASS,
    8. //注解信息会保留在源文件、类文件中,在执行的时也加载到Java的JVM中,因此可以通过反射进行读取。
    9. //例如:Deprecated
    10. RUNTIME
  2. Java自带的标准注解,包括@Override(标明重写某个方法)、@Deprecated(标明某个类或方法过时)和@SuppressWarnings(标明要忽略的警告),使用这些注解后编译器就会进行检查。

  3. 自定义注解,可以根据自己的需求定义注解。

    1.2.注解配置和xml配置的关系

    我们知道Spring早期是使用xml来进行配置的,现在更推荐注解配置,那么xml配置和注解配置的区别是什么呢?
    我们可以认为xml和注解都是元数据。
    xml: 是一种集中式的元数据,与源代码无绑定。
    注解:是一种分散式的元数据,与源代码紧绑定。

    1.3 注解的作用

    生成文档,通过代码里标识的元数据生成javadoc文档。
    编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。
    编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。
    运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例。

    2.注解实现

    2.1 实现注解的步骤

    (1).声明注解
    (2).添加注解
    (3). 获取添加了注解的目标。通常是Class对象,Method对象,Field对象,还有Constructor对象,Parameter对象,Annotation对象等
    a.通过已知对象,获取Class对象
    b.通过全类路径,获取Class对象
    c.扫描包路径,获取Class对象
    (4).实现注解处理器。借助反射,获取注解对象,读取注解属性值, 然后根据注解及属性值做相应处理

    2.2 不基于spring容器实现

    (1). 已知class,直接反射
    举个例子:
    申明注解

    1. @Retention(RetentionPolicy.RUNTIME)
    2. @Target(ElementType.FIELD)
    3. public @interface Person {
    4. String name() default "";
    5. String sex() default "";
    6. int age() default 18;
    7. }

    定义一个普通实体类

    1. public class Human {
    2. // 姓名
    3. private String name;
    4. // 性别
    5. private String sex;
    6. // 年龄
    7. private Integer age;
    8. @Override
    9. public String toString() {
    10. return "Human{" +
    11. "name='" + name + '\'' +
    12. ", sex='" + sex + '\'' +
    13. ", age=" + age +
    14. '}';
    15. }

    注解实现及测试

    1. public class Example01 {
    2. @Person(name="张三", sex = "男", age = 23)
    3. private Human human1;
    4. @Person(name="小红", sex = "女", age = 21)
    5. private Human human2;
    6. public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    7. Example01 example01 = new Example01();
    8. System.out.println(example01.human1);
    9. System.out.println(example01.human2);
    10. example01.initField();
    11. System.out.println(example01.human1);
    12. System.out.println(example01.human2);
    13. }
    14. // 注解实现
    15. public void initField() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    16. Class clazz = this.getClass();
    17. Field[] fields = clazz.getDeclaredFields();
    18. for(Field field : fields){
    19. Person person = field.getDeclaredAnnotation(Person.class);
    20. if(person != null) {
    21. Human human = ((Class<Human>) field.getType()).getDeclaredConstructor().newInstance();
    22. human.setSex(person.sex());
    23. human.setName(person.name());
    24. human.setAge(person.age());
    25. field.set(this, human);
    26. }
    27. }
    28. }
    29. }

    (2). 类扫描,然后反射
    类扫描的方法:
    A. spring(spring工具包)
    B. reflections(反射工具包)
    C. 自己实现
    举例1:借助spring-core
    声明注解

    1. @Retention(RetentionPolicy.RUNTIME)
    2. @Target(ElementType.TYPE)
    3. public @interface Cat {
    4. String name() default "";
    5. }

    添加注解

    1. @Cat(name="tom")
    2. public class Tomcat {
    3. private String name;
    4. public Tomcat(){}
    5. public Tomcat(String name) {
    6. this.name = name;
    7. }
    8. @Override
    9. public String toString() {
    10. return "Tomcat{" +
    11. "name='" + name + '\'' +
    12. '}';
    13. }
    14. }

    实现注解并测试

    1. public class SpringExample {
    2. public static void main(String[] args) throws Exception {
    3. ClassPathScanningCandidateComponentProvider classPathScanningCandidateComponentProvider =
    4. new ClassPathScanningCandidateComponentProvider(false);
    5. classPathScanningCandidateComponentProvider.addIncludeFilter(new AnnotationTypeFilter(Cat.class));
    6. Set<BeanDefinition> beanDefinitions =
    7. classPathScanningCandidateComponentProvider.findCandidateComponents("springboot");
    8. for (BeanDefinition beanDefinition : beanDefinitions) {
    9. String beanClassName = beanDefinition.getBeanClassName();
    10. Class clazz = Class.forName(beanClassName);
    11. Cat cat = (Cat) clazz.getDeclaredAnnotation(Cat.class);
    12. String name = cat.name();
    13. Object obj = clazz.getDeclaredConstructor(String.class).newInstance(name);
    14. System.out.println(obj);
    15. }
    16. }
    17. }

    举例2:借助reflections反射工具包
    声明注解

    1. @Retention(RetentionPolicy.RUNTIME)
    2. @Target(ElementType.TYPE)
    3. public @interface Cat {
    4. String name() default "";
    5. }

    添加注解

    1. @Cat(name="tom")
    2. public class Tomcat {
    3. private String name;
    4. public Tomcat(){}
    5. public Tomcat(String name) {
    6. this.name = name;
    7. }
    8. @Override
    9. public String toString() {
    10. return "Tomcat{" +
    11. "name='" + name + '\'' +
    12. '}';
    13. }
    14. }

    使用注解及测试

    1. public class ReflectionsExample {
    2. public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    3. Reflections reflections = new Reflections("com.dblones.java.annotation.example03", new TypeAnnotationsScanner(), new SubTypesScanner(), new FieldAnnotationsScanner());
    4. Set<Class<?>> cats = reflections.getTypesAnnotatedWith(Cat.class);
    5. for(Class<?> clazz : cats){
    6. Cat cat = (Cat) clazz.getDeclaredAnnotation(Cat.class);
    7. String name = cat.name();
    8. Object object = clazz.getDeclaredConstructor(String.class).newInstance(name);
    9. System.out.println(object);
    10. }
    11. }
    12. }

    基于以上的例子,我们会发现,如果存在多个自定义注解的时候,获取到添加了注解的目标,是整个实现自定义注解过程中,比较频繁发生的事情,如果每个注解的实现都去扫描包,很显然,扫描包这个动作重复了,而且效率低下,那有没有什么办法,可以减少扫描或者我们自己根本不需要扫描呢?一种方式是把扫描抽离出来做为公共初始化方法,另外一种方式就是如果我们的程序里面使用了spring容器,那么我们可以借助spring容器,借助spring的扫描,我们可以不需要自己扫描。

    2.3 基于spring容器实现

    2.3.1 spring模块结构(部分)

1.jpg
整个Spring IOC容器,核心模块包括构造Bean定义,实例化BeanFactory,注册Bean定义,实例化Bean并完成依赖注入,提供Bean获取。核心组件包括BeanDefinition实例对象,BeanFactory实例对象,Bean实例对象。

2.3.2 spring容器核心流程(部分)

2.jpg

核心流程:
1.BeanFactory实例化
2.注册Bean定义
3.实例化普通单例Bean
4.依赖注入属性
5.普通Bean初始化
6.所有普通单例Bean实例化完成
3.jpg
加入钩子后流程:
1.BeanFactory实例化
2.注册Bean定义
3.注册后置处理,实现BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry
4.BeanFactory后置处理,实现BeanFactoryPostProcessor.postProcessBeanFactory
5.实例化Bean前置处理,实现InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
6.实例化普通单例Bean
7.依赖注入属性
8.实例化Bean后置处理,实现InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation
9.依赖注入后置处理,实现InstantiationAwareBeanPostProcessor.postProcessPropertyValues
10.Bean初始化前置处理,实现BeanPostProcessor.postProcessBeforeInitialization
11.普通Bean初始化( @PostConstruct注解方法初始化, InitializingBean接口afterPropertiesSet方法初始化, @Bean注解init-method属性方法初始化)
12.Bean初始化后置处理,实现BeanPostProcessor.postProcessBeforeInitialization
13.所有普通单例Bean实例化完成
14.所有普通单例bean实例化后置处理,实现SmartInitializingSingleton.afterSingletonsInstantiated

2.3.3 spring常见注解实现(部分)

4.jpg

2.3.4 借助spring接口实现注解方式

(1). 基于BeanDefinitionRegistryPostProcessor接口
这个接口一般用来注册bean定义,当然也可以修改bean定义信息,触发时机在BeanFactory实例化后,注册了一些系统内置的bean定义之后

  1. public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
  2. void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
  3. }

ConfigurationClassPostProcessor就是基于BeanDefinitionRegistryPostProcessor接口实现@Configuration、@Bean、@Import、@ImportResource、@ComponentScan、@PropertySource、@Conditional等注解的。
举例:
声明注解

  1. @Target({ElementType.TYPE}) //声明应用在属性上
  2. @Retention(RetentionPolicy.RUNTIME) //运行期生效
  3. @Documented
  4. public @interface Registry {
  5. String value() default "";
  6. }

添加注解

  1. @Registry
  2. public class Dog {
  3. }

注解实现

  1. @Component
  2. public class AnnotationBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
  3. @Override
  4. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
  5. ClassPathScanningCandidateComponentProvider classPathScanningCandidateComponentProvider = new ClassPathScanningCandidateComponentProvider(false);
  6. classPathScanningCandidateComponentProvider.addIncludeFilter(new AnnotationTypeFilter(Registry.class));
  7. Set<BeanDefinition> beanDefinitions = classPathScanningCandidateComponentProvider.findCandidateComponents("com.dblones.java.annotation.example05");
  8. for(BeanDefinition beanDefinition : beanDefinitions){
  9. registry.registerBeanDefinition(beanDefinition.getBeanClassName(), beanDefinition);
  10. }
  11. }
  12. @Override
  13. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  14. }
  15. }

测试验证

  1. @Configuration
  2. @ComponentScan("com.dblones.java.annotation.example05")
  3. public class Example01 {
  4. public static void main(String[] args) {
  5. ApplicationContext context = new AnnotationConfigApplicationContext(Example01.class);
  6. Dog dog = context.getBean(Dog.class);
  7. System.out.println(dog);
  8. }
  9. }

(2). BeanFactoryPostProcessor
这个接口一般修改bean定义信息,触发时机在BeanFactory实例化后,注册了bean定义之后,BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry方法之后

  1. public interface BeanFactoryPostProcessor {
  2. void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
  3. }

@Configuration注解的代理实现就是在这个阶段通过ConfigurationClassPostProcessor类实现的。
(3).InstantiationAwareBeanPostProcessor
是BeanPostProcessor的子接口,通过接口字面意思翻译该接口的作用是感知Bean实例化的处理器。就是全面干预Bean实例化过程,包括Bean实例化前后,设置属性,初始化前后。

  1. public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
  2. Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
  3. boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;
  4. PropertyValues postProcessPropertyValues(
  5. PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException;
  6. }

方法描述postProcessBeforeInitializationBeanPostProcessor接口中的方法,在Bean的自定义初始化方法之前执行postProcessAfterInitializationBeanPostProcessor接口中的方法,在Bean的自定义初始化方法执行完成之后执行postProcessBeforeInstantiation自身方法,是最先执行的方法,它在目标对象实例化之前调用,该方法的返回值类型是Object,我们可以返回任何类型的值。由于这个时候目标对象还未实例化,所以这个返回值可以用来代替原本该生成的目标对象的实例(比如代理对象)。如果该方法的返回值代替原本该生成的目标对象,后续只有postProcessAfterInitialization方法会调用,其它方法不再调用;否则按照正常的流程走postProcessAfterInstantiation在目标对象实例化之后调用,这个时候对象已经被实例化,但是该实例的属性还未被设置,都是null。因为它的返回值是决定要不要调用postProcessPropertyValues方法的其中一个因素(因为还有一个因素是mbd.getDependencyCheck());如果该方法返回false,并且不需要check,那么postProcessPropertyValues就会被忽略不执行;如果返回true,postProcessPropertyValues就会被执行postProcessPropertyValues对属性值进行修改,如果postProcessAfterInstantiation方法返回false,该方法可能不会被调用。可以在该方法内对属性值进行修改

我们常见的注解@Autowired、@Value、@Inject、@Resource、@PostConstruct、@PreDestroy、@WebServiceRef、@EJB、@Required就是借助InstantiationAwareBeanPostProcessor接口实现的,其中@Autowired、@Value、@Inject是AutowiredAnnotationBeanPostProcessor类实现的,@Resource、@PostConstruct、@PreDestroy、@WebServiceRef、@EJB是CommonAnnotationBeanPostProcessor 类实现的,@Required是RequiredAnnotationBeanPostProcessor类实现的。

(4). BeanPostProcessor

对实例Bean进行后置处理, Bean初始化方法调用前被调用或Bean初始化方法调用后被调用

  1. public interface BeanPostProcessor {
  2. //bean初始化方法调用前被调用
  3. Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
  4. //bean初始化方法调用后被调用
  5. Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
  6. }

运行顺序

=Spring IOC容器实例化Bean=
=Bean属性赋值=
=调用BeanPostProcessor的postProcessBeforeInitialization方法=
=调用bean实例的初始化方法=
=调用BeanPostProcessor的postProcessAfterInitialization方法=

我们常见的注解@@PostConstruct、@PreDestroy就是借助BeanPostProcessor 接口实现的,具体是通过InitDestroyAnnotationBeanPostProcessor类实现的。

举例:
声明注解

  1. @Target({ElementType.FIELD}) //声明应用在属性上
  2. @Retention(RetentionPolicy.RUNTIME) //运行期生效
  3. @Documented
  4. public @interface Boy {
  5. String name() default "";
  6. }

添加注解

  1. @Service
  2. public class Hello {
  3. @Boy(name = "小明")
  4. String name = "world";
  5. public void say() {
  6. System.out.println("hello, " + name);
  7. }
  8. }

注解实现

  1. @Component //注意:Bean后置处理器本身也是一个Bean
  2. public class BoyAnnotationBeanPostProcessor implements BeanPostProcessor {
  3. @Override
  4. public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  5. /**
  6. * 利用Java反射机制注入属性
  7. */
  8. Field[] declaredFields = bean.getClass().getDeclaredFields();
  9. for (Field declaredField : declaredFields) {
  10. Boy annotation = declaredField.getAnnotation(Boy.class);
  11. if (null == annotation) {
  12. continue;
  13. }
  14. declaredField.setAccessible(true);
  15. try {
  16. declaredField.set(bean, annotation.name());
  17. } catch (IllegalAccessException e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. return bean;
  22. }
  23. @Override
  24. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  25. return bean;
  26. }
  27. }

测试验证

  1. @Configuration
  2. @ComponentScan("com.dblones.java.annotation.example04")
  3. public class Example {
  4. public static void main(String[] args) {
  5. ApplicationContext context = new AnnotationConfigApplicationContext(Example.class);
  6. Hello hello = context.getBean(Hello.class);
  7. hello.say();
  8. }
  9. }

(5). SmartInitializingSingleton
对全体实例bean进行后置处理。

  1. public interface SmartInitializingSingleton {
  2. void afterSingletonsInstantiated();
  3. }

Spring Cloud中的@LoadBalanced就是借助SmartInitializingSingleton接口实现的。
(6). AOP
对方法进行拦截。
常见权限,加解密,日志,方法调用等处理可以借助AOP完成。