2.2 望闻问切
到这里我们可以看到,我们可以通过这种方式注入bean了。接下来我们就开启debug模式,一步一步来看看
2.2.1 ClassPathXmlApplicationContext.java 构造方法
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("classpath:application.xml");
调用ClassPathXmlApplicationContext构造方法的时候,我们可以看到源码:
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
//通过调试我们可以看到这一行就是设置配置文件的位置
//即上面的传入构造函数的参数 configLocations = "classpath:application.xml"
setConfigLocations(configLocations);
if (refresh) {
//这是关键的方法,为什么这个方法不是什么init或者create的呢?为啥会叫refres呢?
//因为applicationContext是可以重建的,调用这个方法是可以重新初始化applicationContext的
//接下来我们细看一下refresh()方法是怎样的
refresh();
}
}
2.2.2 AbstractApplicationContext.java refresh() 把握整体
@Override
public void refresh() throws BeansException, IllegalStateException {
//首先这里加了个锁,这个startupShutdownMonitor就是一个Object对象
//加锁的目的在于保证容器的完整性,因为可能出现当前refresh()还没有结束,又来一次更新或者销毁容器
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//会初始化占位符、属性源等
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
/*
通过这个obtainFreshBeanFactory方法,会初始化beanFactory,
加载注册bean等,但现在的bean还没有初始化。
得到的beanFactory里面已经包含了配置文件里配置的bean的信息,用beanDefinitionMap保存
这个map以我们配置的bean的id或者name作为key,以BeanDefinition实体作为value
(同时这个id或者name的值会被保存到beanDefinitionNames这个ArrayList里面)
由此可见 这个方法是多么的重要! 要仔细的看这个方法的具体实现*/
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);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// 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();
}
}
}
2.2.2.1 AbstractApplicationContext.java refresh()->prepareRefresh()
这个方法主要就是对xml配置文件的校验等:
/**
* Prepare this context for refreshing, setting its startup date and
* active flag as well as performing any initialization of property sources.
*/
protected void prepareRefresh() {
// Switch to active.
//记录时间,设置closed = false , active = true
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
// Initialize any placeholder property sources in the context environment.
//这个方法是替换(初始化)属性源的方法,子类中没有默认实现
initPropertySources();
// Validate that all properties marked as required are resolvable:
// see ConfigurablePropertyResolver#setRequiredProperties
//校验我们的配置文件是否是正确的、可解析的
getEnvironment().validateRequiredProperties();
// Store pre-refresh ApplicationListeners...
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
}
2.2.2.2 AbstractApplicationContext.java refresh()->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();
}
看来refreshBeanFactory()才是核心的方法,继续进入这个方法看:
refresh()->obtainFreshBeanFactory()->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 {
//判断当前环境的beanFactory是否为空(当前applicaContext是否加载了beanFactory)
//如果存在beanFactory对象,就先销毁所有的bean,再关闭beanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
/*
这里为什么要使用DefaultListableBeanFactory呢?
我们可以看一下下面的图就知道了DefaultListableBeanFactory作为最下面的子类,
具有以上所有父类和实现的接口的功能
(在DefaultListableBeanFactory.java里按Ctrl+Alt+shift+U弹出该视图)*/
DefaultListableBeanFactory beanFactory = createBeanFactory();
//设置beanFactory的序列号
beanFactory.setSerializationId(getId());
/*
这个方法自定义beanFactory的功能,
设置allowBeanDefinitionOverriding和allowCircularReferences
即允许bean被覆盖和允许循环引用
循环引用这一块在容器的基本实现这一节中不展开探究,以后会另外开一篇来探究他*/
customizeBeanFactory(beanFactory);
/*
从方法名字上我们就可以大概看出该方法的大概功能:加载beanDefinition。
beanDefinition就是存储 加载、注册的bean的信息的。
所以在代码层面上,我们可以简单认为,bean就是beanDefinition的实例。
这个方法把各个加载完成的beanDefinition放入beanFactory中
*/
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
关于bean的覆盖问题和循环引用问题,这里简单的说明一下:
bean的覆盖指的是当allowBeanDefinitionOverriding=true的时候,beanDefinitionMap中的key是否有相同的。如果有,那么后面的beanDefinition会把前面的给覆盖掉。当allowBeanDefinitionOverriding=false的时候,不会覆盖,会直接报错。 bean的循环依赖是指A依赖B,B依赖C,C又依赖A这种情况。在单例情况下,Spring是默认允许循环依赖的。
关于BeanDefinition,他保存了bean实际指向的类、是否是单例、依赖了其他哪些bean等信息,底层 就是通过反射从这个beanDefinition里实例化bean的,我们具体看一看这个接口具体包含了哪一些属性:
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; int ROLE_APPLICATION = 0; int ROLE_SUPPORT = 1; int ROLE_INFRASTRUCTURE = 2; void setParentName(@Nullable String parentName); @Nullable String getParentName(); void setBeanClassName(@Nullable String beanClassName); @Nullable String getBeanClassName(); void setScope(@Nullable String scope); @Nullable String getScope(); void setLazyInit(boolean lazyInit); boolean isLazyInit(); void setDependsOn(@Nullable String... dependsOn); @Nullable String[] getDependsOn(); void setAutowireCandidate(boolean autowireCandidate); boolean isAutowireCandidate(); void setPrimary(boolean primary); boolean isPrimary(); void setFactoryBeanName(@Nullable String factoryBeanName); @Nullable String getFactoryBeanName(); void setFactoryMethodName(@Nullable String factoryMethodName); @Nullable String getFactoryMethodName(); ConstructorArgumentValues getConstructorArgumentValues(); default boolean hasConstructorArgumentValues() { return !getConstructorArgumentValues().isEmpty(); } MutablePropertyValues getPropertyValues(); default boolean hasPropertyValues() { return !getPropertyValues().isEmpty(); } void setInitMethodName(@Nullable String initMethodName); @Nullable String getInitMethodName(); void setDestroyMethodName(@Nullable String destroyMethodName); @Nullable String getDestroyMethodName(); void setRole(int role); int getRole(); void setDescription(@Nullable String description); @Nullable String getDescription(); ResolvableType getResolvableType(); boolean isSingleton(); boolean isPrototype(); boolean isAbstract(); @Nullable String getResourceDescription(); @Nullable BeanDefinition getOriginatingBeanDefinition(); }
现在来看看这个极为重要的loadBeanDefinitions方法:
refresh()->obtainFreshBeanFactory()->refreshBeanFactory()->loadBeanDefinitions(beanFactory)
@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. //beanDefinitionReader加载配置文件中的配置并解析 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 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); loadBeanDefinitions(beanDefinitionReader); }
refresh()->obtainFreshBeanFactory()->refreshBeanFactory()->loadBeanDefinitions(beanFactory)->loadBeanDefinitions(beanDefinitionReader)
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { //1 reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { //2 reader.loadBeanDefinitions(configLocations); } }
上面的分支最终都会进入loadBeanDefinitions(Resource… resource)这个方法,注意的是有许多重载的loadBeanDefinitions方法
@Override public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int count = 0; //一个resource代表了一个资源文件,遍历解析每一个资源文件 for (Resource resource : resources) { //在这里进行了解析 count += loadBeanDefinitions(resource); } //返回的是加载的beanDefinition的数量 return count; }
方法loadBeanDefinitions(Resource resource):
@Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { //又转到了 loadBeanDefinitions(EncodedResource encodedResource)方法 return loadBeanDefinitions(new EncodedResource(resource)); }
方法 loadBeanDefinitions(EncodedResource encodedResource):
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); } //resourcesCurrentlyBeingLoaded是一个threadLocal对象,这个set集合保存了当前环境下正在加载的resource Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } //如果这个set集合里面已经有了该资源,说明已经加载过该资源,就会报错 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()); } //最后是通过doLoadBeanDefinitions方法来解析这个xml配置文件 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(); } } }
方法doLoadBeanDefinitions(InputSource inputSource, Resource resource):
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //先解析为document Document doc = doLoadDocument(inputSource, resource); //继续看这个方法,会把document进行进一步解析 ,count是本次注册的bean的数量 int count = registerBeanDefinitions(doc, resource); return count; } catch (Exception ex) { throw ex; } }
方法registerBeanDefinitions(Document doc, Resource resource):
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //这里getBeanDefinitionCount()是在DefaultListBeanFactory.java下面的,实际上返回的就是beanDefinitionMap的size,countBefore就是现有的beanDefinition的数量 int countBefore = getRegistry().getBeanDefinitionCount(); //需要关注这里,进行解析 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); //被注册的总数-之前的数量 = 本次注册了的数量 return getRegistry().getBeanDefinitionCount() - countBefore; }
方法registerBeanDefinitions(Document doc, XmlReaderContext readerContext):
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; //doc.getDocumentElement()得到的是element对象 doRegisterBeanDefinitions(doc.getDocumentElement()); }
in DefaultBeanDefinitionDocumentReader.java
方法doRegisterBeanDefinitions(Element root):
protected void doRegisterBeanDefinitions(Element root) { //涉及到递归,因为解析的beans标签中可能还会有beans标签,往下看就知道了 BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { /* 这里是适配环境配置的profile 在springboot项目里面,我们可以以application-[profile].yml形式配置,例如: application-dev.yml、application-prod.yml等 通过spring.profiles.active 选择项目加载的配置文件 例如:当配置spring.profiles.active: dev时,项目会按照application-dev.yml的配置来启动了。 单纯的spring项目中,我们以<beans profile="dev"></beans>这种方式配置profile 然后在启动项目的时候指定就ok了: -Dspring.profiles.active="dev" */ 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; }
方法parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
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; /*如果是默认的命名空间里的默认节点: <beans />, <bean />, <import /> ,<alias /> */ if (delegate.isDefaultNamespace(ele)) { //解析默认的标签 parseDefaultElement(ele, delegate); } //其他标签 例如<double /> ,<mvc />等 else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
方法parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
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)) { processBeanDefinition(ele, delegate); } /* 因为配置文件里beans标签中还能包含beans标签,所以这里会递归,这也就是为什么doRegisterBeanDefinitions中会有parent */ else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
我们这里解析的默认标签,所以看processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)这个方法:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //这里实际上是个空壳方法,实际上调用的是parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) 这个方法解析的。 //先看下面的方法,看到bdHolder怎么创建出来的了再接着这里往下看 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { //这里是处理自定义的nameSpace的。 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); //到这里为止,我们已经完成了一个beanDefinition的创建了。 //obtainFreshBeanFactory()这个方法完成的就是beanDefinition的创建和注册,那么这个方法的功能到现在为止完成了一半了。 //接下来就是要完成beanDefinition的注册了 try { // 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)); } }
可以关注一下这个BeanDefinitionHolder的结构,实际上就是一个beanDefinition对象和一个aliases的数组和一个beanName的字符串。
方法parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean)
这个时候ele包含了这么多信息
结合这个图片上内容来看代码
@Nullable public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { //获取ele里面的id、name的值 String id = ele.getAttribute(ID_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<>(); //当配置的bean的name属性不为空的时候,通过分隔符分割为数组 //MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; " 即逗号分号空格 if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; //当bean没有配置id属性,就是用配置的第一个name的值作为beanName if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { 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); } //到这步,可以看到就已经得到了beanDefinition对象了,我们直接往下看这个方法,再回来看接下来的代码 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { //当这个beanDefinition对象存在,但beanName没有值,即id和name没有配置的时候,这段代码会设置一个beanName,类似这种形式: //"com.jerry.springSource.pojo.Student#0" if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { 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); //这里返回了一个BeanDefinitionHolder,再往上一个方法看 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; }
看下最终生成beanDefinition的这个parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean)方法@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)) { //这里获取到的是全路径类名,在我的例子中,获取到的是 //com.jerry.springSource.pojo.Student className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } try { //创建一个GenericBeanDefinition对象 AbstractBeanDefinition bd = createBeanDefinition(className, parent); //设置了很多GenericBeanDefinition中的属性,ctrl+左键点击set方法,发现这些属性都定义在AbstractBeanDefinition这里面 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); //这些parse开头的方法,都是解析bean里面的标签的,从上往下分别是解析 //meta、lookup-method、replace-method、constructor-arg、property和qualifier 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)); //返回该beanDefinition对象,看到这里就看上一层方法 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; }