BeanFactory创建过程的个人理解(一)
之前我们大概了解了一下Spring启动的过程。只是知道了一个大概。 我们也可以简化为BeanFactory创建和Bean的创建两个大步骤。
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
2
3
4
5
我们之前所总结的启动步骤,都是在BeanFactory创建的这个过程中。
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
上面的代码都在调用invokeBeanDefinitionRegistryPostProcessors();而这个方法,在默认情况下,我们可以对应为org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions方法,这其中有两个地方比较值得注意,
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
2
3
4
5
6
7
8
9
10
11
第一处,onfigurationClassUtils.checkConfigurationClassCandidate方法,分别为我们的BeanDefinition设置了属性,CONFIGURATION_CLASS_FULL或者CONFIGURATION_CLASS_LITE
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
2
3
4
5
6
第二处最重要的就是parse解析方法
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
this.deferredImportSelectorHandler.process();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
就上文的parse方法展开看,解析的东西到底是什么呢,我理解是将用注解标明的class,转换为BeanDefinition并注册到BeanFactory中承装BeanDefinition的map中去。 比较核心的解析方法如下。我们以
@Nullable
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass, filter);
}
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// Process any @ImportResource annotations
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
这个解析过程,我们根据Spring本身的注释就可以看的很清楚
- 解析内部类
- 解析@PropertySource注解
- 解析@ComponentScan注解
- 解析@Import注解
- 解析@ImportResource注解
- 解析独立的@Bean注解
- 解析接口和方法
- 解析父类
我们把上面8个过程,只当做只Class向BeanDefinition转换的过程。我们挑出一点重点:
# ComponentScan注解
之前我们简单了解过,大概的过程就是,通过@ComponentScan注解的各种参数中进行配置,尤其是其中的basePackages参数,得到需要扫描的路径,最后通过scanner将扫描出来的Class都转换为BeanDefinition,配置到BeanFactory的map中去。也就是说,我么这里的scanner和最早在外面声明的scanner并不是一个。
# Import注解
其中对于@import的解析过程中,一共有三种分歧。
- ImportSelector.class
- ImportBeanDefinitionRegistrar.clas
- 其他普通类
# ImportSelector
ImportSelector的用法我们参考一篇文章; ImportSelector用法 (opens new window)。简单来讲,我们可以通过ImportSelector接口的实现方法,在自定义我们要加入到BeanDefinitionMap中的类,就可以通过这个注解进行扫描。
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] {HelloServiceA.class.getName(), HelloServiceB.class.getName()};
}
2
3
4
看起来这是个多此一举的注解,但是如果我们使用@EnableXXX这类的注解的时候,这个接口就很有必要了。
# ImportBeanDefinitionRegistrar
而关于ImportBeanDefinitionRegistrar,我们做一个模仿mybatis的小例子。 我们知道,我们可以通过applicatonContext的register方法或者scan方法,来插手BeanDefinitionMap的工作。但是我们需要传入的一个class对象,如果是代理对象就无能力为。 通过了解ImportBeanDefinitionRegistrar这个类的用法,通过实现这个接口,可以暴露给外部我们的BeanDefinitionMap,供我们自定义实现对于map的注入,并且也能插手我们由class对象向BeanDefinition对象的过程,并且能处理代理对象。
我们趁热打铁尝试实现一个类似Mybatis的@MapperScan的小功能。 首先我们知道,我们通过@MapperScan注解设置扫描包之后,可以将我们的Dao接口交给Sping管理,变成一个Sping的bean,之后就可以在Service等组件中被@Autowired导入。
# 通过代理首先将接口转化为对象
public class ImportBeanDefinitionRegistrarInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("ImportBeanDefinitionRegistrarInvocationHandler");
return o;
}
}
2
3
4
5
6
7
ImportBeanDefinitionRegistrarDao o = (ImportBeanDefinitionRegistrarDao) Proxy.newProxyInstance(Liuyang19900520Application.class.getClassLoader(), new Class<?>[]{ImportBeanDefinitionRegistrarDao.class}, new ImportBeanDefinitionRegistrarInvocationHandler());
我们执行一下上面代码,发现我么能够打印出来,就说明动态代理对象创建成功了。
# 将BeanDefinition的beanClass设置为FactoryBean
我们怎么把这个代理对象交给Spring管理呢,这里我们就要用到FactoryBean对象了,通过这个对象,我们可以返回我们的代理对象。也就是说如果我们的BeanDefinition中的类型是FactoryBean的话,就可以实现将我们的Dao接口实例化出一个对象的操作。如果不这样做,我们在BeanDefinition中的beanClass如果还是接口的话,那么我们将无法完成Spring的Bean的实例化。
public class ImportBeanDefinitionRegistrarFactoryBean implements FactoryBean<Object> {
Class<?> clazz;
/**
* 传递一个class类型,通过这个传递过来的类型,来实现这个多态性。
* 使得这个FactoryBean能够对应多个接口,或者说能够创建出多个接口对应的对象的代理对象
* 这时候getObject传出的就是得到代理对象
*
* @param c
*/
public ImportBeanDefinitionRegistrarFactoryBean(Class<?> c) {
this.clazz = c;
}
@Override
public Object getObject() throws Exception {
//传入class类型
Class<?>[] classes = new Class<?>[]{clazz};
//创建代理对象
Object o = Proxy.newProxyInstance(this.getClass().getClassLoader(), classes, new ImportBeanDefinitionRegistrarInvocationHandler());
return o;
}
@Override
public Class<?> getObjectType() {
return clazz;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class ImportBeanDefinitionRegistrarInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("================ImportBeanDefinitionRegistrarInvocationHandler");
return null;
}
}
2
3
4
5
6
7
# ImportBeanDefinitionRegistrar来实现BeanDefinition的注册
public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//定义一个builder,定义为我们需要的这个dao的类型
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(ImportBeanDefinitionRegistrarDao.class);
//获取这个BeanDefinition
GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
//我们需要为这个BeanDefinition创建对象时的构造方法上,添加一个参数,这个参数类型是我们要传递给FactoryBean的,实现我们一个FactoryBean,能够对应多个接口的需求
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(ImportBeanDefinitionRegistrarDao.class);
//为BeanDefinition的beanClass设置为我们手写的FactoryBean,这样可以通过getObject方法可到代理对象
beanDefinition.setBeanClass(ImportBeanDefinitionRegistrarFactoryBean.class);
//注册到我们的beanMap中
registry.registerBeanDefinition("liuyang", beanDefinition);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
上面代码的注释已经写清楚了,唯一需要解释的就是为构造方法添加参数这一个地方。 如果不加参数,那么在创建bean的时候,我们从BeanDefinition取出的beanClass,就可以直接用了。但是我们发现,我们的ImportBeanDefinitionRegistrarFactoryBean需要传入一个Class,作为构造方法的参数,这就是这行代码的作用。
最后简单总结一下,通过上面的方法,我们已经能将接口以类的形式注入到Service中,这个我们的mybatis没什么两样
public class TestService {
@Autowired
@Qualifier("liuyang")
ImportBeanDefinitionRegistrarDao dao;
public void test() {
if (dao != null) {
dao.query();
System.out.println("ok");
}
}
}
2
3
4
5
6
7
8
9
10
11
12
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
annotationConfigApplicationContext.register(TestService.class);
TestService bean = annotationConfigApplicationContext.getBean(TestService.class);
bean.test();
2
3
4
当我们能够在控制台看到代理方法的日志和ok的时候,说明我们基本实现了mybatis的思路。这也是ImportBeanDefinitionRegistrar.class解析过程。
# 换个思路BeanDefinitionRegistryPostProcessor
既然我们想插手beanDefinition的创建过程,那么也就是说,只要我们通过实现接口能够得到暴露给我们的BeanDefinitionRegistry registry就可以,那么我们通过BeanDefinitionRegistryPostProcessor接口的 方法也尝试了一下,
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
//定义一个builder,定义为我们需要的这个dao的类型
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(ImportBeanDefinitionRegistrarDao.class);
//获取这个BeanDefinition
GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
//我们需要为这个BeanDefinition创建对象时的构造方法上,添加一个参数,这个参数类型是我们要传递给FactoryBean的,实现我们一个FactoryBean,能够对应多个接口的需求
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(ImportBeanDefinitionRegistrarDao.class);
//为BeanDefinition的beanClass设置为我们手写的FactoryBean,这样可以通过getObject方法可到代理对象
beanDefinition.setBeanClass(ImportBeanDefinitionRegistrarFactoryBean.class);
//注册到我们的beanMap中
registry.registerBeanDefinition("liuyang", beanDefinition);
}
2
3
4
5
6
7
8
9
10
11
12
13
事实证明和上面一样,都实现了我们的设想。
# 一些 Spring 概念的自己理解
- BeanDefinition :存放 bean 定义相关信息,有点像 java 中的 Class。
- BeanDefinitionRegistry:BeanDefinition 的注册器,由于 ApplicationContext 也实现了 Registry 的接口,所以可以实现为 beanDefinitionMaps 中添加 BeanDefinition 的功能等
- BeanDefinitionHolder:一种在 Spring 源码中便于传递的数据结构,主要属性就是 beanDefinition,beanName,aliases
- BeanPostProcessor:bean 后置处理器,可以通过实例化 BeanPostProcessor 接口,插手 bean 实例化的过程
- BeanFactoryPostProcessor:beanFactory 后置处理器,可以通过实例化 BeanPostProcessor 接口,插手 bean工厂 实例化的过程
- BeanDefinitionRegistryPostProcessor:继承了BeanFactoryPostProcessor,通过扩展方法,可以插手BeanDefinitionRegistry的实例化过程