Spring多种加载Bean方式解析

网友投稿 209 2023-05-21


Spring多种加载Bean方式解析

1 定义bean的方式

常见的定义Bean的方式有:

通过xml的方式,例如:

通过注解的方式,在Class上使用@Component等注解,例如

@Component

public class xxxServicer{

....

}

通过在@Configuration类下的@Bean的方式,例如

@Configuration

public class xxxConfiguration{

@Bean

public myBean myBean(){

return new myBean();

}

}

虽然这三种定义Bean的方式不一样,对应的处理细节也不一样,但是从大的逻辑上来看,都是一样。主要的流程如下图: 最关键的就是问题就是这么去找到定义Bean的方式,然后生成BeanDefinition后注册到Spring上下文中,由Spring自动创建Bean的实例。

2 BeanDefinition

BeanDefinition是一个接口,用来描述一个Bean实例,例如是SINGLETON还是PROTOTYPE,属性的值是什么,构造函数的参数是什么等。简单来说,通过一个BeanDefinition我们就可以完成一个Bean实例化。 BeanDefinition及其主要的子类:

下面简单说一下各个子类:

RootBeanDefinition和ChildBeanDefinition: 这2个BeanDefinition是相对的关系,自Spring 2.5 出来以后,已经被GenericBeanDefinition代替。因为这样强迫我们在编写代码的时候就必须知道他们之间的关系。

GenericBeanDefinition: 相比于RootBeanDefinition和ChildBeanDefinition在定义的时候就必须硬编码,GenericBeanDefinition的优点可以动态的为GenericBeanDefinition设置parent。

AnnotatedBeanDefinition:看名字就是知道是用来读取通过注解定义Bean。

3 通过xml文件定义Bean

通过xml定义Bean是最早的Spring定义Bean的方式。因此,怎么把xml标签解析为BeanDefinition(), 入口是在org.springframework.beans.factory.xml.XmlBeanDefinitionReader这个类,但是实际干活的是在org.springframework.beans.factory.xml.BeanDefinitionParserDelegate。代码很多,但实际逻辑很简单,就是解析Spring定义的 等标签 。

4 通过@Component等Spring支持的注解加载Bean

如果要使用@Component等注解定义Bean,一个前提条件是:有或者@ComponentScan注解。但这2个方式还是有一点点区别:

4.1

由于是一个xml标签,因此是在解析xml,生成的类org.springframework.context.annotation.ComponentScanBeanDefinitionParser,关键代码:

@Override

public BeanDefinition parse(Element element, ParserContext parserContext) {

//获取base-package标签

String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);

basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);

String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,

ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

// 实际处理类是ClassPathBeanDefinitionScanner

ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);

//扫描basePackage下所有的类,如果有@Component等标签就是注册到Spring中

Set beanDefinitions = scanner.doScan(basePackages);

registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

return null;

}

4.2 @ComponentScan

注解对应生成的类是org.springframework.context.annotation.ComponentScanAnnotationParser 其实最后实际干活的还是ClassPathBeanDefinitionScanner这个。ComponentScanAnnotationParser类的生成是伴随着@Configuration这个注解处理过程中(意思说@ComponentScan必须和@Configuration一起使用)。而处理@Configuration其实是org.springframework.context.annotation.ConfigurationClassPostProcessor。是不是感觉有点绕。

其实简单来说,在处理@Configuration的时候发现有@ComponentScan注解,就会生成ComponentScanAnnotationParser去扫描@Component注解

4.3 ClassPathBeanDefinitionScanner

上面说到了,无论注解还是标签的方式,最后都会交给ClassPathBeanDefinitionScanner这个类来处理,这个类做的就是1.扫描basePackage下所有class,如果有@Component等注解,读取@Component相关属性,生成ScannedGenericBeanDefinition,注册到Spring中。

5 通过@Bean方式

前面说了@ComponentScan是在@Configuration处理过程中的一环,既然@Bean注解也是必须和@Configuration一起使用,那么说明@Bean的处理也是在@Configuration中,其实最后是交给ConfigurationClassBeanDefinitionReader这个类来处理的,关键代码:

private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,

TrackedConditionEvaluator trackedConditionEvaluator) {

//如果自己是通过@Import注解定义的,那么需要把自己注册到Spring中

if (configClass.isImported()) {

registerBeanDefinitionForImportedConfigurationClass(configClass);

}

//这里就是处理方法上的@Bean

for (BeanMethod beanMethod : configClass.getBeanMethods()) {

loadBeanDefinitionsForBeanMethod(beanMethod);

}

//处理@ImportResource,里面解析xml就是上面说到的解析xml的XmlBeanDefinitionReader

loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());

loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());

}

6 把BeanDefinition实例化

前面分别说了怎么把不同定义Bean的方式转换为BeanDefinition加入到Spring中去(确切来说是保持在BeanFactory的BeanDefinitionMap中),实例是在ApplicationContext最后阶段,关键代码在DefaultListableBeanFactory中

@Override

public void preInstantiateSingletons() throws BeansException {

fhttp://or (String beanName : beanNames) {

RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);

if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {

if (isFactoryBean(beanName)) {

final FactoryBean> factory = (FactoryBean>) getBean(FACTORY_BEAN_PREFIX + beanName);

boolean isEagerInit;

if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {

isEagerInit = AccessController.doPrivileged(new PrivilegedAction() {

@Override

public Boolean run() {

return ((SmartFactoryBean>) factory).isEagerInit();

}

}, getAccessControlContext());

}

else {

isEagerInit = (factory instanceof SmartFactoryBean &&

((SmartFactoryBean>) factory).isEagerInit());

}

if (isEagerInit) {

http:// getBean(beanName);

}

}

else {

getBean(beanName);

}

}

}

}

通过getBean最后最后实例的代码,在AbstractAutowireCapableBeanFactory中

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {

//处理xxAware接口

if (System.getSecurityManager() != null) {

AccessController.doPrivileged(new PrivilegedAction() {

@Override

public Object run() {

invokeAwareMethods(beanName, bean);

return null;

}

}, getAccessControlContext());

}

else {

invokeAwareMethods(beanName, bean);

}

Object wrappedBean = bean;

if (mbd == null || !mbd.isSynthetic()) {

// 调用BeanPostProcessors#postProcessBeforeInitialization

wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

}

try {

//初始化,先判断是否是InitializingBean,

invokeInitMethods(beanName, wrappedBean, mbd);

}

catch (Throwable ex) {

throw new BeanCreationException(

(mbd != null ? mbd.getResourceDescription() : null),

beanName, "Invocation of init method failed", ex);

}

if (mbd == null || !mbd.isSynthetic()) {

// 调用BeanPostProcessors#postProcessAfterInitialization

wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

}

return wrappedBean;

}

从上面初始化可以看出,InitializeBean和BeanPostProcessors的调用顺序

7 总结

综上分析,Spring加载Bean其实大的思想都是一样的,先读取相关信息生成BeanDefinition,然后通过BeanDefinition初始化Bean。如果知道了上面了套路以后,就可以清楚怎么自定义Xml标签或者自定义注解向Spring中注入Bean。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:java实现输出字符串中第一个出现不重复的字符详解
下一篇:spring boot和mybatis集成分页插件
相关文章

 发表评论

暂时没有评论,来抢沙发吧~