spring解析配置文件
Posted zhenhong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring解析配置文件相关的知识,希望对你有一定的参考价值。
一、从XmlBeanDefinitionReader的registerBeanDefinitions(doc,resource)开始
1 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) 2 throws BeanDefinitionStoreException { 3 try { 4 Document doc = doLoadDocument(inputSource, resource); 5 return registerBeanDefinitions(doc, resource); 6 }
进入第5行的registerBeanDefinitions方法
1 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { 2 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); 3 int countBefore = getRegistry().getBeanDefinitionCount(); 4 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 5 return getRegistry().getBeanDefinitionCount() - countBefore; 6 }
第二行创建了一个bean定义文档阅读器,创建的代码如下,第三行的getRegistry()方法得到是DefaultListableBeanFactory类的实例,是个bean工厂,这个工厂在准备读取xml时创建xml阅读器的时候就已经设置进去,getBeanDefinitions方法里的代码就这一句this.beanDefinitionMap.size(),获取bean定义容器的大小,很显然对于我这里是没有的,因为还没解析xml。
1 protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() { 2 return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass)); 3 }
在看看第4行的documentReader.registerBeanDefinitions这个方法
1 @Override 2 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { 3 this.readerContext = readerContext; 4 logger.debug("Loading bean definitions"); 5 Element root = doc.getDocumentElement(); 6 doRegisterBeanDefinitions(root); 7 }
第5行获得了xml的根元素<beans>然后调用
doRegisterBeanDefinitions方法,并把根元素传入进入
1 protected void doRegisterBeanDefinitions(Element root) { 2 // Any nested <beans> elements will cause recursion in this method. In 3 // order to propagate and preserve <beans> default-* attributes correctly, 4 // keep track of the current (parent) delegate, which may be null. Create 5 // the new (child) delegate with a reference to the parent for fallback purposes, 6 // then ultimately reset this.delegate back to its original (parent) reference. 7 // this behavior emulates a stack of delegates without actually necessitating one. 8 BeanDefinitionParserDelegate parent = this.delegate; 9 this.delegate = createDelegate(getReaderContext(), root, parent); 10 11 if (this.delegate.isDefaultNamespace(root)) { 12 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); 13 if (StringUtils.hasText(profileSpec)) { 14 String[] specifiedProfiles = StringUtils.tokenizeToStringArray( 15 profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); 16 if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { 17 return; 18 } 19 } 20 } 21 22 preProcessXml(root); 23 parseBeanDefinitions(root, this.delegate); 24 postProcessXml(root); 25 26 this.delegate = parent; 27 }
第9行创建了使用createDelegate方法创建了一个BeanDefinitionParserDelegate类的实例,它内部的代码如下
1 protected BeanDefinitionParserDelegate createDelegate( 2 XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) { 3 4 BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext); 5 delegate.initDefaults(root, parentDelegate); 6 return delegate; 7 }
第4行的readerContext是一个XMLReaderContext,继续往下调用了initDefaults方法,初始化默认值
1 public void initDefaults(Element root, BeanDefinitionParserDelegate parent) { 2 populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root); 3 this.readerContext.fireDefaultsRegistered(this.defaults); 4 }
进入populateDefaults方法
1 protected void populateDefaults(DocumentDefaultsDefinition defaults, DocumentDefaultsDefinition parentDefaults, Element root) { 2 String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE); 3 if (DEFAULT_VALUE.equals(lazyInit)) { 4 // Potentially inherited from outer <beans> sections, otherwise falling back to false. 5 lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE); 6 } 7 defaults.setLazyInit(lazyInit); 8 9 String merge = root.getAttribute(DEFAULT_MERGE_ATTRIBUTE); 10 if (DEFAULT_VALUE.equals(merge)) { 11 // Potentially inherited from outer <beans> sections, otherwise falling back to false. 12 merge = (parentDefaults != null ? parentDefaults.getMerge() : FALSE_VALUE); 13 } 14 defaults.setMerge(merge); 15 16 String autowire = root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE); 17 if (DEFAULT_VALUE.equals(autowire)) { 18 // Potentially inherited from outer <beans> sections, otherwise falling back to \'no\'. 19 autowire = (parentDefaults != null ? parentDefaults.getAutowire() : AUTOWIRE_NO_VALUE); 20 } 21 defaults.setAutowire(autowire); 22 23 // Don\'t fall back to parentDefaults for dependency-check as it\'s no longer supported in 24 // <beans> as of 3.0. Therefore, no nested <beans> would ever need to fall back to it. 25 defaults.setDependencyCheck(root.getAttribute(DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE)); 26 27 if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) { 28 defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)); 29 } 30 else if (parentDefaults != null) { 31 defaults.setAutowireCandidates(parentDefaults.getAutowireCandidates()); 32 } 33 34 if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) { 35 defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)); 36 } 37 else if (parentDefaults != null) { 38 defaults.setInitMethod(parentDefaults.getInitMethod()); 39 } 40 41 if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) { 42 defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)); 43 } 44 else if (parentDefaults != null) { 45 defaults.setDestroyMethod(parentDefaults.getDestroyMethod()); 46 } 47 48 defaults.setSource(this.readerContext.extractSource(root)); 49 }
第2行的DEFAULT_LAZY_INIT_ATTRIBUTE的值是default-lazy-init,第7行设置懒加载,还有默认的初始化方法什么的,把这些个属性全都设置到
DocumentDefaultsDefinition 中。这个方法返回后继续调用了XmlReaderContext的fireDefaultsRegistered方法,不过里面啥都没做。
接下来有回到了doRegisterBeanDefinitions方法
1 protected void doRegisterBeanDefinitions(Element root) { 2 // Any nested <beans> elements will cause recursion in this method. In 3 // order to propagate and preserve <beans> default-* attributes correctly, 4 // keep track of the current (parent) delegate, which may be null. Create 5 // the new (child) delegate with a reference to the parent for fallback purposes, 6 // then ultimately reset this.delegate back to its original (parent) reference. 7 // this behavior emulates a stack of delegates without actually necessitating one. 8 BeanDefinitionParserDelegate parent = this.delegate; 9 this.delegate = createDelegate(getReaderContext(), root, parent); 10 11 if (this.delegate.isDefaultNamespace(root)) { 12 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); 13 if (StringUtils.hasText(profileSpec)) { 14 String[] specifiedProfiles = StringUtils.tokenizeToStringArray( 15 profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); 16 if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { 17 return; 18 } 19 } 20 } 21 22 preProcessXml(root); 23 parseBeanDefinitions(root, this.delegate); 24 postProcessXml(root); 25 26 this.delegate = parent; 27 }
第11行判断当前的根元素是否是默认的命名空间,spring中存在着两种标签,一种是默认的标签,另一种是自定义标签
第12行获得profile属性,这个属性应用于对个beans标签的情况,从spring3开始的,这样我们可以写多套bean定义,特别是使用到数据源的时候,可以切换不同的数据源,想要使用哪个bean定义就激活谁,想详细了解的,可以去查查资料。
第22行是个空方法,里面什么也没做,这个地方可以进行扩展,在解析bean定义之前,可以先处理自定义的标签
第23行方法的代码如下
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 }
第2行判断root是否是默认的命名空间,第3行获得根元素下的子节点,循环遍历子节点,第8行判断每个子节点是否是默认的命名空间,如果是就执行parseDefaultElement方法,否则
执行parseCustomElement方法,什么是自定义的标签呢,比如用户自己实现的标签,还有就是spring的aop,tx这类标签都是有自定义的命名空间的标签
我们进到parseCustomElement方法中看看
1 public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { 2 String namespaceUri = getNamespaceURI(ele); 3 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 4 if (handler == null) { 5 error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); 6 return null; 7 } 8 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); 9 }
第2行获取这个元素的命名空间,通过命名空间可以在类路径下的spring.handlers中找到对应的处理器,在spring.schemas中找到对应的xsd文件。进入resolve方法查看一下他的
代码逻辑
1 @Override 2 public NamespaceHandler resolve(String namespaceUri) { 3 Map<String, Object> handlerMappings = getHandlerMappings(); 4 Object handlerOrClassName = handlerMappings.get(namespaceUri); 5 if (handlerOrClassName == null) { 6 return null; 7 } 8 else if (handlerOrClassName instanceof NamespaceHandler) { 9 return (NamespaceHandler) handlerOrClassName; 10 } 11 else { 12 String className = (String) handlerOrClassName; 13 try { 14 Class<?> handlerClass = ClassUtils.forName(className, this.classLoader); 15 if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { 16 throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + 17 "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); 18 } 19 NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); 20 namespaceHandler.init(); 21 handlerMappings.put(namespaceUri, namespaceHandler); 22 return namespaceHandler; 23 } 24 catch (ClassNotFoundException ex) { 25 throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + 26 namespaceUri + "] not found", ex); 27 } 28 catch (LinkageError err) { 29 throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + 30 namespaceUri + "]: problem with handler class file or dependent class", err); 31 } 32 } 33 }
第3行代码getHandlerMappings()获得所有的命名空间和处理程序的映射
1 private Map<String, Object> getHandlerMappings() { 2 if (this.handlerMappings == null) { 3 synchronized (this) { 4 if (this.handlerMappings == null) { 5 try { 6 Properties mappings = 7 PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); 8 if (logger.isDebugEnabled()) { 9 logger.debug("Loaded NamespaceHandler mappings: " + mappings); 10 } 11 Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size()); 12 CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); 13 this.handlerMappings = handlerMappings; 14 } 15 catch (IOException ex) { 16 throw new IllegalStateException( 17 "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex); 18 } 19 } 20 } 21 } 22 return this.handlerMappings; 23 }
进入第7行看看
1 public static Properties loadAllProperties(String resourceName, ClassLoader classLoader) throws IOException { 2 Assert.notNull(resourceName, "Resource name must not be null"); 3 ClassLoader classLoaderToUse = classLoader; 4 if (classLoaderToUse == null) { 5 classLoaderToUse = ClassUtils.getDefaultClassLoader(); 6 } 7 Enumeration<URL> urls = (classLoaderToUse != null ? classLoaderToUse.getResources(resourceName) : 8 ClassLoader.getSystemResources(resourceName)); 9 Properties props = new Properties(); 10 while (urls.hasMoreElements()) { 11 URL url = urls.nextElement(); 12 URLConnection con = url.openConnection(); 13 ResourceUtils.useCachesIfNecessary(con); 14 InputStream is = con.getInputStream(); 15 try { 16 if (resourceName.endsWith(XML_FILE_EXTENSION)) { 17 props.loadFromXML(is); 18 } 19 else { 20 props.load(is); 21 } 22 } 23 finally { 24 is.close(); 25 } 26 } 27 return props; 28 }
它是通过加载器加载classpath下的META-INF/spring.handler文件,使用jdk的Properties方法加载,加载完后返回properties的实例,最后拿到对应的标签处理器
拿到命名空间处理器的类名后
1 NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); 2 namespaceHandler.init(); 3 handlerMappings.put(namespaceUri, namespaceHandler); 4 return namespaceHandler;
第1行使用bean助手类实例化了这个命名空间处理器,并进行了初始化,加入我显示使用的是aop的命名空间,那么这个命名空间处理器是AopNamespaceHandler类的实例
它对应init方法代码如下:这个aop命名空间处理器中拥有下面这些类别的标签解析器,第一个我们是最属性的,这个解析器里提供了aop:config标签中pointcut,before之类的解析方法
第3行将初始化好的命名空间处理器放到handlerMappings中,如果下次要使用就直接可以拿到,不需要在此实例化
@Override public void init() { // In 2.0 XSD as well as in 2.1 XSD. registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser()); registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser()); registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator()); // Only in 2.0 XSD: moved to context namespace as of 2.1 registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); }
下面是一些bean定义解析器的继承结构,各种各样的解析器
向处理器中注册解析器
1 protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) { 2 this.parsers.put(elementName, parser); 3 }
第2行的parsers是一个Map,用来存放bean定义解析器
得到命名空间处理器后再调用命名空间处理器的parse方法handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
进入parse方法,这个方法又调用了findParserForElement方法
1 private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
2 String localName = parserContext.getDelegate().getLocalName(element); 3 BeanDefinitionParser parser = this.parsers.get(localName); 4 if (parser == null) { 5 parserContext.getReaderContext().fatal( 6 "Cannot locate BeanDefinitionParser for element [" + localName + "]", element); 7 } 8 return parser; 9 }
第2行,通过这个元素拿到了它的本地名字,比如aop:config元素的本地名为config
通过config从命名空间中的解析器map容器中拿到了对应的解析器,然后调用这个aop的config解析器的parse方法
1 @Override 2 public BeanDefinition parse(Element element, ParserContext parserContext) { 3 CompositeComponentDefinition compositeDef = 4 new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element)); 5 parserContext.pushContainingComponent(compositeDef); 6 7 configureAutoProxyCreator(parserContext, element); 8 9 List<Element> childElts = DomUtils.getChildElements(element); 10 for (Element elt: childElts) { 11 String localName = parserContext.getDelegate().getLocalName(elt); 12 if (POINTCUT.equals(localName)) { 13 parsePointcut(elt, parserContext); 14 } 15 else if (ADVISOR.equals(localName)) { 16 parseAdvisor(elt, parserContext); 17 } 18 else if (ASPECT.equals(localName)) { 19 parseAspect(elt, parserContext); 20 } 21 } 22 23 parserContext.popAndRegisterContainingComponent(); 24 return null; 25 }
第三行创建了一个组件定义,并将它压入ParseContext上下文中的stack中
进入第7行的方法
1 private void configureAutoProxyCreator(ParserContext parserContext, Element element) { 2 AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element); 3 }
这个方法是在配置自动代理创建者,这个方法在配置了<aop:config>才使用,如果你配置的是<aop:aspect-autoproxy/>这个标签,就会使用AspectJAutoProxyBeanDefinitionParser这个类的解析器。回到aop:config这个标签的解析器,上面的第2行使用了AopNamespaceUtils aop命名空间助手类注册一个自动代理切面创建者,进入这个方法看看
1 public static void registerAspectJAutoProxyCreatorIfNecessary( 2 ParserContext parserContext, Element sourceElement) { 3 4 BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary( 5 parserContext.getRegistry(), parserContext.extractSource(sourceElement)); 6 useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement); 7 registerComponentIfNecessary(beanDefinition, parserContext); 8 }
第4行调用了registerAspectJAutoProxyCreatorIfNecessary的重载方法,这个方法传入了BeanRegistry类的对象(这里的这个实例实际上是DefaultListableBeanFactory)和ParseContext包装后的可提取资源实例
进入这个方法查看一下它的代码
1 public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) { 2 return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source); 3 }
第2行传入了一个AspectJAwareAdvisorAutoProxyCreator.class类对象,暂且不管它是干啥的,继续往下看
1 private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) { 2 Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); 3 if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { 4 BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); 5 if (!cls.getName().equals(apcDefinition.getBeanClassName())) { 6 int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); 7 int requiredPriority = findPriorityForClass(cls); 8 if (currentPriority < requiredPriority) { 9 apcDefinition.setBeanClassName(cls.getName()); 10 } 11 } 12 return null; 13 } 14 RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); 15 beanDefinition.setSource(source); 16 beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); 17 beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 18 registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); 19 return beanDefinition; 20 }
第3行判断这个BeanFactory中的BeanDefinitionMap容器中是否存在一个key叫做AUTO_PROXY_CREATOR_BEAN_NAME
(org.springframework.aop.config.internalAutoProxyCreator)的元素,如果存在就获得他的BeanDefinition对象,并且与我们传进来的AspectJAwareAdvisorAutoProxyCreator
类名是否一样,如果一样就要根据优先级来选择其中一个作为自动创建代理类,在AopConfigUtils类中有个静态的属性list集合APC_PRIORITY_LIST,
它在静态块中初始化
1 private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<Class<?>>(); 2 3 /** 4 * Setup the escalation list. 5 */ 6 static { 7 APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class); 8 APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class); 以上是关于spring解析配置文件的主要内容,如果未能解决你的问题,请参考以下文章Spring boot:thymeleaf 没有正确渲染片段