java 注解

Java 注解

注解

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

1、注解的类型

  1. 元注解

    • 是用于定义注解的注解,包括:

      • @Retention(标明注解被保留的阶段)
      • @Target(标明注解使用的范围)
      • @Inherited(标明注解可继承)
      • @Documented(标明是否生成 javadoc 文档)
    • ```java
      public enum RetentionPolicy {
      // 此注解类型的信息只会记录在源文件中,编译时将被编译器丢弃,也就是说不会保存在编译好的 class 文件中
      // 例如:@SuppressWarnings
      SOURCE,

      // 编译器将注解记录在 class 文件中,但不会加载到 JVM 中。如果一个注解声明没指定范围,则系统默认值就是 Class
      // 例如:@Override
      CLASS,


      //注解信息会保留在源文件、类文件中,在执行的时候也会加载到 java 的 jvm 中,因此可以通过反射进行读取
      //例如:Deprecated
      RUNTIME
      }

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71

      2. java 自带的标准注解,使用这些注解后编译器就会进行检查

      - @Override:标明重写某个方法
      - @Deprecated:标明某个类或方法过时
      - @SuppressWarnings:标明要忽略的警告

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



      ## 2、注解配置和 xml 配置的关系



      - 可以认为 xml 和注解都是元数据
      - xml:是一种集中式的元数据,与源代码无绑定
      - 注解:是一种分散式的元数据,与源代码紧绑定



      ## 3、注解的作用



      1. 生成文档,通过代码里标识的元数据生成 javadoc 文档
      2. 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证
      3. 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码
      4. <font color='orange'>运行时动态处理</font>font>,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例



      ## 4、注解实现



      ## 5、注解实现的步骤



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



      ## 6、不基于 Spring 容器实现



      1. 已知 Class 直接反射

      1. 声明注解

      ```java
      // 运行时
      @Retention(RetentionPolicy.RUNTIME)
      // 作用在字段上
      @Target(ElementType.FIELD)
      public @interface Person {
      // 支持基本数据类型,枚举
      String name() default "";

      SexEnum sex() default SexEnum.UNKNOWN;

      int age() default 18;
      }
    1. 定义一个普通实体类

      1
      2
      3
      4
      5
      6
      7
      8
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class Human {
      private String name;
      private SexEnum sex;
      private int age;
      }
    2. 定义注解处理方法

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      public static void initField(HumanTest humanTest) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
      Class<? extends HumanTest> aClass = humanTest.getClass();
      // 获取字段集
      Field[] declaredFields = aClass.getDeclaredFields();
      for (Field declaredField : declaredFields) {
      // 获取字段上的注解
      Person declaredAnnotation = declaredField.getDeclaredAnnotation(Person.class);
      if(declaredAnnotation != null){
      // 获取字段的类型
      Class<?> type = declaredField.getType();
      // 获取字段的实例
      Constructor<?> constructor = type.getConstructor();
      Human human = (Human) constructor.newInstance();
      // 为字段赋值
      human.setName(declaredAnnotation.name());
      human.setSex(declaredAnnotation.sex());
      human.setAge(declaredAnnotation.age());
      declaredField.set(humanTest,human);
      }
      }
      }
  2. 类扫码,然后反射

    1. spring(spring 工具包)
    2. reflections(反射工具包)
    3. 自己实现

7、基于 Spring 容器

spring模块结构

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

img

加入钩子后流程:

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

7.1、基于BeanDefinitionRegistryPostProcessor接口

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

ConfigurationClassPostProcessor 就是基于 BeanDefinitionRegistryPostProcessor接口实现 @Configuration@Bean@Import@ImportResource@ComponentScan@PropertySource@Conditional 等注解的。

  1. 声明注解

    1
    2
    3
    4
    5
    6
    7
    @Target({ElementType.TYPE}) //声明应用在l上
    @Retention(RetentionPolicy.RUNTIME) //运行期生效
    @Documented
    public @interface Registry {

    String value() default "";
    }
  2. 添加注解

    1
    2
    3
    @Registry
    public class Dog {
    }
  3. 注解实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Component
    public class AnnotationBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    ClassPathScanningCandidateComponentProvider classPathScanningCandidateComponentProvider = new ClassPathScanningCandidateComponentProvider(false);
    classPathScanningCandidateComponentProvider.addIncludeFilter(new AnnotationTypeFilter(Registry.class));
    Set<BeanDefinition> beanDefinitions = classPathScanningCandidateComponentProvider.findCandidateComponents("com.dblones.java.annotation.example05");
    for(BeanDefinition beanDefinition : beanDefinitions){
    registry.registerBeanDefinition(beanDefinition.getBeanClassName(), beanDefinition);
    }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
    }

7.2、基于 BeanFactoryPostProcessor 接口

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

@Configuration注解的代理实现就是在这个阶段通过ConfigurationClassPostProcessor类实现的

7.3、基于 InstantiationAwareBeanPostProcessor 接口

  • BeanPostProcessor的子接口,通过接口字面意思翻译该接口的作用是感知Bean实例化的处理器。就是全面干预Bean实例化过程,包括Bean实例化前后设置属性初始化前后

@Autowired@Value@Inject@Resource@PostConstruct@PreDestroy@WebServiceRef@EJB@Required就是借助InstantiationAwareBeanPostProcessor接口实现的,其中@Autowired@Value@InjectAutowiredAnnotationBeanPostProcessor类实现的,@Resource@PostConstruct@PreDestroy@WebServiceRef@EJBCommonAnnotationBeanPostProcessor 类实现的,@RequiredRequiredAnnotationBeanPostProcessor类实现的。

7.4、基于 BeanPostProcessor 接口

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

@PostConstruct@PreDestroy就是借助BeanPostProcessor 接口实现的,具体是通过InitDestroyAnnotationBeanPostProcessor类实现的。

7.5、基于 SmartInitializingSingleton 接口

  • 对全体实例bean进行后置处理。

Spring Cloud中的@LoadBalanced就是借助SmartInitializingSingleton接口实现的。

7.6、AOP

  • 对方法进行拦截。

常见权限,加解密,日志,方法调用等处理可以借助AOP完成

  • Copyrights © 2022-2023 hqz

请我喝杯咖啡吧~

支付宝
微信