上节讲到了如何获取document,当把文件转换为document后,接下来的提取及注册bean就是我们的重头戏。
复制代码
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//从资源文件转换为document对象
Document doc = doLoadDocument(inputSource, resource);
//解析document,并注册beanDefiniton到工厂中
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
复制代码
在xmlBeanDefinitionReader的doLoadBeanDefinitions方法中,将document对象交给registerBeanDefinitions方法,返回本次加载的BeanDefinition个数。
复制代码
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//创建默认的documentReader,即DefaultBeanDefinitionDocumentReader,用来解析document
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//获取注册中心,记录统计前的BeanDefinition加载个数
int countBefore = getRegistry().getBeanDefinitionCount();
//加载及注册BeanDefinition
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//记录本次加载的BeanDefinition个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
复制代码
这里首先会createReaderContext(resource),代码很简单。
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
这里重点是将this对象,也就是当前的xmlBeanDefinitionReader对象放进去了,所以XmlReaderContext相当于一个上下文,方便数据的传递,类似于ServletContext,SecurityContext等。
然后将document和上下文对象交给documentReader.registerBeanDefinitions方法。
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
此处引用了之前的上下文,然后调用doRegisterBeanDefinitions,在spring中,很多以"do"开头的方法名就是真正干活的方法。
这里获取了document的根元素,进入documentReader.doRegisterBeanDefinitions方法
复制代码
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
//委托给delegate解析
this.delegate = createDelegate(getReaderContext(), root, parent);
//判断当前Beans节点是否是默认命名空间
if (this.delegate.isDefaultNamespace(root)) {
//获取beans节点的profile属性
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
//可以使用逗号或分号将当前beans标签指定为多个profile类型
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
//判断当前beans标签的profile是否被激活
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
//解析前处理,留给子类实现
preProcessXml(root);
//真正的解析过程
parseBeanDefinitions(root, this.delegate);
//解析后处理,留给子类实现
postProcessXml(root);
this.delegate = parent;
复制代码
可以先根据我的注释看看这个方法到底是在干啥,此处documentReader对象引用了一个BeanDefinitionParserDelegate,又将解析过程委托给了delegate 处理,在parseBeanDefinitions(root, this.delegate)方法中实现。
方法一开头,便有一大段英文注释,大致意思就是说,任何内嵌的beans标签,将会导致该方法的递归调用,为了正确的传播和保留<beans>标签的default属性,追踪当前delegate(即父delegete,可能为null),每次都会创建一个新的delegate(即子delegate),引用父delegate,而这个子delegate下次又会作为父delegate。
脑瓜子是不是有点嗡嗡的???但是大概也能明白这里就是为了处理beans标签的default属性的。
因为我们在配置xml文件的时候,是可以在根beans标签中嵌套beans标签的(虽然这样的写法很少,一般是写在另外一个xml文件中,然后通过import标签导入,但实际上效果是一样的),类似这样:
复制代码
<?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" default-autowire="byType">
<beans profile="test" default-autowire="byType">
<bean id="user" class="cn.zxh.po.User" >
<property name="name" value="张三"/>
</bean>
</beans>
<beans profile="dev" default-autowire="constructor">
<bean id="user" class="cn.zxh.po.User">
<property name="name" value="李四"/>
</bean>
</beans>
</beans>
复制代码
理论上没有栈溢出的情况下beans内部应该是可以无限嵌套beans的(不一定正确,还没有试过),后面会讲到每次解析到beans标签都会进入到该方法中,所以该方法可能会递归调用,每次都会创建delegate,对应一个beans标签,根据父Delegate来决定当前Delegate的默认属性。
1、创建Delegate
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
createDelegate点进入看看。
复制代码
protected BeanDefinitionParserDelegate createDelegate(
XmlReaderContext readerContext, Element root, @Nullable BeanDefinitionParserDelegate parentDelegate) {
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
//根据父delegate的默认属性,初始化当前beans的默认属性
delegate.initDefaults(root, parentDelegate);
return delegate;
}
复制代码
首先会实例化一个BeanDefinitionParserDelegate对象,该对象引用了之前的上下文readerContext,并且还引用了一个DocumentDefaultsDefinition。
private final DocumentDefaultsDefinition defaults = new DocumentDefaultsDefinition();
public BeanDefinitionParserDelegate(XmlReaderContext readerContext) {
Assert.notNull(readerContext, "XmlReaderContext must not be null");
this.readerContext = readerContext;
}
DocumentDefaultsDefinition保存了根节点的默认配置属性值,比如说default-lazyInit,default-autowire,default-initMethod,default-destroyMethod等,这几种属性应该都用过吧,如果该beans标签下的bean没有配置这些属性,就会使用beans标签的默认配置。
所以在这里,delegate引用了一个DocumentDefaultsDefinition,将来在解析各个bean标签时会起作用,然后调用initDefaults(root, parentDelegate),就是根据父delegate,初始化它自身的DefaultsDefinition。
在initDefaults方法中大概就是采用子配置优先的原则给DocumentDefaultsDefinition属性赋值的,具体就不带大家细看了,不然很容易从入门到放弃,反正经过几层方法的调用,最终进入到这个方法中,这里仅仅贴一下代码。
复制代码
protected void populateDefaults(DocumentDefaultsDefinition defaults, @Nullable DocumentDefaultsDefinition parentDefaults, Element root) {
String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
// Potentially inherited from outer <beans> sections, otherwise falling back to false.
lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE);
}
defaults.setLazyInit(lazyInit);
String merge = root.getAttribute(DEFAULT_MERGE_ATTRIBUTE);
if (isDefaultValue(merge)) {
// Potentially inherited from outer <beans> sections, otherwise falling back to false.
merge = (parentDefaults != null ? parentDefaults.getMerge() : FALSE_VALUE);
}
defaults.setMerge(merge);
String autowire = root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE);
if (isDefaultValue(autowire)) {
// Potentially inherited from outer <beans> sections, otherwise falling back to 'no'.
autowire = (parentDefaults != null ? parentDefaults.getAutowire() : AUTOWIRE_NO_VALUE);
}
defaults.setAutowire(autowire);
if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) {
defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE));
}
else if (parentDefaults != null) {
defaults.setAutowireCandidates(parentDefaults.getAutowireCandidates());
}
if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) {
defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE));
}
else if (parentDefaults != null) {
defaults.setInitMethod(parentDefaults.getInitMethod());
}
if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) {
defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE));
}
else if (parentDefaults != null) {
defaults.setDestroyMethod(parentDefaults.getDestroyMethod());
}
defaults.setSource(this.readerContext.extractSource(root));
}
复制代码
2、判断当前profile是否被激活
我们知道spring支持配置多个profile,可在beans标签的profile属性配置,然后在运行前动态指定spring.active.profile的。在java项目中,可以配置系统属性System.setProperty("spring.profiles.active","test"),web项目中配置ServletContext上下文参数指定,springboot中也可以通过spring.active.profile指定。
复制代码
//判断当前Beans节点是否是默认命名空间
if (this.delegate.isDefaultNamespace(root)) {
//获取beans节点的profile属性
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
//可以使用逗号或分号将当前beans标签指定为多个profile类型
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
//判断当前beans标签的profile是否被激活
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
复制代码
知道了怎么用,就容易多了,首先得到beans标签的指定的profile数组,与指定的spring.active.profile对比,符合条件的话改beans才会被加载。
首先通过getReaderContext().getEnvironment(),获取StandardEnvironment。这里的getReaderContext()就是获取的刚刚说的XmlReaderContext上下文,再从上下文中的得到XmlBeanDefinitionReader初始化时引用的StandardEnvironment。
下面是XmlBeanDefinitionReader继承自抽象父类的构造方法。
复制代码
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
// Determine ResourceLoader to use.
if (this.registry instanceof ResourceLoader) {
this.resourceLoader = (ResourceLoader) this.registry;
}
else {
this.resourceLoader = new PathMatchingResourcePatternResolver();
}
// Inherit Environment if possible
if (this.registry instanceof EnvironmentCapable) {
this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
}
else {
this.environment = new StandardEnvironment();
}
}
复制代码
这里的StandardEnvironment初始化会加载当前的系统变量和环境变量,是对系统变量和环境变量的封装。在StandardEnvironmen继承自父类的构造方法中,会调用customizePropertySources方法
复制代码
private final MutablePropertySources propertySources = new MutablePropertySources()
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
复制代码
该方法将当前系统变量和环境变量保存在其propertySources属性中。
复制代码
public class MutablePropertySources implements PropertySources {
private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
/**
* Create a new {@link MutablePropertySources} object.
*/
public MutablePropertySources() {
}
}
复制代码
而MutablePropertySources 有一个List属性,保存多个属性来源。
也就是说,StandardEnvironment初始化完成时,就会加载系统变量和环境变量,然后这里会调用acceptsProfiles(specifiedProfiles)方法判定当前beans标签的profile是否应该被加载,遍历给定的profiles数组,只要有一个被指定为spring.active.profile就返回true。
复制代码
public boolean acceptsProfiles(String... profiles) {
Assert.notEmpty(profiles, "Must specify at least one profile");
for (String profile : profiles) {
if (StringUtils.hasLength(profile) && profile.charAt(0) == '!') {
if (!isProfileActive(profile.substring(1))) {
return true;
}
}
else if (isProfileActive(profile)) {
return true;
}
}
return false;
}
protected boolean isProfileActive(String profile) {
validateProfile(profile);
Set<String> currentActiveProfiles = doGetActiveProfiles();
return (currentActiveProfiles.contains(profile) ||
(currentActiveProfiles.isEmpty() && doGetDefaultProfiles().contains(profile)));
}
复制代码
重点是获取spring.active.profile的方法。
复制代码
protected Set<String> doGetActiveProfiles() {
synchronized (this.activeProfiles) {
if (this.activeProfiles.isEmpty()) {
//从propertySources中获取,key为spring.active.profile
String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
if (StringUtils.hasText(profiles)) {
setActiveProfiles(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(profiles)));
}
}
return this.activeProfiles;
}
}
复制代码
3、解析根节点parseBeanDefinitions
经过了委托类的创建,spring.active.profile判断返回true的beans标签,才会进入到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) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
//默认标签的解析
parseDefaultElement(ele, delegate);
}
else {
//自定义标签的解析
delegate.parseCustomElement(ele);
}
}
}
}
else {
//自定义标签的解析
delegate.parseCustomElement(root);
}
}
复制代码
看到这里应该就一目了然了,判定root节点以及其子节点是否是默认标签,默认标签和自定义标签有不同的解析方式,除了beans、bean、alias和import四种标签外,都是自定义标签,自定义标签需要实现一些接口和配置。如果是默认标签,进入parseDefaultElement方法。
复制代码
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//解析import标签
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//解析alias标签
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//解析bean标签
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//解析beans标签
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
复制代码
具体的解析过程下一章会讲到,在这里,看看解析beans标签的时候,是不是又会调用doRegisterBeanDefinitions方法?还记得吗?这正是对之前该方法会递归调用的解释,再贴一遍代码吧。
复制代码
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
//委托给delegate解析
this.delegate = createDelegate(getReaderContext(), root, parent);
//判断当前Beans节点是否是默认命名空间
if (this.delegate.isDefaultNamespace(root)) {
//获取beans节点的profile属性
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
//可以使用逗号或分号将当前beans标签指定为多个profile类型
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
//判断当前beans标签的profile是否被激活
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
//解析前处理,留给子类实现
preProcessXml(root);
//真正的解析过程
parseBeanDefinitions(root, this.delegate);
//解析后处理,留给子类实现
postProcessXml(root);
this.delegate = parent;
}
复制代码
|
|