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”
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectLogger {
}
下面我们定义处理注解的
InjectLoggerAnnotationBeanPostPorcessor,我们在上节已经学习过,我们只需要实现BeanPostPorcessor接口即可:
@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;
}
}
- 指明当前类处理
@InjectLogger注解; - 通过反射机制对类的每个属性(
Field)进行处理,第一个参数是Bean的Class,第二参数是入参为Field无返回值的函数接口的Lambda实现; - 通过反射机制让当前属性可访问;
- 新建Logger的实例logger
通过反射将logger值设置到bean实例的当前属性(field)上。 我们将注解使用到其他Bean上使用:
@Component public class DemoLoggerService { @InjectLogger private Logger log; public void doSomething(){ log.info("通过自定义InjectLoggerAnnotationBeanPostPorcessor让注解@InjectLogger注入Logger对象"); } }通过在
JavaConfig运行:@Bean CommandLineRunner changeAnnotationBeanPostProcessorClr(DemoLoggerService demoLoggerService){ return args -> { demoLoggerService.doSomething(); }; }

第二种:BeanFactoryPostProcessor
前面我们讲了针对Bean进行处理的BeanPostProcessor,而BeanFactoryPostProcessor是针对Bean的配置元数据(注解等)进行处理操作,而这项工作属于BeanFactory的职责范畴。
ConfigurationClassPostProcessor:使@PropertySource、@ComponentScan、@Component类、@Configuration、@Bean、@Import和@ImportResource注解起效;EventListenerMethodProcessor:使@EventListener注解起效; 我们也通过一个自定义的注解@CustomBean,来自己自动注册Bean,我们的`@CustomBean的作用即是配置元数据。
自定义的注解为:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomBean {
}
使用在Bean上:
@CustomBean
public class CustomBeanService {
public void doSomething(){
System.out.println("通过自定义的注解成功注册bean");
}
}
同样,我们也实现BeanFactoryPostProcessor接口:
@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
}
}
- 定义一个类路径Bean定义扫描器,它的入参是
BeanDefinitionRegistry类型,而ConfigurableListableBeanFactory是它的子类,可强制转换使用,当然我们可以让我们的类直接实现BeanDefinitionRegistryPostProcessor接口,它的方法void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)直接提供了BeanDefinitionRegistry的对象; - 为扫描器添加包含的注解
@CustomBean的过滤器; - 在包
top.wisely.springfundamentals.custom_scan下扫描注解; 在JavaConfig中注入我们自定义的Bean,因为是我们自定义的,IDE不能自动检测而显示红色,但可以正常执行:@Bean CommandLineRunner customBeanDefinitionRegistryPostProcessorClr(CustomBeanService customBeanService){ return args -> { customBeanService.doSomething(); }; }

第三种:AOP
我们还可以通过基于AOP来让注解具备功能,通过拦截标注了指定注解的方法或类,然后再建言执行功能代码。如:
AnnotationTransactionAspect:让@Transactional注解起效;AnnotationCacheAspect:让@Cacheable注解起效;
第四种:组合元注解
Spring的大部分的元注解,我们可以使用元注解到其它的注解,即用元注解(元数据)描述注解,从而使其它的注解具备元注解的功能,一般我们认为组合注解是元注解在新的语义下的特例。
@Component元注解:@Service、@Repository、@Controller、@Configuration:@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component //组合了@Component注解,具备了声明Bean的能力 public @interface Service {}@Import元注解:大量的@Enable*注解:@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AsyncConfigurationSelector.class) //组合了@Import注解,具备了导入配置的能力 public @interface EnableAsync {}@Conditional元注解:@Profile以及Spring Boot的大量条件注解@ConditionalOn*@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnClassCondition.class) public @interface ConditionalOnClass {}总结
Spring框架的注解的主要工作方式大体罗列在本文,如果大家发现还有其他的Spring注解的工作方式,欢迎补充。




