Spring 解析xml中的 BeanDefination 大概过程
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean name="person" class="com.fll.model.Person" init-method="initPerson" destroy-method="initPerson" >
<property name = "name" value = "zhangsan"/>
<property name = "age" value = "20"/>
</bean>
</beans>
package com.fll.model;
public class Person {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public void initPerson(){
System.out.println("Person.initPerson()");
}
public void destroyPerson(){
System.out.println("Person.destroyPerson()");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
这里就用上面的简单例子大概说一下流程,复杂的也就是 <bean> 中的属性多一点(这里只是最基本的 (name="person" class="com.fll.model.Person" init-method="initPerson" destroy-method="destroyPerson"),<bean> 中嵌套的子标签类型和数量多一点(这里只有两个子标签 (<property name = "name" value = "zhangsan"/> <property name = "age" value = "20"/> )
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Object person = context.getBean("person");
System.out.println(person);
}
/**
* Create a new ClassPathXmlApplicationContext, loading the definitions
* from the given XML file and automatically refreshing the context.
* @param configLocation resource location
* @throws BeansException if context creation failed
*/
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
/**
* Create a new ClassPathXmlApplicationContext with the given parent,
* loading the definitions from the given XML files.
* @param configLocations array of resource locations
* @param refresh whether to automatically refresh the context,
* loading all bean definitions and creating all singletons.
* Alternatively, call refresh manually after further configuring the context.
* @param parent the parent context
* @throws BeansException if context creation failed
* @see #refresh()
*/
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
//这里根据java对象的实例化流程,构造方法的第一行必须先通过super()调用父类的构造方法
//其实这里通过一层层的父类构造方法的调用,最主要就是创建了父类
//AbstractApplicationContext 中的 resourcePatternResolver
super(parent);
// 设置要加载的配置文件,需要从配置文件中解析出定义好的 BeanDefination
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
/**
* Create a new AbstractXmlApplicationContext with the given parent context.
* @param parent the parent context
*/
public AbstractXmlApplicationContext(@Nullable ApplicationContext parent) {
super(parent);
}
/**
* Create a new AbstractRefreshableConfigApplicationContext with the given parent context.
* @param parent the parent context
*/
public AbstractRefreshableConfigApplicationContext(@Nullable ApplicationContext parent) {
super(parent);
}
/**
* Create a new AbstractRefreshableApplicationContext with the given parent context.
* @param parent the parent context
*/
public AbstractRefreshableApplicationContext(@Nullable ApplicationContext parent) {
super(parent);
}
/**
* Create a new AbstractApplicationContext with the given parent context.
* @param parent the parent context
*/
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this();
setParent(parent);
}
可以看到一直到这里其实没有什么复杂的东西,就是一直通过super() 调用多层父类的构造方法,这里的 this() 中会开始创建 容器启动需要的第一个组件
/**
* Create a new AbstractApplicationContext with no parent.
*/
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
/**
* Return the ResourcePatternResolver to use for resolving location patterns
* into Resource instances. Default is a
* {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver},
* supporting Ant-style location patterns.
* <p>Can be overridden in subclasses, for extended resolution strategies,
* for example in a web environment.
* <p><b>Do not call this when needing to resolve a location pattern.</b>
* Call the context's {@code getResources} method instead, which
* will delegate to the ResourcePatternResolver.
* @return the ResourcePatternResolver for this context
* @see #getResources
* @see org.springframework.core.io.support.PathMatchingResourcePatternResolver
*/
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
这里创建的 PathMatchingResourcePatternResolver 使用功能比较强大的资源加载器,支持按照 ant 风格的表达式,加载匹配到的所有资源。一下是这个类的注释,注释中包含我们常用的一些资源的格式,可以从注释中了解到每一种类型的资源格式的加载范围。
A ResourcePatternResolver implementation that is able to resolve a specified resource location path into one or more matching Resources. The source path may be a simple path which has a one-to-one mapping to a target Resource, or alternatively may contain the special "classpath*:" prefix and/or internal Ant-style regular expressions (matched using Spring's AntPathMatcher utility). Both of the latter are effectively wildcards.
No Wildcards:
In the simple case, if the specified location path does not start with the "classpath*:" prefix, and does not contain a PathMatcher pattern, this resolver will simply return a single resource via a getResource() call on the underlying ResourceLoader. Examples are real URLs such as "file:C:/context.xml", pseudo-URLs such as "classpath:/context.xml", and simple unprefixed paths such as "/WEB-INF/context.xml". The latter will resolve in a fashion specific to the underlying ResourceLoader (e.g. ServletContextResource for a WebApplicationContext).
Ant-style Patterns:
When the path location contains an Ant-style pattern, e.g.:
/WEB-INF/*-context.xml
com/mycompany/**/applicationContext.xml
file:C:/some/path/*-context.xml
classpath:com/mycompany/**/applicationContext.xml
the resolver follows a more complex but defined procedure to try to resolve the wildcard. It produces a Resource for the path up to the last non-wildcard segment and obtains a URL from it. If this URL is not a "jar:" URL or container-specific variant (e.g. "zip:" in WebLogic, "wsjar" in WebSphere", etc.), then a java.io.File is obtained from it, and used to resolve the wildcard by walking the filesystem. In the case of a jar URL, the resolver either gets a java.net.JarURLConnection from it, or manually parses the jar URL, and then traverses the contents of the jar file, to resolve the wildcards.
Implications on portability:
If the specified path is already a file URL (either explicitly, or implicitly because the base ResourceLoader is a filesystem one, then wildcarding is guaranteed to work in a completely portable fashion.
If the specified path is a classpath location, then the resolver must obtain the last non-wildcard path segment URL via a Classloader.getResource() call. Since this is just a node of the path (not the file at the end) it is actually undefined (in the ClassLoader Javadocs) exactly what sort of a URL is returned in this case. In practice, it is usually a java.io.File representing the directory, where the classpath resource resolves to a filesystem location, or a jar URL of some sort, where the classpath resource resolves to a jar location. Still, there is a portability concern on this operation.
If a jar URL is obtained for the last non-wildcard segment, the resolver must be able to get a java.net.JarURLConnection from it, or manually parse the jar URL, to be able to walk the contents of the jar, and resolve the wildcard. This will work in most environments, but will fail in others, and it is strongly recommended that the wildcard resolution of resources coming from jars be thoroughly tested in your specific environment before you rely on it.
classpath*: Prefix:
There is special support for retrieving multiple class path resources with the same name, via the "classpath*:" prefix. For example, "classpath*:META-INF/beans.xml" will find all "beans.xml" files in the class path, be it in "classes" directories or in JAR files. This is particularly useful for autodetecting config files of the same name at the same location within each jar file. Internally, this happens via a ClassLoader.getResources() call, and is completely portable.
The "classpath*:" prefix can also be combined with a PathMatcher pattern in the rest of the location path, for example "classpath*:META-INF/*-beans.xml". In this case, the resolution strategy is fairly simple: a ClassLoader.getResources() call is used on the last non-wildcard path segment to get all the matching resources in the class loader hierarchy, and then off each resource the same PathMatcher resolution strategy described above is used for the wildcard subpath.
Other notes:
WARNING: Note that "classpath*:" when combined with Ant-style patterns will only work reliably with at least one root directory before the pattern starts, unless the actual target files reside in the file system. This means that a pattern like "classpath*:*.xml" will not retrieve files from the root of jar files but rather only from the root of expanded directories. This originates from a limitation in the JDK's ClassLoader.getResources() method which only returns file system locations for a passed-in empty String (indicating potential roots to search). This ResourcePatternResolver implementation is trying to mitigate the jar root lookup limitation through URLClassLoader introspection and "java.class.path" manifest evaluation; however, without portability guarantees.
WARNING: Ant-style patterns with "classpath:" resources are not guaranteed to find matching resources if the root package to search is available in multiple class path locations. This is because a resource such as
com/mycompany/package1/service-context.xml
may be in only one location, but when a path such as
classpath:com/mycompany/**/service-context.xml
is used to try to resolve it, the resolver will work off the (first) URL returned by getResource("com/mycompany");. If this base package node exists in multiple classloader locations, the actual end resource may not be underneath. Therefore, preferably, use "classpath*:" with the same Ant-style pattern in such a case, which will search all class path locations that contain the root package.
/**
* Create a new PathMatchingResourcePatternResolver.
* <p>ClassLoader access will happen via the thread context class loader.
* @param resourceLoader the ResourceLoader to load root directories and
* actual resources with
*/
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
this.resourceLoader = resourceLoader;
}
可以看到创建 PathMatchingResourcePatternResolver需要一个ResourceLoader的实例,但是在org.springframework.context.support.AbstractApplicationContext#getResourcePatternResolver
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
创建的PathMatchingResourcePatternResolver实例的时候却传的是 this
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
这是因为 AbstractApplicationContext 继承了 DefaultResourceLoader,DefaultResourceLoader是ResourceLoader的一个默认实现,所以AbstractApplicationContext也具有DefaultResourceLoader能力,但是DefaultResourceLoader只支持加载单个资源。这里的ResourcePatternResolver 这个组件主要是在后面根据我们传入的资源配置表达式加载匹配到的所有资源,用于解析我们配置的所有组件信息,为解析封装BeanDefinnation做准备。
创建完ResourcePatternResolver用来加载配置文件,接下来就该设置需要加载的配置文件
/**
* Create a new ClassPathXmlApplicationContext with the given parent,
* loading the definitions from the given XML files.
* @param configLocations array of resource locations
* @param refresh whether to automatically refresh the context,
* loading all bean definitions and creating all singletons.
* Alternatively, call refresh manually after further configuring the context.
* @param parent the parent context
* @throws BeansException if context creation failed
* @see #refresh()
*/
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
//这里根据java对象的实例化流程,构造方法的第一行必须先通过super()调用父类的构造方法
//其实这里通过一层层的父类构造方法的调用,最主要就是创建了父类
//AbstractApplicationContext 中的 resourcePatternResolver
super(parent);
// 设置要加载的配置文件,需要从配置文件中解析出定义好的 BeanDefination
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
/**
* Set the config locations for this application context.
* <p>If not set, the implementation may use a default as appropriate.
*/
public void setConfigLocations(@Nullable String... locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
/**
* Resolve the given path, replacing placeholders with corresponding
* environment property values if necessary. Applied to config locations.
* @param path the original file path
* @return the resolved file path
* @see org.springframework.core.env.Environment#resolveRequiredPlaceholders(String)
*/
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}
resolvePath() 的主要逻辑就是解析配置文件路径中的表达式,比如有一个配置的格式是 beans-${currActive}.xml,并且 (Map) System.getProperties() 和 (Map) System.getenv()包含 currActive 这个key,并且value为test,那么beans-${currActive}.xml就会被解析为beans-test.xml ,一下代码验证一下
public static void main(String[] args) {
// 这里提前给设置好 要解析的值
System.setProperty("currActive" , "test");
ApplicationContext context = new ClassPathXmlApplicationContext("beans-${currActive}.xml");
Object person = context.getBean("person");
System.out.println(person);
}
这里用debug进行验证,可以看到原来是beans-${currActive}.xml,经过resolvePath()的解析以后变成了 beans-test.xml ,这样我们就可以在启动时,通过添加不同的 systemProperty 或者配置不同的环境变量动态改变要加载配置文件
设置好 resourcePatternResolver 和 配置文件的路径 之后就可以开始加载配置文件资源,将其中配置的组件解析为BeanDefination
org.springframework.context.support.AbstractApplicationContext#refresh
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}
org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory
/**
* Tell the subclass to refresh the internal bean factory.
* @return the fresh BeanFactory instance
* @see #refreshBeanFactory()
* @see #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
/**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
org.springframework.context.support.AbstractRefreshableApplicationContext#createBeanFactory
/**
* Create an internal bean factory for this context.
* Called for each {@link #refresh()} attempt.
* <p>The default implementation creates a
* {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
* with the {@linkplain #getInternalParentBeanFactory() internal bean factory} of this
* context's parent as parent bean factory. Can be overridden in subclasses,
* for example to customize DefaultListableBeanFactory's settings.
* @return the bean factory for this context
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowEagerClassLoading
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
*/
protected DefaultListableBeanFactory createBeanFactory() {
// 这里就是简单的 new 了一个 DefaultListableBeanFactory 对象
// DefaultListableBeanFactory 继承了各种的抽象类和实现了各种接口,并进行了
// 实现,算是功能最强大的一个 BeanFactory 类了
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
org.springframework.context.support.AbstractRefreshableApplicationContext#customizeBeanFactory
/**
* Customize the internal bean factory used by this context.
* Called for each {@link #refresh()} attempt.
* <p>The default implementation applies this context's
* {@linkplain #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
* and {@linkplain #setAllowCircularReferences "allowCircularReferences"} settings,
* if specified. Can be overridden in subclasses to customize any of
* {@link DefaultListableBeanFactory}'s settings.
* @param beanFactory the newly created bean factory for this context
* @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
* @see DefaultListableBeanFactory#setAllowCircularReferences
* @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
* @see DefaultListableBeanFactory#setAllowEagerClassLoading
*/
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
// 这里设置如果遇到 name 一样的BeanDefination,后面的是否覆盖前面的,如果配置false
// 那么出现两个 name 一样的BeanDefination的时候,就会报错
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
// 配置是否容许循环依赖
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
接下来真正开始加载解析 BeanDefination 了
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 创建 BeanDefinitionReader 用来解析并注册BeanDefination,这里创建BeanDefinitionReader
// 的时候传的 DefaultListableBeanFactory 对象,而 DefaultListableBeanFactory 需要一个
// BeanDefinitionRegistry 对象,因为 DefaultListableBeanFactory 实现了
// BeanDefinitionRegistry,BeanDefinitionReader 之所以需要一个BeanDefinitionRegistry
// 的组件,就是要把解析出来的BeanDefination注册到BeanDefinitionRegistry中,以便后面
// bean 的实例化使用
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 这里给BeanDefinitionReader配置Environment,用于解析配置文件中的表达式
// 配置 ResouorceLoader 用于加载配置文件
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
// 这里开始加载BeanDefination
loadBeanDefinitions(beanDefinitionReader);
}
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.xml.XmlBeanDefinitionReader)
/**
* Load the bean definitions with the given XmlBeanDefinitionReader.
* <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
* method; hence this method is just supposed to load and/or register bean definitions.
* @param reader the XmlBeanDefinitionReader to use
* @throws BeansException in case of bean registration errors
* @throws IOException if the required XML document isn't found
* @see #refreshBeanFactory
* @see #getConfigLocations
* @see #getResources
* @see #getResourcePatternResolver
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String...)
@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int count = 0;
// 这里通过遍历,把配置的每一个配置文件都进行加载
for (String location : locations) {
count += loadBeanDefinitions(location);
}
return count;
}
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String)
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set<org.springframework.core.io.Resource>)
/**
* Load bean definitions from the specified resource location.
* <p>The location can also be a location pattern, provided that the
* ResourceLoader of this bean definition reader is a ResourcePatternResolver.
* @param location the resource location, to be loaded with the ResourceLoader
* (or ResourcePatternResolver) of this bean definition reader
* @param actualResources a Set to be filled with the actual Resource objects
* that have been resolved during the loading process. May be {@code null}
* to indicate that the caller is not interested in those Resource objects.
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
* @see #getResourceLoader()
* @see #loadBeanDefinitions(org.springframework.core.io.Resource)
* @see #loadBeanDefinitions(org.springframework.core.io.Resource[])
*/
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 {
// 这里先用ResourcePatternResolver对配置文件资源进行加载
// 这里的ResourcePatternResolver其实就是之前AbstractApplicationContext中
// 创造的 PathMatchingResourcePatternResolver ,支持按照 ant 风格,加载所有
// 匹配的资源,例如我们配置 beans-*.xml,那么就会加载所有以 beans- 开头的xml
// 文件
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 这里对加载到的资源进行解析
int count = loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
}
return count;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// 如果 resourceLoader 不是 ResourcePatternResolver,那就是一个
// DefaultResourceLoader 只支持单个资源的加载
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int count = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
}
return count;
}
}
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource...)
@Override
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;
}
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource)
/**
* Load bean definitions from the specified XML file.
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)
/**
* 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.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
// 获取资源的输入流,以便读取资源内容
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 读取资源,加载 BeanDefinition
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions
/**
* Actually load bean definitions from the specified XML file.
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
* @see #doLoadDocument
* @see #registerBeanDefinitions
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 这里是最重要的两部
// 一,将输入里解析为一个 Document 对象
Document doc = doLoadDocument(inputSource, resource);
// 二,将Document的信息封装成BeanDefination,并注册到BeanDefinationRegistry
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);
}
}
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions
/**
* 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 {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
/**
* Create the {@link BeanDefinitionDocumentReader} to use for actually
* reading bean definitions from an XML document.
* <p>The default implementation instantiates the specified "documentReaderClass".
* @see #setDocumentReaderClass
*/
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanUtils.instantiateClass(this.documentReaderClass);
}
/**
* Create the {@link XmlReaderContext} to pass over to the document reader.
*/
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
/**
* This implementation parses bean definitions according to the "spring-beans" XSD
* (or DTD, historically).
* <p>Opens a DOM Document; then initializes the default settings
* specified at the {@code <beans/>} level; then parses the contained bean definitions.
*/
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
/**
* Register each bean definition within the given root {@code <beans/>} element.
*/
@SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...)
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;
// 这里创建一个BeanDefinitionParserDelegate ,这是将Element 中的
// 信息解析成 BeanDefination 的关键组件
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
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.
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;
}
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
* @param root the DOM root element of the document
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
// 根节点(root 这里的根节点就是 <beans> 标签)
// 获取根节点的所有子节点
// 这里的子节点有多种类型
// 有标签 有文本(比如标签前后换行符) 有注释
// Element 代表标签
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 如果是标签就进行处理
// 因为我们把Bean的信息都定义在标签中
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 解析标签中的属性 和 子标签
parseDefaultElement(ele, delegate);
}else {
delegate.parseCustomElement(ele);
}
}
}
}else {
delegate.parseCustomElement(root);
}
}
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElement
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
// 处理 <import> 标签,import 标签可以导入另一个xml文件
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
// 处理 <alias> 标签
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
// 处理 <bean> 标签
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// 处理 嵌套的 <beans> 标签
// recurse
doRegisterBeanDefinitions(ele);
}
}
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate){
// 解析<bean> 标签并封装在一个BeanDefination中,并将
// BeanDefination 封装到一个BeanDefinitionHolder中
// 最重要的就是这里
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 如果需要的话,使用一个 namespace handler 对封装好的BeanDefination再次进行装饰
//Decorate the given bean definition through a namespace handler, if applicable.
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 将封装好的BeanDefination注册到 BeanDefinitionRegistry 中
// 这里 getReaderContext().getRegistry()获取到的BeanDefinitionRegistry
// 其实就是之前 AbstractApplicationContext 中创建的 DefaultListableBeanFactory
// 将封装好的BeanDefination注册到DefaultListableBeanFactory,后面就可以用来
// 实例化我们定义好的 bean 实例
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element)
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 获取<bean>标签的 id 属性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取<bean>标签的 name 属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 将上面获取的name 根据 , 或者 ; 进行切割,作为别名
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
//默认beanName等于 id
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
// 如果id为null 但是别名不为空 beanName等于第一个别名
beanName = aliases.remove(0);
if (logger.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
// 校验beanName的唯一性
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 将Element ele 的 信息封装为一个BeanDefination
// 这里的containingBean一般都是null,但是在一种特殊情况下,就是当前解析到的<bean>标签是嵌套在
// <property> 标签中,这是containingBean,就是<property>所属的<bean>标签解析出来的 BeanDefination
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
//如果当前解析的<bean>标签成功解析出了AbstractBeanDefinition,但是beanName为空
//这里需要为其生成一个beanName
try {
if (containingBean != null) {
// 如果containingBean != null 说明当前解析到的<bean>标签是嵌套在<property> 标签中
// 生成beanName
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
// 当前解析到的<bean>标签是单独的一个<bean>标签
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, java.lang.String, org.springframework.beans.factory.config.BeanDefinition)
/**
* Parse the bean definition itself, without regard to name or aliases. May return
* {@code null} if problems occurred during the parsing of the bean definition.
*/
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
// 解析出 class 属性
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
// 解析出 parent 属性
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 创建 AbstractBeanDefinition 实例,并设置最基本的属性
// parent 和 beanClass或者beanClassName
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 将<bean>标签的属性 设置到 BeanDefination
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 解析<bean>标签的子标签 <description>一个Person的信息</description> 获取其内容
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 解析子标签<meta key="personKey" value="personValue"/>
parseMetaElements(ele, bd);
//解析子标签<lookup-method name="findLookupCommandBean" bean="lookupCommand"/>
// 添加到 bd.getMethodOverrides() 中
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//解析子标签<replaced-method name="executeMethodReplacerTwo" replacer="methodReplacerOne"/>
// 添加到 bd.getMethodOverrides() 中
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析构造函数的参数 <constructor-arg name="age" value="30"/>
//由于构造函数可能有多个参数,所以可能有多个constructor-arg子标签
parseConstructorArgElements(ele, bd);
//解析子标签<property name = "name" value = "zhangsan"/>
//一个实例可能需要配置多个属性,所以可能会有多个property 子标签
parsePropertyElements(ele, bd);
//解析子标签<qualifier value="car" type="com.fll.model.Car"/>
parseQualifierElements(ele, bd);
// 这是这个BeanDefination是从哪个资源文件中解析出来的
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
/**
* Create a bean definition for the given class name and parent name.
* @param className the name of the bean class
* @param parentName the name of the bean's parent bean
* @return the newly created bean definition
* @throws ClassNotFoundException if bean class resolution was attempted but failed
*/
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}
org.springframework.beans.factory.support.BeanDefinitionReaderUtils#createBeanDefinition
/**
* Create a new GenericBeanDefinition for the given parent name and class name,
* eagerly loading the bean class if a ClassLoader has been specified.
* @param parentName the name of the parent bean, if any
* @param className the name of the bean class, if any
* @param classLoader the ClassLoader to use for loading bean classes
* (can be {@code null} to just register bean classes by name)
* @return the bean definition
* @throws ClassNotFoundException if the bean class could not be loaded
*/
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionAttributes
/**
* Apply the attributes of the given bean element to the given bean * definition.
* @param ele bean declaration element
* @param beanName bean name
* @param containingBean containing bean definition
* @return a bean definition initialized according to the bean element attributes
*/
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
// 解析属性 scoope
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}
else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
bd.setScope(containingBean.getScope());
}
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
// 解析属性 abstract
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
//解析属性 autowire
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
//解析属性 depends_on
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
// 解析属性 autowire_candidate
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if (isDefaultValue(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
}
else {
bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
}
// 解析属性 primary
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
// 解析属性 init-method
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
bd.setInitMethodName(initMethodName);
}
else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
// 解析属性 destory-method
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
}
else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
// 解析属性 factory-method
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
// 解析属性 factory-bean
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}
用debug看看上面举的例子,解析为BeanDefination是啥样的