Spring多种加载Bean⽅式解析
1 定义bean的⽅式
常见的定义Bean的⽅式有:
通过xml的⽅式,例如:
<bean id="dictionaryRelMap" class="java.util.HashMap"/>
通过注解的⽅式,在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及其主要的⼦类:
下⾯简单说⼀下各个⼦类:
1. RootBeanDefinition和ChildBeanDefinition:这2个BeanDefinition是相对的关系,⾃Spring
2.5 出来以后,已经被
GenericBeanDefinition代替。因为这样强迫我们在编写代码的时候就必须知道他们之间的关系。
2. GenericBeanDefinition: 相⽐于RootBeanDefinition和ChildBeanDefinition在定义的时候就必须硬编
码,GenericBeanDefinition的优点可以动态的为GenericBeanDefinition设置parent。
3. AnnotatedBeanDefinition:看名字就是知道是⽤来读取通过注解定义Bean。
3 通过xml⽂件定义Bean
通过xml定义Bean是最早的Spring定义Bean的⽅式。因此,怎么把xml标签解析为BeanDefinition(),⼊⼝是在
org.springframework.l.XmlBeanDefinitionReader这个类,但是实际⼲活的是在
org.springframework.l.BeanDefinitionParrDelegate。代码很多,但实际逻辑很简单,就是解析Spring定义的<bean> <property> 等标签。
4 通过@Component等Spring⽀持的注解加载Bean
如果要使⽤@Component等注解定义Bean,⼀个前提条件是:有<context:component-scan/>或者@ComponentScan注解。但这2个⽅式还是有⼀点点区别:
4.1 <context:component-scan/>
由于<context:component-scan/>是⼀个xml标签,因此是在解析xml,⽣成的类
t.annotation.ComponentScanBeanDefinitionParr,关键代码:
@Override
public BeanDefinition par(Element element, ParrContext parrContext) {
//获取ba-package标签
String baPackage = Attribute(BASE_PACKAGE_ATTRIBUTE);
baPackage = ReaderContext().getEnvironment().resolvePlaceholders(baPackage);
String[] baPackages = kenizeToStringArray(baPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
// 实际处理类是ClassPathBeanDefinitionScanner
ClassPathBeanDefinitionScanner scanner = configureScanner(parrContext, element);
//扫描baPackage下所有的类,如果有@Component等标签就是注册到Spring中
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(baPackages);
ReaderContext(), beanDefini公主英语怎么读
tions, element);
return null;
}
4.2 @ComponentScan
注解对应⽣成的类是t.annotation.ComponentScanAnnotationParr 其实最后实际⼲活的还是ClassPathBeanDefinitionScanner这个。ComponentScanAnnotationParr景观中国
类的⽣成是伴随着@Configuration这个注解处理过程中(意思说@ComponentScan必须和@Configuration
⼀起使⽤)。⽽处理@Configuration其实是
t.annotation.ConfigurationClassPostProcessor。是不是感觉有点绕。
其实简单来说,在处理@Configuration的时候3英文怎么写
发现有@ComponentScan注解,就会⽣成ComponentScanAnnotationParr去扫描@Component注解
4.3 ClassPathBeanDefinitionScanner
上⾯说到了,⽆论注解还是标签的⽅式,最后都会交给ClassPathBeanDefinitionScanner这个类来处理,这个类做的就是1.扫描baPackage下所有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 : BeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
//处理@ImportResource,⾥⾯解析xml就是上⾯说到的解析xml的XmlBeanDefinitionReader
loadBeanDefinitionsFromImportedResources(confiache
ImportedResources());
ImportBeanDefinitionRegistrars());
}
6 把BeanDefinition实例化
前⾯分别说了怎么把不同定义Bean的⽅式转换为BeanDefinition加⼊到Spring中去(确切来说是保持在BeanFactory的BeanDefinitionMap中),实例是在ApplicationContext最后阶段,关键代码在DefaultListableBeanFactory中
@Override
public void preInstantiateSingletons() throws BeansException {
for (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 (SecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
el {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
el {
getBean(beanName);
}
}
}
}
通过getBean最后最后实例的代码,在AbstractAutowireCapableBeanFactory中
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
//处理xxAware接⼝
if (SecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
invokeAwareMethods(beanName, bean);
return null;
}
}, getAccessControlContext());
}
el {
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 ? ResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
// 调⽤BeanPostProcessors#postProcessAfterInitialization
wrappedBean = applyBeanPostProcessorsAft良多趣味
erInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
从上⾯初始化可以看出,InitializeBean和BeanPostProcessors的调⽤顺序
7 总结
综上分析,Spring加载Bean其实⼤的思想都是⼀样的,先读取相关信息⽣成BeanDefinition,然后通过BeanDefinition初始化Bean。如果知道了上⾯了套路以后,就可以清楚怎么⾃定义Xml标签或者⾃定义注解向Spring中注⼊Bean。
以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希逆境的名言
望⼤家多多⽀持。