前言
Spring配置文件读取流程本来是和一文放在一起的,这两天在看Spring自定义标签的时候,感觉对Spring配置文件读取流程还是研究得不够,因此将Spring配置文件读取流程部分从之前的文章拆出来单独成为一文。
为了看一下Spring配置文件加载流程,先定义一个bean.xml:
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans
5 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
6
7 <bean id="beanPostProcessorBean" class="org.xrq.action.BeanPostProcessorBean" />
8
9 <bean id="beanFactoryPostProcessorBean" class="org.xrq.action.BeanFactoryPostProcessorBean" />
10
11 <bean id="multiFunctionBean" class="org.xrq.action.MultiFunctionBean" init-method="initMethod">
12 <property name="propertyA" value="abc" />
13 </bean>
14
15 </beans>
至于Bean是什么并不重要,有配置文件就够了。
Bean定义加载流程----从Refresh到Bean定义加载前
首先看一下Bean加载前整个代码流程走向。Spring上下文刷新始于AbstractApplicationContext的refresh()方法:
1 public void refresh() throws BeansException, IllegalStateException {
2 synchronized (this.startupShutdownMonitor) {
3 // Prepare this context for refreshing.
4 prepareRefresh();
5
6 // Tell the subclass to refresh the internal bean factory.
7 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
8
9 // Prepare the bean factory for use in this context.
10 prepareBeanFactory(beanFactory);
11
12 ...
13 }
代码不全帖了,第7行的obtainFreshBeanFactory()方法进去:
1 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
2 refreshBeanFactory();
3 ConfigurableListableBeanFactory beanFactory = getBeanFactory();
4 if (logger.isDebugEnabled()) {
5 logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
6 }
7 return beanFactory;
8 }
第2行的refreshBeanFactory()方法进去,它是AbstractApplicationContext的子类AbstractRefreshableApplicationContext中的方法:
1 @Override
2 protected final void refreshBeanFactory() throws BeansException {
3 if (hasBeanFactory()) {
4 destroyBeans();
5 closeBeanFactory();
6 }
7 try {
8 DefaultListableBeanFactory beanFactory = createBeanFactory();
9 beanFactory.setSerializationId(getId());
10 customizeBeanFactory(beanFactory);
11 loadBeanDefinitions(beanFactory);
12 synchronized (this.beanFactoryMonitor) {
13 this.beanFactory = beanFactory;
14 }
15 }
16 catch (IOException ex) {
17 throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
18 }
19 }
首先第8行获取DefaultListableBeanFactory,然后执行第11行的方法,传入当前获取的BeanFactory,准备加载Bean定义。BeanFactory中有存储了些什么数据在【Spring源码分析】Bean加载流程概览一文中有画表格详细说明,看过表格的朋友应该知道为什么第8行要获取的是DefaultListableBeanFactory而不是它的接口BeanFactory,因为Bean定义存储在Map<String, BeanDefinition>中,这个Map的位置就是在DefaultListableBeanFactory里,因此这里直接获取DefaultListableBeanFactory并作为参数层层向后传,加载完Bean定义后直接向Map<String, BeanDefinition>里put键值对。
看下loadBeanDefinitions方法,它是AbstractRefreshableApplicationContext子类AbstractXmlApplicationContext中的一个方法:
1 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
2 // Create a new XmlBeanDefinitionReader for the given BeanFactory.
3 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
4
5 // Configure the bean definition reader with this context's
6 // resource loading environment.
7 beanDefinitionReader.setResourceLoader(this);
8 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
9
10 // Allow a subclass to provide custom initialization of the reader,
11 // then proceed with actually loading the bean definitions.
12 initBeanDefinitionReader(beanDefinitionReader);
13 loadBeanDefinitions(beanDefinitionReader);
14 }
第3行的XmlBeanDefinitionReader是Bean加载的核心类,先构建出来,后面代码没什么值得看的,直接看第13行代码,传入XmlBeanDefinitionReader:
1 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
2 Resource[] configResources = getConfigResources();
3 if (configResources != null) {
4 reader.loadBeanDefinitions(configResources);
5 }
6 String[] configLocations = getConfigLocations();
7 if (configLocations != null) {
8 reader.loadBeanDefinitions(configLocations);
9 }
10 }
由第8行的代码进去,这个就不跟了,直接走到XmlBeanDefinitionReader的父类AbstractBeanDefinitionReader的loadBeanDefinitions方法中:
1 public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
2 ResourceLoader resourceLoader = getResourceLoader();
3 if (resourceLoader == null) {
4 throw new BeanDefinitionStoreException(
5 "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
6 }
7
8 if (resourceLoader instanceof ResourcePatternResolver) {
9 // Resource pattern matching available.
10 try {
11 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
12 int loadCount = loadBeanDefinitions(resources);
13 if (actualResources != null) {
14 for (Resource resource : resources) {
15 actualResources.add(resource);
16 }
17 }
18 if (logger.isDebugEnabled()) {
19 logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
20 }
21 return loadCount;
22 }
23 catch (IOException ex) {
24 throw new BeanDefinitionStoreException(
25 "Could not resolve bean definition resource pattern [" + location + "]", ex);
26 }
27 }
28 else {
29 // Can only load single resources by absolute URL.
30 Resource resource = resourceLoader.getResource(location);
31 int loadCount = loadBeanDefinitions(resource);
32 if (actualResources != null) {
33 actualResources.add(resource);
34 }
35 if (logger.isDebugEnabled()) {
36 logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
37 }
38 return loadCount;
39 }
40 }
我们研究Spring加载流程使用的ClassPathXmlApplicationContext是ResourcePatternResolver的实现类,进入第8行的判断,走第12行的方法,这里也不跟了,很简单,最终代码走到了XmlBeanDefinitionReader的loadBeanDefinitions方法中,也就是Bean定义加载的开始。
Bean定义加载流程----Bena定义的存储
上面说到了Bean定义是存储在DefaultListableBeanFactory中的,我们来看一下具体代码:
1 /** Map of bean definition objects, keyed by bean name */
2 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
3
4 /** List of bean definition names, in registration order */
5 private final List<String> beanDefinitionNames = new ArrayList<String>();
最终DefaultListableBeanFactory会先遍历beanDefinitionNames,从beanDefinitionMap中拿到对应的BeanDefinition,最终转为具体的Bean对象。BeanDefinition本身是一个接口,AbstractBeanDefinition这个抽象类存储了Bean的属性,看一下AbstractBeanDefinition这个抽象类的定义:
这个类的属性与方法很多,这里就列举了一些最主要的方法和属性,可以看到包含了bean标签中的所有属性,之后就是根据AbstractBeanDefinition中的属性值构造出对应的Bean对象。
Bean定义加载流程----开始加载Bean定义
上面一部分的结尾说道,Bean定义加载的开始始于XmlBeanDefinitionReader的loadBeanDefinitions方法,看下loadBeanDefinitions方法定义:
1 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
2 Assert.notNull(encodedResource, "EncodedResource must not be null");
3 if (logger.isInfoEnabled()) {
4 logger.info("Loading XML bean definitions from " + encodedResource.getResource());
5 }
6
7 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
8 if (currentResources == null) {
9 currentResources = new HashSet<EncodedResource>(4);
10 this.resourcesCurrentlyBeingLoaded.set(currentResources);
11 }
12 if (!currentResources.add(encodedResource)) {
13 throw new BeanDefinitionStoreException(
14 "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
15 }
16 try {
17 InputStream inputStream = encodedResource.getResource().getInputStream();
18 try {
19 InputSource inputSource = new InputSource(inputStream);
20 if (encodedResource.getEncoding() != null) {
21 inputSource.setEncoding(encodedResource.getEncoding());
22 }
23 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
24 }
25 finally {
26 inputStream.close();
27 }
28 }
29 catch (IOException ex) {
30 throw new BeanDefinitionStoreException(
31 "IOException parsing XML document from " + encodedResource.getResource(), ex);
32 }
33 finally {
34 currentResources.remove(encodedResource);
35 if (currentResources.isEmpty()) {
36 this.resourcesCurrentlyBeingLoaded.remove();
37 }
38 }
39 }
第17行根据XML文件获取输入字节流,接着流程走到23行doLoadBeanDefinitions方法:
1 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
2 throws BeanDefinitionStoreException {
3 try {
4 int validationMode = getValidationModeForResource(resource);
5 Document doc = this.documentLoader.loadDocument(
6 inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
7 return registerBeanDefinitions(doc, resource);
8 }
9 catch (BeanDefinitionStoreException ex) {
10 throw ex;
11 }
12 catch (SAXParseException ex) {
13 throw new XmlBeanDefinitionStoreException(resource.getDescription(),
14 "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
15 }
16 catch (SAXException ex) {
17 throw new XmlBeanDefinitionStoreException(resource.getDescription(),
18 "XML document from " + resource + " is invalid", ex);
19 }
20 catch (ParserConfigurationException ex) {
21 throw new BeanDefinitionStoreException(resource.getDescription(),
22 "Parser configuration exception parsing XML from " + resource, ex);
23 }
24 catch (IOException ex) {
25 throw new BeanDefinitionStoreException(resource.getDescription(),
26 "IOException parsing XML document from " + resource, ex);
27 }
28 catch (Throwable ex) {
29 throw new BeanDefinitionStoreException(resource.getDescription(),
30 "Unexpected exception parsing XML document from " + resource, ex);
31 }
32 }
首先是第4行,获取验证模式,代码不跟了,最终出来的是DetectMode,DetectMode的意思是XML文件的验证模式由XML文件本身决定,如果是DTD那就使用DTD验证,如果是XSD就使用XSD验证。
接着是第5行~第6行,这两行的作用是通过DOM得到org.w3c.dom.Document对象,Document将XML文件看成一棵树,Dociument即对这颗树数据结构的一个描述。
最近进入第7行,继续加载Bean定义的流程,跟一下registerBeanDefinitions方法:
1 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
2 // Read document based on new BeanDefinitionDocumentReader SPI.
3 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
4 int countBefore = getRegistry().getBeanDefinitionCount();
5 documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
6 return getRegistry().getBeanDefinitionCount() - countBefore;
7 }
因为是每个XML文件执行一次registerBeanDefinitions方法注册Bean定义,因此这整个方法的返回值表示的是当前XML里面一共注册了多少个Bean。直接进入第5行的代码,使用BeanDefintionDocumentReader的registerBeanDefinitions方法来注册Bean定义:
1 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
2 this.readerContext = readerContext;
3
4 logger.debug("Loading bean definitions");
5 Element root = doc.getDocumentElement();
6
7 BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
8
9 preProcessXml(root);
10 parseBeanDefinitions(root, delegate);
11 postProcessXml(root);
12 }
这里面的方法,第9行的方法preProcessXml是个空方法,留给子类扩展用;第11行的方法postProcessXml是个空方法,留给子类扩展用。
第5行的方法得到根节点,也就是<beans ...></beans>。
剩下的就是第7行的createHelper方法与第10行的parseBeanDefintions方法了,前者构造出一个Bean定义解析器的委托类,后者使用委托类解析Bean定义,下面分两部分分别来看。
Bean定义加载流程----createHelper
先看createHelper,即根据根节点创建一个Bean定义解析器的委托类,看一下代码实现:
1 protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root) {
2 BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
3 delegate.initDefaults(root);
4 return delegate;
5 }
第2行没有什么特别的,new一个BeanDefinitionParserDelegate出来,第3行的代码跟一下,用于设置默认属性:
1 public void initDefaults(Element root) {
2 populateDefaults(this.defaults, root);
3 this.readerContext.fireDefaultsRegistered(this.defaults);
4 }
跟一下第2行的代码:
1 protected void populateDefaults(DocumentDefaultsDefinition defaults, Element root) {
2 defaults.setLazyInit(root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE));
3 defaults.setMerge(root.getAttribute(DEFAULT_MERGE_ATTRIBUTE));
4 defaults.setAutowire(root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE));
5 defaults.setDependencyCheck(root.getAttribute(DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE));
6 if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) {
7 defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE));
8 }
9 if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) {
10 defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE));
11 }
12 if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) {
13 defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE));
14 }
15 defaults.setSource(this.readerContext.extractSource(root));
16 }
看到就是这个地方将<beans>标签下的default-lazy-init、default_merge、default_autowire、default-dependency-check、default-autowire-candidates、default-init-method、default-destroy-method这几个属性取出来,设置到DocumentDefaultsDefinition即defaults中。
Bean定义加载流程----parseBeanDefintions
到了parseBeanDefintions方法了,这个方法开始真正遍历XML文件中的各个标签并转换为对应的Bean定义,看一下方法定义:
1 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
2 if (delegate.isDefaultNamespace(root)) {
3 NodeList nl = root.getChildNodes();
4 for (int i = 0; i < nl.getLength(); i++) {
5 Node node = nl.item(i);
6 if (node instanceof Element) {
7 Element ele = (Element) node;
8 if (delegate.isDefaultNamespace(ele)) {
9 parseDefaultElement(ele, delegate);
10 }
11 else {
12 delegate.parseCustomElement(ele);
13 }
14 }
15 }
16 }
17 else {
18 delegate.parseCustomElement(root);
19 }
20 }
首先说一下,这里的Namespace都是默认的Namespace,至于Namespace的问题,和自定义Spring标签相关,我想放到自定义Spring标签部分说,这里只要知道代码会进入第2行与第9行的判断即可。
第2行的判断进去,都在遍历Element下的节点不看了,直接跟第9行的代码parseDefaultElement:
1 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
2 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
3 importBeanDefinitionResource(ele);
4 }
5 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
6 processAliasRegistration(ele);
7 }
8 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
9 processBeanDefinition(ele, delegate);
10 }
11 }
这边就是判断节点名称是import还是alias还是bean,是其中任意一个就进入相应的执行逻辑,import和alias不看了,这里就看Bean加载流程部分,也就是第9行的processBeanDefinition方法:
1 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
2 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
3 if (bdHolder != null) {
4 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
5 try {
6 // Register the final decorated instance.
7 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
8 }
9 catch (BeanDefinitionStoreException ex) {
10 getReaderContext().error("Failed to register bean definition with name '" +
11 bdHolder.getBeanName() + "'", ele, ex);
12 }
13 // Send registration event.
14 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
15 }
16 }
先看第4行,第4行的意思是在需要的时候装饰Bean定义,比如AOP的场景会使用到,这个留在AOP的时候看这段代码。
再看第7行,第7行的意思是注册Bean定义,这在下一部分说,属于Bean定义加载流程的最后一步。
现在看来第2行的代码,顾名思义即解析Bean定义元素,跟一下代码:
1 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
2 String id = ele.getAttribute(ID_ATTRIBUTE);
3 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
4
5 List<String> aliases = new ArrayList<String>();
6 if (StringUtils.hasLength(nameAttr)) {
7 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);
8 aliases.addAll(Arrays.asList(nameArr));
9 }
10
11 String beanName = id;
12 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
13 beanName = aliases.remove(0);
14 if (logger.isDebugEnabled()) {
15 logger.debug("No XML 'id' specified - using '" + beanName +
16 "' as bean name and " + aliases + " as aliases");
17 }
18 }
19
20 if (containingBean == null) {
21 checkNameUniqueness(beanName, aliases, ele);
22 }
23
24 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
25 if (beanDefinition != null) {
26 if (!StringUtils.hasText(beanName)) {
27 try {
28 if (containingBean != null) {
29 beanName = BeanDefinitionReaderUtils.generateBeanName(
30 beanDefinition, this.readerContext.getRegistry(), true);
31 }
32 else {
33 beanName = this.readerContext.generateBeanName(beanDefinition);
34 // Register an alias for the plain bean class name, if still possible,
35 // if the generator returned the class name plus a suffix.
36 // This is expected for Spring 1.2/2.0 backwards compatibility.
37 String beanClassName = beanDefinition.getBeanClassName();
38 if (beanClassName != null &&
39 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
40 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
41 aliases.add(beanClassName);
42 }
43 }
44 if (logger.isDebugEnabled()) {
45 logger.debug("Neither XML 'id' nor 'name' specified - " +
46 "using generated bean name [" + beanName + "]");
47 }
48 }
49 catch (Exception ex) {
50 error(ex.getMessage(), ele);
51 return null;
52 }
53 }
54 String[] aliasesArray = StringUtils.toStringArray(aliases);
55 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
56 }
57
58 return null;
59 }
第2行~第18行的代码都是用于获取BeanName,从这段逻辑中我们可以总结出BeanName定义的规则:
- 默认的BeanName就是<bean>标签重定义的id
- <bean>标签中可以定义name属性,一个bean可以有多个别名(alias),都定义在name属性中,不同的别名以",;"分割,假如beanId未定义,那么就以name属性中的第一个别名作为beanName
第20行~第22行的代码主要用于确保BeanName的唯一性,跟一下第21行的方法就知道,BeanName与Bean别名都会放在Set<String>中,然后每次加载Bean定义的时候都会去这个Set<String>中检查当前BeanName和Bean别名是否存在,如果存在就报错。
接着进入第24行的代码,开始解析Bean定义元素:
1 public AbstractBeanDefinition parseBeanDefinitionElement(
2 Element ele, String beanName, BeanDefinition containingBean) {
3
4 this.parseState.push(new BeanEntry(beanName));
5
6 String className = null;
7 if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
8 className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
9 }
10
11 try {
12 String parent = null;
13 if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
14 parent = ele.getAttribute(PARENT_ATTRIBUTE);
15 }
16 AbstractBeanDefinition bd = createBeanDefinition(className, parent);
17
18 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
19 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
20
21 parseMetaElements(ele, bd);
22 parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
23 parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
24
25 parseConstructorArgElements(ele, bd);
26 parsePropertyElements(ele, bd);
27 parseQualifierElements(ele, bd);
28
29 bd.setResource(this.readerContext.getResource());
30 bd.setSource(extractSource(ele));
31
32 return bd;
33 }
34 catch (ClassNotFoundException ex) {
35 error("Bean class [" + className + "] not found", ele, ex);
36 }
37 catch (NoClassDefFoundError err) {
38 error("Class that bean class [" + className + "] depends on not found", ele, err);
39 }
40 catch (Throwable ex) {
41 error("Unexpected failure during bean definition parsing", ele, ex);
42 }
43 finally {
44 this.parseState.pop();
45 }
46
47 return null;
48 }
对这个方法逐行总结一下:
- 第4行,ParseState是一个基于栈的简单结构,在解析的流程中用于追踪逻辑上的位置,里面存放的是Entry接口。Entry接口有各种实现类比如AdviceEntry、AdvisorEntry、BeanEntry、PropertyEntry等,每一步操作开始将一个Entry推送至栈顶,每一步操作结束将Entry从栈顶弹出,这里将一个BeanEntry推送至栈顶,标识解析Bean定义开始
- 第6行~第9行的代码,用于获取Bean对应的类路径
- 第12行~第15行的代码,用于解析<bean>标签中的parent属性
- 第16行的代码,根据Bean对应的类路径以及parent,生成一个AbstractBeanDefinition,AbstractBeanDefinition是一个抽象类,生成出来的具体实例为GenericBeanDefinition
- 第18行的代码,就不跟了进去一看就很好理解,是用于解析Bean定义的属性的,包括scope、sington、abstract、lazy-init、autowire、dependency-check、depends-on、autowire-candidate、primary、init-method、destory-method、factory-method、factory-bean,createHelper部分说了这个方法会创建一个名为defaults的DocumentDefaultsDefinition,像lazy-init、autowire-candidate、init-method、destory-method未定义时都会尝试从DocumentDefaultsDefinition中获取
- 第19行用于设置Bean描述
- 第21行的方法parseMetaElements用于解析META元素
- 第22行的方法parseLookupOverrideSubElements用于解析<bean>标签下的<lookup-method>标签
- 第23行的方法parseReplacedMethodSubElements用于解析<bean>标签下的<replaced-method>标签,不过这个和<lookup-method>标签好像不太常用
- 第25行的方法parseConstructorArgElements用于解析<bean>标签下的<constructor-arg>标签,<constructor-arg>标签用于实现构造函数注入Bean属性
- 第26行的方法parsePropertyElements用于解析<bean>标签下的<property>标签,<property>标签是最常见的Bean属性注入的方式
- 第27行的方法parseQualifierElements用于解析<bean>标签下的<qualifier>标签,使用<qualifier>标签也是Spring属性注入的一种方式,不过不太常用
这样,就把整个Bean定义加载的流程跟完了,最后一步,就是将AbstractBeanDefinition写回到DefaultListableBeanFactory中了。
Bean定义加载流程----Bean定义写回DefaultListableBeanFactory
最后一步,将Bean定义写回DefaultListableBeanFactory中。代码要追溯回DefaultBeanDefinitionDocumentReader的processBeanDefinition方法:
1 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
2 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
3 if (bdHolder != null) {
4 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
5 try {
6 // Register the final decorated instance.
7 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
8 }
9 catch (BeanDefinitionStoreException ex) {
10 getReaderContext().error("Failed to register bean definition with name '" +
11 bdHolder.getBeanName() + "'", ele, ex);
12 }
13 // Send registration event.
14 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
15 }
16 }
Bean定义加载完毕后构造为一个BeanDefinitionHolder,第4行的代码之前说过的,用于在必要的情况下装饰Bean定义先不管。
第7行的代码用于注册Bean定义,跟一下代码:
1 public static void registerBeanDefinition(
2 BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
3 throws BeanDefinitionStoreException {
4
5 // Register bean definition under primary name.
6 String beanName = definitionHolder.getBeanName();
7 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
8
9 // Register aliases for bean name, if any.
10 String[] aliases = definitionHolder.getAliases();
11 if (aliases != null) {
12 for (String aliase : aliases) {
13 registry.registerAlias(beanName, aliase);
14 }
15 }
16 }
跟一下第7行的方法,调用DefaultListableBeanFactory的registerBeanDefinition方法:
1 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
2 throws BeanDefinitionStoreException {
3
4 Assert.hasText(beanName, "Bean name must not be empty");
5 Assert.notNull(beanDefinition, "BeanDefinition must not be null");
6
7 if (beanDefinition instanceof AbstractBeanDefinition) {
8 try {
9 ((AbstractBeanDefinition) beanDefinition).validate();
10 }
11 catch (BeanDefinitionValidationException ex) {
12 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
13 "Validation of bean definition failed", ex);
14 }
15 }
16
17 synchronized (this.beanDefinitionMap) {
18 Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
19 if (oldBeanDefinition != null) {
20 if (!this.allowBeanDefinitionOverriding) {
21 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
22 "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
23 "': There is already [" + oldBeanDefinition + "] bound.");
24 }
25 else {
26 if (this.logger.isInfoEnabled()) {
27 this.logger.info("Overriding bean definition for bean '" + beanName +
28 "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
29 }
30 }
31 }
32 else {
33 this.beanDefinitionNames.add(beanName);
34 this.frozenBeanDefinitionNames = null;
35 }
36 this.beanDefinitionMap.put(beanName, beanDefinition);
37
38 resetBeanDefinition(beanName);
39 }
40 }
简单说这个方法做了几件事情:
- beanDefinitionNames添加BeanName
- beanDefinitionMap添加一对映射,Key为BeanName,Value为Bean定义
- 第38行的方法用于重置一下所有本地缓存了的Bean定义
<bean>中不定义id及id重复场景Spring的处理方式
这两天又想到了两个细节问题,<bean>中不定义id或者id重复,这两种场景Spring是如何处理的。首先看一下不定义id的场景,代码在BeanDefinitionParserDelegate类第398行的这个判断这里:
1 if (beanDefinition != null) {
2 if (!StringUtils.hasText(beanName)) {
3 try {
4 if (containingBean != null) {
5 beanName = BeanDefinitionReaderUtils.generateBeanName(
6 beanDefinition, this.readerContext.getRegistry(), true);
7 }
8 else {
9 beanName = this.readerContext.generateBeanName(beanDefinition);
10 ...
11 }
当bean的id未定义时,即beanName为空,进入第2行的if判断。containingBean可以看一下,这里是由方法传入的,是一个null值,因此进入第9行的判断,即beanName由第9行的方法生成,看一下生成方式,代码最终要追踪到BeanDefinitionReaderUtils的generateBeanName方法:
1 public static String generateBeanName(
2 BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
3 throws BeanDefinitionStoreException {
4
5 String generatedBeanName = definition.getBeanClassName();
6 if (generatedBeanName == null) {
7 if (definition.getParentName() != null) {
8 generatedBeanName = definition.getParentName() + "$child";
9 }
10 else if (definition.getFactoryBeanName() != null) {
11 generatedBeanName = definition.getFactoryBeanName() + "$created";
12 }
13 }
14 if (!StringUtils.hasText(generatedBeanName)) {
15 throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
16 "'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
17 }
18
19 String id = generatedBeanName;
20 if (isInnerBean) {
21 // Inner bean: generate identity hashcode suffix.
22 id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
23 }
24 else {
25 // Top-level bean: use plain class name.
26 // Increase counter until the id is unique.
27 int counter = -1;
28 while (counter == -1 || registry.containsBeanDefinition(id)) {
29 counter++;
30 id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
31 }
32 }
33 return id;
34 }
这段代码的逻辑很容易看懂,即:
- 假如是innerBean(比如Spring AOP产生的Bean),使用【类全路径+#+对象HashCode的16进制】的格式来命名Bean
- 假如不是innerBean,使用【类全路径+#+数字】的格式来命名Bean,其中数字指的是,同一个Bean出现1次,只要该Bean没有id,就从0开始依次向上累加,比如a.b.c#0、a.b.c#1、a.b.c#2
接着看一下id重复的场景Spring的处理方式,重复id是这样的,Spring使用XmlBeanDefinitionReader读取xml文件,在这个类的doLoadBeanDefinitions的方法中:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
...
}
第5行的代码将xml解析成Document,这里的解析使用的是JDK自带的DocumentBuilder,DocumentBuilder处理xml文件输入流,发现两个<bean>中定义的id重复即会抛出XNIException异常,最终将导致Spring容器启动失败。
因此,结论就是:Spring不允许两个<bean>定义相同的id。
==================================================================================
我不能保证写的每个地方都是对的,但是至少能保证不复制、不黏贴,保证每一句话、每一行代码都经过了认真的推敲、仔细的斟酌。每一篇文章的背后,希望都能看到自己对于技术、对于生活的态度。
我相信乔布斯说的,只有那些疯狂到认为自己可以改变世界的人才能真正地改变世界。面对压力,我可以挑灯夜战、不眠不休;面对困难,我愿意迎难而上、永不退缩。
其实我想说的是,我只是一个程序员,这就是我现在纯粹人生的全部。
==================================================================================