文章目录
1 前言
根据配置Bean方式不同,在定义域解析注册阶段,会执行不同的流程。下面给出根据不同配置方式,执行的不同流程图:
2 第一阶段-Bean信息配置阶段
2.1 配置方式
Bean信息配置(定义)有4种方式:
- 注解方式:@Component、@Configuration@Bean等等
- xml文件方式:xxx.xml
- API方式:通过BeanDefinitionBuilder工具类提供的各种API完成
- properties文件方式:不常用,这里不详述,感兴趣的自行查阅相关文档。
2.2 配置信息
具体可以配置那些信息,在下面API配置打印BeanDefiniton信息后,我们在详细说明。
2.3 配置示例
下面给出一个简单的应用场景,现在我们需要把一个User(用户)信息存入数据库,按照三层架构,这里省略Controller,只给出UserService和UserDao。分别通过不同的方式配置UserDao和UserService的bean。
User类:
public class User {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
2.3.1 注解方式配置
-
UserDao
@Repository public class UserDao { public void insert(User user) { System.out.println("数据库正在插入数据!: " + user); } }
-
UserService
@Service public class UserService { @Autowired private UserDao userDao; public void insert(User user) { userDao.insert(user); } }
2.3.2 xml文件配置
在UserService需要给属性userDao添加set方法
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
xml文件放置在类路径resources下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.gaogzhen.myspring.dao.UserDao"/>
<bean id="userService" class="com.gaogzhen.myspring.service.UserService">
<property name="userDao" ref="userDao"/>
</bean>
</beans>
2.3.3 API方式
这里只做简单示范,在讲解完BeanDefinition注册后,单独讲解API方式详细配置,包括设置简单类型,设置引用类型,设置parent等等。
public void testApiConfig() {
BeanDefinitionBuilder userDaoBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserDao.class);
Definition beanDefinition = userDaoBuilder.getBeanDefinition();
System.out.println("userDao 的配置信息:" + beanDefinition);
BeanDefinitionBuilder userServiceBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserService.class)
.addPropertyReference("userDao", "userDao");
BeanDefinition serviceBeanDefinition = userServiceBuilder.getBeanDefinition();
System.out.println("userService 的配置信息:" + serviceBeanDefinition);
}
输出信息如下:
userDao 的配置信息:Generic bean: class [com.gaogzhen.myspring.dao.UserDao]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
userService 的配置信息:Generic bean: class [com.gaogzhen.myspring.service.UserService]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
bean可配置信息:
- class:类
- scope:作用域,单例或者多例;在web应用中,还可以配置request,session等。
- abstract:是否是抽象的
- lazyInit:是否懒惰初始化
- autowireMode:自动注入模式
- AUTOWIRE_NO:不自动注入
- AUTOWIRE_BY_NAME:通过bean名称自动注入(需要提供set方法)
- AUTOWIRE_BY_TYPE:通过类型自动注入(需要提供set方法)
- AUTOWIRE_CONSTRUCTOR:通过构造函数自动注入
- AUTOWIRE_AUTODETECT:自动检测注入方式
- dependencyCheck:依赖检查
- DEPENDENCY_CHECK_NONE:不检查
- DEPENDENCY_CHECK_OBJECTS:检查对象引用
- DEPENDENCY_CHECK_SIMPLE:检查简单类型属性
- autowireCandidate:是否是自动注入其他bean的候补,默认true。只影响根据类型的主动注入,不影响根据名称的自动注入。
- primary:是否是自动注入主要候补
- factoryBeanName:工厂bean名称
- factoryMethodName:工厂bean方法名称
- initMethodName:自定义初始化bean方法名称
- destroyMethodName:自定义销毁bean方法名称
问题::
- 为什么在Service的BeanDefiniton打印信息中,没有关于userDao属性的相关信息呢?
- Bean与BeanDefinition的关系
在文章的最后统一给出解答。
3 第二阶段-Bean元信息解析阶段
解析过程就是把Bean元信息封装为BeanDefinition的过程。
因为API方式配置不同,相应的解析方式也不同。这里只讲解下XML文件和注解方式的解析,properties文件自行查阅相关文档。
- 不同配置方式解析所需要的类
- 注解:AnnotatedBeanDefinitionReader或者 ClassPathBeanDefinitionScanner
- xml文件:XmlBeanDefinitionReader
- properties文件:PropertiesBeanDefinitionReader
- 说明:
- 在new解析对象时,都需要一个BeanDefinitionRegister注册器类型的对象
- 都是通过某个方法,在调用链中完成了解析和注册,并没有单独的解析注册接口(方法)。
3.1 注解方式解析
3.1.1 AnnotatedBeanDefinitionReader解析过程
以配置阶段注解配置示例为例,看下AnnotatedBeanDefinitionReader如何解析,示例代码如下:
public void testAnnotateParse() {
//定义一个spring容器,这个容器默认实现了BeanDefinitionRegistry,所以本身就是一个bean注册器
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//定义一个注解方式的BeanDefinition读取器,需要传递一个BeanDefinitionRegistry(bean注册器)对象
AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(factory);
// 解析注册
annotatedBeanDefinitionReader.register(UserDao.class, UserService.class);
//打印出注册的bean的配置信息
for (String beanName : new String[]{"userDao", "userService"}) {
//通过名称从容器中获取对应的BeanDefinition信息
BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
//获取BeanDefinition具体使用的是哪个类
String beanDefinitionClassName = beanDefinition.getClass().getName();
//通过名称获取bean对象
Object bean = factory.getBean(beanName);
//打印输出
System.out.println(beanName + ":");
System.out.println(" beanDefinitionClassName:" + beanDefinitionClassName);
System.out.println(" beanDefinition:" + beanDefinition);
System.out.println(" bean:" + bean);
}
}
也就是说解析注册通过AnnotatedBeanDefinitionReader的register()方法完成。下面我们通过源码来追踪下,经过了那些步骤(操作),解析出BeanDefinition。
-
regist()调用registerBean()
public void register(Class<?>... componentClasses) { for (Class<?> componentClass : componentClasses) { registerBean(componentClass); } }
-
registerBean()调用doRegisterBean()
public void registerBean(Class<?> beanClass) { doRegisterBean(beanClass, null, null, null, null); }
-
doRegisterBean()方法第一行:AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);通过AnnotatedGenericBeanDefinition构造方法new一个BeanDefinition对象。
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier, @Nullable BeanDefinitionCustomizer[] customizers) { AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass); // ...省略其他代码 }
3.1.2 ClassPathBeanDefinitionScanner解析过程
下面看下ClassPathBeanDefinitionScanner解析实例代码:
ClassPathBeanDefinitionScanner beanDefinitionScanner = new ClassPathBeanDefinitionScanner(factory);
beanDefinitionScanner.scan("com.gaogzhen.myspring.dao", "com.gaogzhen.myspring.service");
其他代码同上,这里调用scan()完成解析注册。下面通过源码跟踪下怎么完成解析的,我们只关注解析部分,其他暂时忽略。
-
调用scan()方法里面doScan()方法
public int scan(String... basePackages) { int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); doScan(basePackages); // Register annotation config processors, if necessary. if (this.includeAnnotationConfig) { AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); }
-
276行doScan()执行findCandidateComponents()解析得到BeanDefinition集合
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { Set<BeanDefinition> candidates = findCandidateComponents(basePackage); //...省略其他代码 }
-
findCandidateComponents()判断如果有候选组件索引调用addCandidateComponentsFromIndex();否则调用scanCandidateComponents()。大部分情况下,没有开启候选组件索引生成,所以我们追踪下下面的方法。
public Set<BeanDefinition> findCandidateComponents(String basePackage) { if (this.componentsIndex != null && indexSupportsIncludeFilters()) { // 候选组件索引不为空且支持包含的过滤器 // 通过生成的候选组件索引添加候选组件 return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); } else { // 扫描包路径 return scanCandidateComponents(basePackage); } }
-
scanCandidateComponents()方法为解析生成BeanDefinition的核心方法
private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); for (Resource resource : resources) { if (traceEnabled) { logger.trace("Scanning " + resource); } try { MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setSource(resource); if (isCandidateComponent(sbd)) { if (debugEnabled) { logger.debug("Identified candidate component class: " + resource); } candidates.add(sbd); } //省略日志及异常处理 return candidates; }
-
执行流程
-
转换包路径为绝对路径
- basePacke->packageSearchPath->绝对路径: 形式 “com.gaogzhen.myspring.dao”==>classpath*:com/gaogzhen/myspring/dao/**/*.class==>“K:/projects/java/framework/spring6/spring6-006-cyclelife/target/classes/com/gaogzhen/myspring/dao/UserDao.class”
-
通过资源解析器包装路径下的class为Resource对象
-
通过MetadataReaderFactory传递resource创建对应的MetadataReader
- MetadataReader:访问类元数据信息
-
isCandidateComponent()判断是否是候选组件
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return false; } } for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } return false; }
-
判断如果和exclue过滤器内类型一致返回false;和include过滤器中类型一致返回true。
-
exclue过滤器默认为空;include过滤器中默认添加了Component.class,jakarta.annotation.ManagedBean,jakarta.inject.Named三种类型
protected void registerDefaultFilters() { this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("jakarta.annotation.ManagedBean", cl)), false)); logger.trace("JSR-250 'jakarta.annotation.ManagedBean' found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-250 1.1 API (as included in Jakarta EE) not available - simply skip. } try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("jakarta.inject.Named", cl)), false)); logger.trace("JSR-330 'jakarta.inject.Named' annotation found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } }
-
-
实际执行的match()方法为AbstractTypeHierarchyTraversingFilter中的
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { // This method optimizes avoiding unnecessary creation of ClassReaders // as well as visiting over those readers. if (matchSelf(metadataReader)) { return true; } ClassMetadata metadata = metadataReader.getClassMetadata(); if (matchClassName(metadata.getClassName())) { return true; } if (this.considerInherited) { String superClassName = metadata.getSuperClassName(); if (superClassName != null) { // Optimization to avoid creating ClassReader for super class. Boolean superClassMatch = matchSuperClass(superClassName); if (superClassMatch != null) { if (superClassMatch.booleanValue()) { return true; } } else { // Need to read super class to determine a match... try { if (match(metadata.getSuperClassName(), metadataReaderFactory)) { return true; } } catch (IOException ex) { if (logger.isDebugEnabled()) { logger.debug("Could not read super class [" + metadata.getSuperClassName() + "] of type-filtered class [" + metadata.getClassName() + "]"); } } } } } if (this.considerInterfaces) { for (String ifc : metadata.getInterfaceNames()) { // Optimization to avoid creating ClassReader for super class Boolean interfaceMatch = matchInterface(ifc); if (interfaceMatch != null) { if (interfaceMatch.booleanValue()) { return true; } } else { // Need to read interface to determine a match... try { if (match(ifc, metadataReaderFactory)) { return true; } } catch (IOException ex) { if (logger.isDebugEnabled()) { logger.debug("Could not read interface [" + ifc + "] for type-filtered class [" + metadata.getClassName() + "]"); } } } } } return false; }
-
先判断注解类型是否匹配
- 示例中匹配这里
-
在判断类名是否匹配
-
在判断是否是继承匹配
-
在判断是否是接口匹配
-
-
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);new新的ScannedGenericBeanDefinition类型的BeanDefinition。解析完成。
-
-
思考:
- 候选组件索引是啥东西?
3.2 xml文件解析
通过XmlBeanDefinitionReader解析xml中配置的bean为对应的BeanDefinition,以配置中xml文件为例,下面给出解析实例代码:
public void testXmlParse() {
//定义一个spring容器,这个容器默认实现了BeanDefinitionRegistry,所以本身就是一个bean注册器
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
// XmlBeanDefinitionReader加载xml配置文件,解析生成BeanDefinition注册到容器中
String location = "classpath:config.xml";
int beanCount = reader.loadBeanDefinitions(location);
System.out.println(String.format("共注册了 %s 个bean", beanCount));
//打印出注册的bean的配置信息
for (String beanName : factory.getBeanDefinitionNames()) {
//通过名称从容器中获取对应的BeanDefinition信息
BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
//获取BeanDefinition具体使用的是哪个类
String beanDefinitionClassName = beanDefinition.getClass().getName();
//通过名称获取bean对象
Object bean = factory.getBean(beanName);
//打印输出
System.out.println(beanName + ":");
System.out.println(" beanDefinitionClassName:" + beanDefinitionClassName);
System.out.println(" beanDefinition:" + beanDefinition);
System.out.println(" bean:" + bean);
}
}
输出:
2023-03-08 09:43:06 521 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [config.xml]
共注册了 2 个bean
2023-03-08 09:43:06 526 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userDao'
userDao:
beanDefinitionClassName:org.springframework.beans.factory.support.GenericBeanDefinition
beanDefinition:Generic bean: class [com.gaogzhen.myspring.dao.UserDao]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [config.xml]
bean:com.gaogzhen.myspring.dao.UserDao@55342f40
2023-03-08 09:43:06 536 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
userService:
beanDefinitionClassName:org.springframework.beans.factory.support.GenericBeanDefinition
beanDefinition:Generic bean: class [com.gaogzhen.myspring.service.UserService]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [config.xml]
bean:com.gaogzhen.myspring.service.UserService@306851c7
XmlBeanDefinitionReader调用loadBeanDefinitions()完成了xml配置文件的加载解析和注册,下面我们通过源码追踪下解析为BeanDefinition的流程。在追踪过程中其他非关键步骤,我们直接省略。
调用AbstractBeanDefinitionReader的loadBeanDefinitions()
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
继续调用loadBeanDefinitions()
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
// 资源加载器
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources);
// 省略
}
int count = loadBeanDefinitions(resources);继续调用loadBeanDefinitions()
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int count = 0;
for (Resource resource : resources) {
// 继续调用
count += loadBeanDefinitions(resource);
}
return count;
}
调用loadBeanDefinitions()回到XMLBeanDefinition的loadBeanDefinitions()方法
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
分析:
- EncodedResource :组合字符编码或者字符集,用于从resource读取信息
调用loadBeanDefinitions()方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
// 省略
// 继续调用
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
// 省略
}
分析:
- ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded 这个用于当前线程保存已加载resource
- 在finally中使用完之后做了移除
继续调用doLoadBeanDefinitions()
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// Document为解析xml文件后封装的对象
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
// 省略异常处理代码
}
分析:
- Document:HTML或者XML文档接口,表示文档树的根,提供访问数据的方法;文档中包含元素、文本结点、注释等等。
继续调用registerBeanDefinitions()
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
继续调用DefaultBeanDefinitionDocumentReader的registerBeanDefinitions()方法
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
调用doRegisterBeanDefinitions()
protected void doRegisterBeanDefinitions(Element root) {
// 省略
// preProcessXml为空方法,由子类去实现
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
// preProcessXml为空方法,由子类去实现
postProcessXml(root);
this.delegate = parent;
}
继续调用parseBeanDefinitions()
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element ele) {
// 判断结点是否是Element
if (delegate.isDefaultNamespace(ele)) {
// 判断是不是默认,即命名空间是不是"http://www.springframework.org/schema/beans"
parseDefaultElement(ele, delegate);
}
else {
// 自定义
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
分析:
-
一般我们直接使用spring提供标准的xml beans配置
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
继续调用parseDefaultElement()
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
// <bean></bean>标签对应的元素
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// <beans></beans>标签对应的元素,会递归调用
doRegisterBeanDefinitions(ele);
}
}
继续调用processBeanDefinition()
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
// 省略注册相关
}
继续调用BeanDefinitionParserDelegate的parseBeanDefinitionElement()方法
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
继续调用parseBeanDefinitionElement()
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);
// 省略
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
// 省略设置别名等操作
}
分析:id这里获取<bean id=“xxx”>中的id,即beanName
继续调用parseBeanDefinitionElement()
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
// 省略异常处理
return null;
}
哎总算找到了封装BeanDefinition的地方。这里获取并设置BeanDefinition信息,比如className,parent,等等。
总结,解析封装BeanDefinition的过程:
- 加载读取xml文件为Docment文档对象;
- 递归获取Bean标签对应的Element元素;
- 从Element元素获取BeanDefinition所需信息。
3.3 注解解析与xml解析比较
- AnnotatedBeanDefinitionReader,传递Class对象,通过Class对象及其注解获取BeanDefinition信息,封装为AnnotatedGenericBeanDefinition对象。
- ClassPathBeanDefinitionScanner需要扫描包下面.class文件,完成class加载,完成注解类型校验后获取BeanDefinition信息封装为ScannedGenericBeanDefinition对象。
- XmlBeanDefinitionReader加载校验解析xml文件中bean,获取信息封装为GenericBeanDefinition对象。
- XmlBeanDefinitionReader过程更为繁琐复杂;ClassPathBeanDefinitionScanner在没有开启候选组件索引生成的情况下不建议使用,原因在解答候选组件索引这个内容的时候给出;更为推荐AnnotatedBeanDefinitionReader解析Bean。
4 问题解答
- 为什么在Service的BeanDefiniton打印信息中,没有关于userDao属性的相关信息呢?
- Bean与BeanDefinition的关系
这两个问题在理解了Bean和BeanDefinition关系之后,就很容易理解,参考下面的[2]。
- 候选组件索引?
参考下面[3]
后记
❓QQ:806797785
⭐️源代码仓库地址:https://gitee.com/gaogzhen/spring6-study
参考:
[1]Spring系列之Bean生命周期详解[CP/OL].
[2]spring源码分析系列2:Bean与BeanDefinition关系[CP/OL].
[2]Spring 5 启动性能优化之 @Indexed[CP/OL].