谈到spring,首先想到的肯定是ioc,DI依赖注入,aop,但是其实很多人只是知道这些是spring核心概念,甚至不知道这些代表了什么意思,,作为一个java程序员,怎么能说自己对号称改变了java生态的spring不了解呢。
首先说一下spring做了啥,他将我们会频繁用到的javaBean交给spring的容器管理,在bean创建的不同阶段,可以在不同的容器中找到,说到底这些容器就是map缓存,有了spring我们就有了管家,可以过过当老爷的瘾,再也不用担心对象啥时候创建,初始化,销毁的问题了,用就完事了。
说了这么多废话,还是直接撸源码实在,首先看spring源码入口:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { //为容器初始化做准备 // Prepare this context for refreshing. prepareRefresh(); /* 1、创建BeanFactory对象 * 2、xml解析 * 传统标签解析:bean、import等 * 自定义标签解析 如:<context:component-scan base-package="com.xiangxue.jack"/> * 自定义标签解析流程: * a、根据当前解析标签的头信息找到对应的namespaceUri * b、加载spring所以jar中的spring.handlers文件。并建立映射关系 * c、根据namespaceUri从映射关系中找到对应的实现了NamespaceHandler接口的类 * d、调用类的init方法,init方法是注册了各种自定义标签的解析类 * e、根据namespaceUri找到对应的解析类,然后调用paser方法完成标签解析 * * 3、把解析出来的xml标签封装成BeanDefinition对象 * */ // 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(); } 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(); } } }
这一个方法,包含了所有spring的操作,spring的代码果然是美如画,加上trycatch才十几行代码就改变了java的生态。
prepareRefresh();首先看看这个方法,有兴趣的可以自己看看,就是刷新上下文,设置启动日期之类的。
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();我们主要看看这一行代码,进去看看
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { //核心方法 refreshBeanFactory(); return getBeanFactory(); }
好像还看不到啥关键的,再看看refreshBeanFactory();
protected final void refreshBeanFactory() throws BeansException { //如果BeanFactory不为空,则清除BeanFactory和里面的实例 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //创建DefaultListableBeanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); //设置是否可以循环依赖 allowCircularReferences //是否允许使用相同名称重新注册不同的bean实现. customizeBeanFactory(beanFactory); //解析xml,并把xml中的标签封装成BeanDefinition对象 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
1.首先做了判断,如果已经有了BeanFactory,就给他干掉,毕竟一山不容二虎嘛。
2.其次就是创建BeanFactory,其实里面就是new 了一个DefaultListableBeanFactory对象
3.设置循环依赖标识(默认就是支持的)
4.解析xml,封装成BeanDefinition对象(这个对象可是所有bean的胚胎,孕育着所有的bean)
好了,看到这四步应该都知道哪个重要了吧,我们进去看看xml解析是怎么做的:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. //创建xml的解析器,这里是一个委托模式 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); //这里传一个this进去,因为ApplicationContext是实现了ResourceLoader接口的 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); }
1.首先用了一记委托模式(很多地方都用到了,就不一一介绍了,平时看源码的时候,稍微注意一点应该很容易发现),将xml解析的工作交给了XmlBeanDefinitionReader,一直往下调用loadBeanDefinitions直到:org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions
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; }
这边使用了模板设计模式,点进去任意选择一个类,可以看到都是实现了AbstractBeanDefinitionReader类的,选择xml解析实现:
//获取Resource对象中的xml文件流对象 InputStream inputStream = encodedResource.getResource().getInputStream(); try { //InputSource是jdk中的sax xml文件解析对象 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //主要看这个方法 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); }
获取xml解析对象,继续向下:
//把inputSource 封装成Document文件对象,这是jdk的API Document doc = doLoadDocument(inputSource, resource); //主要看这个方法,根据解析出来的document对象,拿到里面的标签元素封装成BeanDefinition int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count;
再看如何将bean封装成BeanDefinition对象的:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //又来一记委托模式,BeanDefinitionDocumentReader委托这个类进行document的解析 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); //主要看这个方法,createReaderContext(resource) XmlReaderContext上下文,封装了XmlBeanDefinitionReader对象 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
又一个委托模式,这是个老板啊,啥都让别人来做,再往下看:registerBeanDefinitions(doc, createReaderContext(resource));一直往下点:
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; 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; }
好像很复杂,好像啥也没干。可以看到preProcessXml和postProcessXml方法里面啥也没有,明显是个钩子方法,主要看parseBeanDefinitions(root, this.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; if (delegate.isDefaultNamespace(ele)) { //默认标签解析 parseDefaultElement(ele, delegate); } else { //自定义标签解析 delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
默认标签和自定义标签解析还是比较复杂的,看来一篇是讲不完了,下一篇再好好讲讲这一块。
总结:
写到现在,spring连标签解析都没开始,我们已经看到两个设计模式了,用心去看的话,工厂模式也是到处都在,单例模式贯穿了spring。在spring里面用到了很多设计模式和技巧,我们在看源码的同时,稍稍注意一下这些技巧的使用,并想一想,如果是我们要实现这些功能会怎么做,像spring这样的做法是否比你的想法更好呢,或者,如果你是大牛,你是否有更好的方法去实现spring的这一功能,如果有,去github上提交spring,那你在it界就小有名气了,嘿嘿!