# Spring注解的内部工作原理

###  注解工作原理
注解本身只是元数据，即描述数据的数据，被描述的数据可以是类、方法、属性、参数、构造器等。注解仅是标记本身没有任何可执行的功能代码，但是我们只要标注了注解，我们就能得到想要的功能，这是怎么做到的呢？我们理解肯定是有某处代码根据我们标注的注解找到我们注解的类、方法、属性、参数等数据本身，并根据注解本身的含义以及注解属性里的内容执行功能代码。

### 第一种：`BeanPostProcessor`
`BeanPostProcessor`实现中有一类名称为`*AnnotationBeanPostProcessor`都是针对处理注解的，对容器内标注了指定注解的Bean，进行功能处理。如：

- `AutowiredAnnotationBeanPostProcessor`：让`@Autowired`、`@Value`、`@Inject`注解起效；
- `CommonAnnotationBeanPostProcessor`：让`@PostConstruct`、`@PreDestroy`注解起效；
- `AsyncAnnotationBeanPostProcessor`：让`@Async`或`@Asynchronous`注解起效；
- `ScheduledAnnotationBeanPostProcessor`：让`@Scheduled`注解起效；
- `PersistenceAnnotationBeanPostProcessor`：让`@PersistenceUnit` 、 `@PersistenceContext`注解起效；
- `JmsListenerAnnotationBeanPostProcessor`：让`@JmsListener`注解起效。
它们都会在构造器里指定其能处理的注解类型，并在对应的方法进行功能处理，我们下面演示一个简单的例子帮助大家理解，我们定义注解`@InjectLogger`向Bean注入`org.slf4j.Logger`来做系统日志。

我们先定义要使用的注解，只能注解在类上，默认的后缀为“-Bean”
```java
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectLogger {
}
```
下面我们定义处理注解的
`InjectLoggerAnnotationBeanPostPorcessor`，我们在上节已经学习过，我们只需要实现`BeanPostPorcessor`接口即可：
```java
@Component
public class InjectLoggerAnnotationBeanPostPorcessor implements BeanPostProcessor {
    private Class<? extends Annotation> changeAnnotationType; //1

    public InjectLoggerAnnotationBeanPostPorcessor() {
        this.changeAnnotationType = InjectLogger.class; //1
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        ReflectionUtils.doWithFields(bean.getClass(), field -> { //2
            ReflectionUtils.makeAccessible(field); //3
            if(field.isAnnotationPresent(changeAnnotationType)){ //4
                Logger logger = LoggerFactory.getLogger(bean.getClass()); //5
                field.set(bean, logger); //6
            }
        });
        return bean;
    }
}
```
1. 指明当前类处理`@InjectLogger`注解；
2. 通过反射机制对类的每个属性（`Field`）进行处理，第一个参数是Bean的Class，第二参数是入参为Field无返回值的函数接口的Lambda实现；
3. 通过反射机制让当前属性可访问；
4. 新建Logger的实例logger
5. 通过反射将logger值设置到bean实例的当前属性（field）上。
我们将注解使用到其他Bean上使用：
```java
@Component
public class DemoLoggerService {
    @InjectLogger
    private Logger log;

    public void doSomething(){
        log.info("通过自定义InjectLoggerAnnotationBeanPostPorcessor让注解@InjectLogger注入Logger对象");
    }
}
```
通过在`JavaConfig`运行：
```java
@Bean
CommandLineRunner changeAnnotationBeanPostProcessorClr(DemoLoggerService demoLoggerService){
    return args -> {
        demoLoggerService.doSomething();
    };
}
```

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1663555293589/zqUuk81pS.png align="left")
### 第二种：`BeanFactoryPostProcessor`
前面我们讲了针对Bean进行处理的`BeanPostProcessor`，而`BeanFactoryPostProcessor`是针对Bean的配置元数据（注解等）进行处理操作，而这项工作属于`BeanFactory`的职责范畴。

- `ConfigurationClassPostProcessor`：使@`PropertySource`、`@ComponentScan`、`@Component`类、`@Configuration`、`@Bean`、`@Import`和`@ImportResource`注解起效；
- `EventListenerMethodProcessor`：使`@EventListener`注解起效；
我们也通过一个自定义的注解`@CustomBean`，来自己自动注册Bean，我们的`@CustomBean的作用即是配置元数据。

自定义的注解为：
```java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomBean {
}
```
使用在Bean上：
```java
@CustomBean
public class CustomBeanService {
    public void doSomething(){
        System.out.println("通过自定义的注解成功注册bean");
    }
}
```
同样，我们也实现`BeanFactoryPostProcessor`接口：
```java
@Component
public class CustomBeanDefinitionRegistryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner((BeanDefinitionRegistry) beanFactory); //1
        scanner.addIncludeFilter(new AnnotationTypeFilter(CustomBean.class)); //2
        scanner.scan("top.wisely.springfundamentals.custom_scan"); //3

    }
}
```
1. 定义一个类路径Bean定义扫描器，它的入参是`BeanDefinitionRegistry`类型，而`ConfigurableListableBeanFactory`是它的子类，可强制转换使用，当然我们可以让我们的类直接实现`BeanDefinitionRegistryPostProcessor`接口，它的方法`void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)`直接提供了`BeanDefinitionRegistry`的对象；
2. 为扫描器添加包含的注解`@CustomBean`的过滤器；
3. 在包`top.wisely.springfundamentals.custom_scan`下扫描注解；
在`JavaConfig`中注入我们自定义的Bean，因为是我们自定义的，IDE不能自动检测而显示红色，但可以正常执行：
```java
@Bean
CommandLineRunner customBeanDefinitionRegistryPostProcessorClr(CustomBeanService customBeanService){
    return args -> {
        customBeanService.doSomething();
    };
}
```

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1663555430733/v_OFleE6M.png align="left")
### 第三种：AOP
我们还可以通过基于AOP来让注解具备功能，通过拦截标注了指定注解的方法或类，然后再建言执行功能代码。如：

- `AnnotationTransactionAspect`：让`@Transactional`注解起效；
- `AnnotationCacheAspect`：让`@Cacheable`注解起效；


### 第四种：组合元注解
Spring的大部分的元注解，我们可以使用元注解到其它的注解，即用元注解（元数据）描述注解，从而使其它的注解具备元注解的功能，一般我们认为组合注解是元注解在新的语义下的特例。

- `@Component`元注解：`@Service`、`@Repository`、`@Controller`、`@Configuration`：
```java
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component //组合了@Component注解，具备了声明Bean的能力
public @interface Service {}
```
- `@Import`元注解：大量的`@Enable*`注解：
```java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class) //组合了@Import注解，具备了导入配置的能力
public @interface EnableAsync {}
```
- `@Conditional`元注解：`@Profile`以及Spring Boot的大量条件注解`@ConditionalOn*`
```java
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {}
```
### 总结
Spring框架的注解的主要工作方式大体罗列在本文，如果大家发现还有其他的Spring注解的工作方式，欢迎补充。
