一、问题
我们在spring的xml配置文件里经常定义各种各样的配置(tx、bean、mvc、bean等等)。以及集成第三方框架时,也会看到一些spring之外的配置,例如dubbo的配置、security的配置、redis的配置等等。spring作为一个庞大的容器,到底是怎么工作的呢?这篇文章将简单的介绍一下其实现机制,具体细节朋友们可以自己查看相关代码。
二、代码分析
稍微了解一点spring的朋友都知道spring的入口方法就是refresh(),所以我们直接进入refresh()方法【不清楚的可以查看一下ClassPathXmlApplicationContext,一步一步跟踪进去】。
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 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); // 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(); } ...由于篇幅,我省略了一部分
而解析spring配置的入口就在obtainFreshBeanFactory().refreshBeanFactory()中:
protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(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); } }
我们首先会告诉spring的配置文件所在路径,即setLocations(),spring创建完BeanFactory,接下来要做的就是解析配置,完成其他各种属性的封装(BeanDefinition、各种代理、映射关系等等)。这里的loadBeanDefinitions就是解析配置的核心方法。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. 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); }
DefaultListableBeanFactory 作为BeanFactory的一个核心实例,且实现了BeanDefinitionRegistry接口,在解析之前当然是要给配置文件解析器(XmlBeanDefinitionReader)设置一个存放BeanDefinition的容器(即将注册bean的功能委托给BeanFactory)。然后配置文件解析器就开始它的解析工作了。
我们直接定位到XmlBeanDefinitionReader的doLoadBeanDefinitions方法:
try { Document doc = doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { throw ex; }
利用w3c的DOM将配置文件包装成了一个Document对象,接下来就是利用Dom来解析childNode相关属性。
接下来,我们沿着代码定位到DefaultBeaDefinitionDocumentReader中的的parseDefinitions方法:
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; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
核心部分来了,spring迭代根节点<beans/>,一个一个的解析其子节点.如果是<beans/>节点,则走parseDefaultElement方法,我们这里关注的是parseCustomElement():
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(ele); NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
根据元素的namespaceUri(也就是xml文件头那一串schema)找到对应的NamespaceHandler.【这里就是扩展自定义配置的入口】
我们接下来看一下resolve()方法:
public NamespaceHandler resolve(String namespaceUri) { Map<String, Object> handlerMappings = getHandlerMappings(); Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } else if (handlerOrClassName instanceof NamespaceHandler) { return (NamespaceHandler) handlerOrClassName; } else { String className = (String) handlerOrClassName; try { Class<?> handlerClass = ClassUtils.forName(className, this.classLoader); if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); namespaceHandler.init(); handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } catch (ClassNotFoundException ex) { throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", ex); } catch (LinkageError err) { throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", err); } } }
对应的NamespaceHandler就是根据handlerMappings找到匹配关系的,所以我们只需要看DefaultNamespaceHandlerResolver.getHandlerMappings()即可。
private Map<String, Object> getHandlerMappings() { if (this.handlerMappings == null) { synchronized (this) { if (this.handlerMappings == null) { try { Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);//handlerMappingsLocation默认的路径就是META-INF/spring.handlers if (logger.isDebugEnabled()) { logger.debug("Loaded NamespaceHandler mappings: " + mappings); } Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size()); CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); this.handlerMappings = handlerMappings; } catch (IOException ex) { throw new IllegalStateException( "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex); } } } } return this.handlerMappings; }
spring将classpath下的META-INF/spring.handlers中的配置加载到Map中.这个spring.handler文件就是以NamespaceUrl作为key,对应的Handler作为value的键值对,例如:
···
http://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
···
当我们解析到<context/>配置的时候,我们会根据context的schema,也就是[http://www.springframework.org/schema/context]作为key找到对应的ContextNamespacehandler进行解析的,一切都清楚了。
三、总结
spring这点做得很强大,利用这种类似于Serviceloader的功能来平等对待第三方,方便别人扩展集成。我们也要停下来想一想,当我们自己开发这种核心组件的时候一定要做到"平等对待第三方"以及核心足够小,依赖少。这也是一种修行!
作者:jerrik
链接:https://www.jianshu.com/p/91f6068adff2
共同学习,写下你的评论
评论加载中...
作者其他优质文章