把spring的源码拉下来,就可以开始学习之路了,为了走通一个过程。就拿org.springframework.context.support.ClassPathXmlApplicationContextTests#testGenericApplicationContextWithXmlBeanDefinitions 这个方法入手进行debug
@Test
public void testGenericApplicationContextWithXmlBeanDefinitions() {
// 创建context
GenericApplicationContext ctx = new GenericApplicationContext();
// 创建reader用于解析资源
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ctx);
// 加载资源并解析为BeanDefinition
reader.loadBeanDefinitions(new ClassPathResource(CONTEXT_B, getClass()));
reader.loadBeanDefinitions(new ClassPathResource(CONTEXT_C, getClass()));
reader.loadBeanDefinitions(new ClassPathResource(CONTEXT_A, getClass()));
ctx.refresh();
assertTrue(ctx.containsBean("service"));
assertTrue(ctx.containsBean("logicOne"));
assertTrue(ctx.containsBean("logicTwo"));
ctx.close();
}
方法首先创建了XMLBeanDefinitionReader用于解析资源
创建reader后调用loadBeanDefinitions()方法解析资源为BeanDefinition并且注册到factory中。
/**
* Create a new AbstractBeanDefinitionReader for the given bean factory.
* <p>If the passed-in bean factory does not only implement the BeanDefinitionRegistry
* interface but also the ResourceLoader interface, it will be used as default
* ResourceLoader as well. This will usually be the case for
* {@link org.springframework.context.ApplicationContext} implementations.
* <p>If given a plain BeanDefinitionRegistry, the default ResourceLoader will be a
* {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
* <p>If the passed-in bean factory also implements {@link EnvironmentCapable} its
* environment will be used by this reader. Otherwise, the reader will initialize and
* use a {@link StandardEnvironment}. All ApplicationContext implementations are
* EnvironmentCapable, while normal BeanFactory implementations are not.
* @param registry the BeanFactory to load bean definitions into,
* in the form of a BeanDefinitionRegistry
* @see #setResourceLoader
* @see #setEnvironment
*/
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();
}
}
我们看看创建XMLBeanDefinitionReader执行了什么,它的父类AbstractBeanDefinitionReader构造方法中,接收了一个Registry参数,并设置了这个类的registy,也就是当解析成为BeanDefinition后要用这个registry将其注册到BeanFactory中。
然后设置了resourceLoader,用于加载资源文件
/** System environment property source name: {@value}. */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
/** JVM system properties property source name: {@value}. */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
/**
* Customize the set of property sources with those appropriate for any standard
* Java environment:
* <ul>
* <li>{@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME}
* <li>{@value #SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME}
* </ul>
* <p>Properties present in {@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME} will
* take precedence over those in {@value #SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME}.
* @see AbstractEnvironment#customizePropertySources(MutablePropertySources)
* @see #getSystemProperties()
* @see #getSystemEnvironment()
*/
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
最后设置了environment,这个就是spring的环境配置了,它干了两件事,一个是加载系统环境配置,一个是加载JVM系统配置。然后说是JVM系统配置的优先级比系统环境配置要高。举个例子把,就是说你在启动容器时配置JVM参数的优先级高于配置文件的优先级,也就是JVM参数会覆盖掉配置文件里的。配置文件当然是指.properties配置文件。spring boot 现在还有.yml的配置文件。当然配置文件的形式在这里不重要。
而我们传入的是GenericApplicationContext对象。这个对象既实现了ResourceLoader,又实现了EnvironmentCapable。所以registry、resourceLoader、environment都是GenericApplicationContext对象。当然如果没有实现这些接口的话,会创建一个默认的PathMatchingResourcePatternResolver和StandardEnvironment对象
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<import resource="import1.xml"/>
<import resource="classpath:org/springframework/context/support/test/*/import2.xml"/>
<context:property-override location="org/springframework/context/support/override.properties"/>
<bean id="messageSource" class="org.springframework.context.support.StaticMessageSource"/>
<bean class="org.springframework.context.support.FactoryBeanAndApplicationListener"/>
<bean name="service" class="org.springframework.context.support.Service">
<property name="resources" value="/org/springframework/context/support/test/context*.xml"/>
</bean>
<bean name="service2" class="org.springframework.context.support.Service" autowire="byName" depends-on="service">
<property name="resources" value="/org/springframework/context/support/test/context*.xml"/>
</bean>
<bean name="autowiredService" class="org.springframework.context.support.AutowiredService" autowire="byName"/>
<bean name="autowiredService2" class="org.springframework.context.support.AutowiredService" autowire="byType"/>
<bean name="wrappedAssemblerOne" class="org.springframework.context.support.TestProxyFactoryBean">
<property name="target" ref="assemblerOne"/>
</bean>
<bean name="wrappedAssemblerTwo" class="org.springframework.context.support.TestProxyFactoryBean">
<property name="target" ref="assemblerTwo"/>
</bean>
</beans>
/**
* Load bean definitions from the specified XML file.
* @param encodedResource the resource descriptor for the XML file,
* allowing to specify an encoding to use for parsing the file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
// 获取资源的输入流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
// 构建资源
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 解析资源
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
// 关闭流
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
// 解析完成后移除资源
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
然后调用 loadBeanDefinitions() 方法加载为BeanDefinition对象。具体的工作是在XmlBeanDefinitionReader#loadBeanDefinitions(EncodedResource)中进行的。
在XmlBeanDefinitionReader的首先做的是将当前资源保存到资源集合中,解析完成后再移除。然后做解析的准备工作。在最后调用doLoadBeanDefinitions()方法进行解析。
doLoadBeanDefinitions()主要干了两件事,一个是将资源解析为Document文档对象,然后注册BeanDefinition。
/**
* Register the bean definitions contained in the given DOM document.
* Called by {@code loadBeanDefinitions}.
* <p>Creates a new instance of the parser class and invokes
* {@code registerBeanDefinitions} on it.
* @param doc the DOM document
* @param resource the resource descriptor (for context information)
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of parsing errors
* @see #loadBeanDefinitions
* @see #setDocumentReaderClass
* @see BeanDefinitionDocumentReader#registerBeanDefinitions
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 创建reader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 保存已经注册的BeanDefinition数量
int countBefore = getRegistry().getBeanDefinitionCount();
// 解析Document对象为BeanDefinition
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
在registerBeanDefinitions()方法中首先创建了一个BeanDefinitionDocumentReader对象,实际上就是DefaultBeanDefinitionDocumentReader对象。然后在第524行调用DefaultBeanDefinitionDocumentReader#registerBeanDefinitions注册
上图的调用过程在此就不详细解析了。直接上图,以后填坑。
注册BeanDefinition调用的是DefaultListableBeanFactory#registerBeanDefinition方法。DefaultListableBeanFactory可以说是默认的BeanFactory实现类了。
下图是DefaultListableBeanFactory实现的接口和继承的类,大概能知道这个BeanFactory有什么功能了。
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
// 打日志
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
// 打日志
}
}
else {
if (logger.isTraceEnabled()) {
// 打日志
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
DefaultListableBeanFactory#registerBeanDefinition方法的大概调用过程就是这样了。被注释为打日志的那一部分的代码被我删掉了。因为占位置。
这段代码挺简单易懂的。大概就是先检查是否存在这个BeanDefinition,如果存在又允许重写的话,就重新覆盖之前的BeanDefinition。不然的话就抛异常了。
不存在的话就先检查是否有bean正在实例化。有的话就先对beanDefinitionMap加锁,防止bean实例化过程出错,然后放进beanDefinitionMap,更新beanDefinitionNames和manualSingletonNames,至于为什么要先重新拷贝一份呢,我想应该是为了防止并发导致的一些问题吧。没有的话直接操作。
最后,如果已经存在了BeanDefinition或者存在这个单例,就要重置一下里面的缓存。因为这个BeanDefinition有可能是beanDefinitionMap中其他BeanDefinition的BeanDefinition,所以都要刷新一次。