一、背景
注解( Annotation) 是 JDK1.5 中引入的 一个新特性。从Spring2.0以后的版本中,Spring引入了基于注解方式的配置 ,用于取代 XML 配置文件 ,从而极简化了 Bean 的配置,Spring后来的新版本。话不多说,直接上图:
在Spring Boot 中完全采用基于注解 ( Spring4.x 引 入了更加智能的@Condition 系列注解,实现 “零 XML 的配置”(当然,同时也支持之前的 XML 配置文件方式)。
我们知道,@Component注解的功能是把普通 POJO实例化到 Spring容器中,相当于配置文件中的。
在类上添加注解@Configuration,表明这个类代表一个Spring配置文件,与原来 XML配置是等效的。只不过现在用 Java类加上一个@Configuration注解进行配置了,这种方式与XML 相比可以称得上是极简风格了。同时基于注解的配置风格,使得代码的可读性也大大增高了 。
二、AnnotationConfigApplicationContext
在开始分析之前,我们先运行一段简单的代码
@Configuration public class MyConfig { @Bean public MyAnnoBean myAnnoBean(){ return new MyAnnoBean(); } }@Slf4j public class AnnoMain {
public static void main(String[] args) { AnnotationConfigApplicationContext annoContext=new AnnotationConfigApplicationContext(MyConfig.class); Arrays.stream(annoContext.getBeanDefinitionNames()).forEach(log::info); }
}
输出结果如下:
14:04:41.566 [main] INFO org.example.springbean.AnnoMain - org.springframework.context.annotation.internalConfigurationAnnotationProcessor 14:04:41.566 [main] INFO org.example.springbean.AnnoMain - org.springframework.context.annotation.internalAutowiredAnnotationProcessor 14:04:41.566 [main] INFO org.example.springbean.AnnoMain - org.springframework.context.annotation.internalCommonAnnotationProcessor 14:04:41.566 [main] INFO org.example.springbean.AnnoMain - org.springframework.context.event.internalEventListenerProcessor 14:04:41.566 [main] INFO org.example.springbean.AnnoMain - org.springframework.context.event.internalEventListenerFactory 14:04:41.566 [main] INFO org.example.springbean.AnnoMain - myConfig 14:04:41.566 [main] INFO org.example.springbean.AnnoMain - myAnnoBea
我们发现除了myConfig,myAnnoBean两个我们自己想要加载的Bean之外,Spring在后台还给我们额外注入了5个Bean。我们暂且不用全部理解他们各自具有什么作用。我们先看一下相关代码。
/**
- Register all relevant annotation post processors in the given registry.
- @param registry the registry to operate on
- @param source the configuration source element (already extracted)
- that this registration was triggered from. May be {@code null}.
- @return a Set of BeanDefinitionHolders, containing all bean definitions
- that have actually been registered by this call
*/ public static Set registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) {
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
Set beanDefs = new LinkedHashSet<>(8);
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
//a.1 处理器(@Configuration @Bean @Import @ImportResourse ImportBeanDefinitionRegistrar)
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
//a.2 处理器(@Autowired and @Value annotations.Also supports JSR-330's @Inject annotation)
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
//a.3 处理器(support for the PostConstruct and PreDestroy annotations)
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
try {
def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
AnnotationConfigUtils.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
}
def.setSource(source);
//a.4
beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
//a.5 处理器
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
//a.6
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}
return beanDefs;
}
//b. private static BeanDefinitionHolder registerPostProcessor( BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//c.将BeanDefinition注册到BeanDefinitionRegistry中
registry.registerBeanDefinition(beanName, definition);
return new BeanDefinitionHolder(definition, beanName);
}
我们发现Spring内部注册Bean就是用的registry.registerBeanDefinition(beanName, definition);方式。
三、ConfigurationClassPostProcessor
ConfigurationClassPostProcessor:
实现了BeanDefinitionRegistryPostProcessor接口。
参考该接口的注释并结合我们第一章第12种注入方式,我们可以猜到,这是个特殊的类,当它加入容器中之后势必会执行postProcessBeanDefinitionRegistry方法。
介绍一下 BeanDefinitionRegistryPostProcessor
看一下这个接口的注释
Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for the registration of further bean definitions before regular BeanFactoryPostProcessor detection kicks in. In particular, BeanDefinitionRegistryPostProcessor may register further bean definitions which in turn define BeanFactoryPostProcessor instances.
译文:
对标准BeanFactoryPostProcessor SPI的扩展,允许在常规BeanFactoryPostProcessor检测开始之前注册进一步的bean定义。特别地,BeanDefinitionRegistryPostProcessor可以注册更多的bean定义,这些定义反过来又定义了BeanFactoryPostProcessor实例。
如果我们跟进一下该方法。我们会发现它的作用就是将所有@Configuration注解的配置类去解析,然后逐步的解析:
- @PropertySource
- @ComponentScans
- @ComponentScan
- @Import
- @ImportResource
- @Bean等注解。
以当前例子来大致说说流程
new AnnotationConfigApplicationContext
(MyConfig.class);将当前设置的MyConfig注册进容器
注册了ConfigurationClassPostProcessor处理器
执行:
AbstractApplicationContext.refresh()方法中的invokeBeanFactoryPostProcessors触发ConfigurationClassPostProcessor处理器,发现MyConfig是一个有@Configuration注解的配置类,解析该类,发现内部有@Bean注解的方法,作为待注册的对象保存在configClasses对象中
执行:
ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(configClasses);
将所有可能的注解解析封装成BeanDefinition对象
registry.registerBeanDefinition方法注册到容器中
执行:
AbstractApplicationContext.finishBeanFactoryInitialization将非懒加载的单例实例化
四、AutowiredAnnotationBeanPostProcessor
AutowiredAnnotationBeanPostProcessor:
实现了MergedBeanDefinitionPostProcessor接口。
它根本上是一个BeanPostProcessor,它的作用是在Spring容器实例化的时候触发以下:
postProcessBeforeInitialization
postProcessAfterInitialization
介绍一下BeanPostProcessor
Factory hook that allows for custom modification of new bean instances, e.g. checking for marker interfaces or wrapping them with proxies.
ApplicationContexts can autodetect BeanPostProcessor beans in their bean definitions and apply them to any beans subsequently created. Plain bean factories allow for programmatic registration of post-processors, applying to all beans created through this factory.
Typically, post-processors that populate beans via marker interfaces or the like will implement postProcessBeforeInitialization, while post-processors that wrap beans with proxies will normally implement postProcessAfterInitialization.
译文:
允许对新bean实例进行自定义修改的工厂钩子,例如检查标记接口或用代理包装它们。
ApplicationContexts可以在它们的bean定义中自动检测BeanPostProcessor bean,并将它们应用于随后创建的任何bean。普通bean工厂允许以编程方式注册BeanPostProcessor处理器,应用于通过该工厂创建的所有bean。
通常,通过标记接口或类似方式填充bean的处理程序将实现postProcessBeforeInitialization,而用代理包装bean的后处理程序通常将实现postProcessAfterInitialization。
它的作用是处理@Autowired、@Value 注解.同时支持 JSR-330 规范的 @Inject注解,在Bean实例化时,在填充属性值的时候允许调用的一个入口
我们回顾一下平时最常用的Spring的功能,我们在MyService里面直接使用Spring的@Autowired注解将MyDao进行了依赖的注入。那么这是怎么实现的呢?其实它就是由我们的以下处理器来实现的:
AutowiredAnnotationBeanPostProcessor
@Service
public class MyService {
@Autowired
private MyDao myDao;
public void getInfo(){
myDao.getInfo();
}
}
@Component
public class MyDao {
public void getInfo() {
System.out.println("====ServiceB.getInfo()====");
}
}
五、CommonAnnotationBeanPostProcessor
它也是一个BeanPostProcessor处理器,用于处理@PostConstruct 和@PreDestroy 注解。
我们顺便看看平时我们需要在初始化或者销毁实例的时候处理一些东西是如何处理的。
@Configuration
public class MyInitConfig {
@Bean(initMethod = "initMethod",destroyMethod = "destroyMethod")
public MyInitBean myInitBean(){
return new MyInitBean();
}
}
public class MyInitBean implements InitializingBean, DisposableBean {
public MyInitBean() {
System.out.println("====MyInitBean()====");
}
public void initMethod(){
System.out.println("====MyInitBean.initMethod()====");
}
public void destroyMethod(){
System.out.println("====MyInitBean.destroyMethod()====");
}
@PostConstruct
public void postConstruct(){
System.out.println("====MyInitBean.postConstruct()====");
}
@PreDestroy
public void preDestroy(){
System.out.println("====MyInitBean.preDestroy()====");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("====MyInitBean.afterPropertiesSet()====");
}
@Override
public void destroy(){
System.out.println("====MyInitBean.destroy()====");
}
}
public class AnnoMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(MyInitConfig.class);
context.close();
}
}
程序运行的结果为:
====MyInitBean()====
====MyInitBean.postConstruct()====
====MyInitBean.afterPropertiesSet()====
====MyInitBean.initMethod()====
====MyInitBean.preDestroy()====
====MyInitBean.destroy()====
====MyInitBean.destroyMethod()===
我们可以发现定义初始化方法和销毁方法有3种方式
@PostConstruct,@PreDestroy
实现InitializingBean接口afterPropertiesSet方法, DisposableBean接口destroy方法
@Bean(initMethod = "initMethod",destroyMethod = "destroyMethod")(同xml配置中)
六、Spring解析文件方式加载Bean
6.1 早年使用Spring的时候是XML配置
主要是使用AbstractXmlApplicationContext提供的以下方法:loadBeanDefinitions
委托:XmlBeanDefinitionReader
执行loadBeanDefinitions解析xml配置的地址并加载为Resource,再将Resource使用SAX解析为Doc文档
6.2 使用@ImportResource注解
@Configuration
@ImportResource(locations={"beans.xml"})
public class XmlConfig {
}
@Slf4j
public class AnnoMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext annoContext=new AnnotationConfigApplicationContext(XmlConfig.class);
Arrays.stream(annoContext.getBeanDefinitionNames()).forEach(log::info);
}
}
它是由我们的ConfigurationClassPostProcessor配置类处理器委托:
ConfigurationClassBeanDefinitionReader.loadBeanDefinitions处理@ImportResource通过调用注解内设置的BeanDefinitionReader处理器去解析相应的资源文件。
Spring默认给我们实现了以下Reader:
XmlBeanDefinitionReader
GroovyBeanDefinitionReader
PropertiesBeanDefinitionReader
它们都是通过继承AbstractBeanDefinitionReader并实现loadBeanDefinitions方法来完成文件的解析以及Bean的注册。
我们完全可以自己实现一套。需要注意的是除了xml和groovy方式的配置文件可以不用指定reader,其它格式的配置必须指定如beans.properties ,若不指定会默认使用xml方式的解析器去解析。
程序员的核心竞争力其实还是技术,因此对技术还是要不断的学习,关注“IT巅峰技术” ,该公众号内容定位:中高级开发、架构师、中层管理人员等中高端岗位服务的,除了技术交流外还有很多架构思想和实战案例,作者是“《 消息中间件 RocketMQ技术内幕》 ”一书作者,同时也是“RocketMQ上海社区”联合创始人,曾就职于拼多多、德邦等公司,现任上市快递公司架构负责人,主要负责开发框架的搭建、中间件相关技术的二次开发和运维管理、混合云及基础服务平台的建设;
请关注公众号,一起探讨、学习.