写点什么

Spring 定义 BeanDefinition

作者:IT巅峰技术
  • 2022 年 4 月 09 日
  • 本文字数:6967 字

    阅读完需:约 23 分钟

一、背景

注解( 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


在开始分析之前,我们先运行一段简单的代码



@Configurationpublic class MyConfig {
@Bean public MyAnnoBean myAnnoBean(){ return new MyAnnoBean(); }}
@Slf4jpublic 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.internalConfigurationAnnotationProcessor14:04:41.566 [main] INFO org.example.springbean.AnnoMain - org.springframework.context.annotation.internalAutowiredAnnotationProcessor14:04:41.566 [main] INFO org.example.springbean.AnnoMain - org.springframework.context.annotation.internalCommonAnnotationProcessor14:04:41.566 [main] INFO org.example.springbean.AnnoMain - org.springframework.context.event.internalEventListenerProcessor14:04:41.566 [main] INFO org.example.springbean.AnnoMain - org.springframework.context.event.internalEventListenerFactory14:04:41.566 [main] INFO org.example.springbean.AnnoMain - myConfig14: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<BeanDefinitionHolder> 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<BeanDefinitionHolder> 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 等注解。


以当前例子来大致说说流程


  1. new AnnotationConfigApplicationContext

  2. (MyConfig.class);将当前设置的 MyConfig 注册进容器

  3. 注册了 ConfigurationClassPostProcessor 处理器

  4. 执行:

  5. AbstractApplicationContext.refresh()方法中的 invokeBeanFactoryPostProcessors 触发 ConfigurationClassPostProcessor 处理器,发现 MyConfig 是一个有 @Configuration 注解的配置类,解析该类,发现内部有 @Bean 注解的方法,作为待注册的对象保存在 configClasses 对象中

  6. 执行:

  7. ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(configClasses);

  8. 将所有可能的注解解析封装成 BeanDefinition 对象

  9. registry.registerBeanDefinition 方法注册到容器中

  10. 执行:

  11. 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



@Servicepublic class MyService {
@Autowired private MyDao myDao;
public void getInfo(){ myDao.getInfo(); }
}
@Componentpublic class MyDao { public void getInfo() { System.out.println("====ServiceB.getInfo()===="); }}
复制代码

五、CommonAnnotationBeanPostProcessor

它也是一个 BeanPostProcessor 处理器,用于处理 @PostConstruct 和 @PreDestroy 注解。我们顺便看看平时我们需要在初始化或者销毁实例的时候处理一些东西是如何处理的。



@Configurationpublic 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 {}
@Slf4jpublic 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 上海社区”联合创始人,曾就职于拼多多、德邦等公司,现任上市快递公司架构负责人,主要负责开发框架的搭建、中间件相关技术的二次开发和运维管理、混合云及基础服务平台的建设。

用户头像

一线架构师、二线开发、三线管理 2021.12.07 加入

Redis6.X、ES7.X、Kafka3.X、RocketMQ5.0、Flink1.X、ClickHouse20.X、SpringCloud、Netty5等热门技术分享;架构设计方法论与实践;作者热销新书《RocketMQ技术内幕》;

评论

发布
暂无评论
Spring定义BeanDefinition_IT巅峰技术_InfoQ写作平台