Spring宏观上简单梳理分析一

关于Spring框架,说的最多的就是IOC和AOP了,即依赖注入和切面编程。这些也是使用spring过程中要用的最基本的东西了。但是,spring是如何进行运转的呢?这是一个问题呀,我们起码应该要明白spring的工作流程。

思考:

  spring常用的方式有注解和xml配置两种方式,根据我们的猜测,这两种方式殊路同归。Spring首先要做的事,就是解析xml(找到注解的类),根据解析出来的文档进行各种类的信息组装,最后当需要某个类的实例时,根据反射、利用组装好的class信息,构造出我们想要的实例;

其实,主体思路确实如我们所想的那样,走的就是这个流程。但是,spring在其中添加了很多的检查工作,来保证安全性。接下来,我们简单分析一下;

过程分析(Spring4.x):

 1.加载配置文件

      

    String file="spring.xml";
    Resource source=new ClassPathResource(file);
    BeanFactory factory=new XmlBeanFactory(source);

这是一段最简单的将xml文件加载到spring容器中,并从容器中获得注册Bean的代码了。那我们来看看XmlBeanFactory的代码:

 1 public class XmlBeanFactory extends DefaultListableBeanFactory {
 2 
 3     private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
 4 
 5 
 6     /**
 7      * Create a new XmlBeanFactory with the given resource,
 8      * which must be parsable using DOM.
 9      * @param resource XML resource to load bean definitions from
10      * @throws BeansException in case of loading or parsing errors
11      */
12     public XmlBeanFactory(Resource resource) throws BeansException {
13         this(resource, null);
14     }
15 
16     /**
17      * Create a new XmlBeanFactory with the given input stream,
18      * which must be parsable using DOM.
19      * @param resource XML resource to load bean definitions from
20      * @param parentBeanFactory parent bean factory
21      * @throws BeansException in case of loading or parsing errors
22      */
23     public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
24         super(parentBeanFactory);
25         this.reader.loadBeanDefinitions(resource);
26     }
27 
28 }

从源码看出,实际的加载xml文件即解析xml文件为Bean的工作是交给XmlBeanDefinitionReader来完成的,那我们再看看这个类的loadBeanDefinitions方法:

@Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(resource));
    }

    
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }

        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<EncodedResource>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        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());
                }
                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();
            }
        }
    }

可以看出,XmlBeanDefinitionReader类会先将Resource封装成EncodedResource类,并调用重载方法来完成解析工作。重载方法里关键部分是一下代码:

      InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }

上述代码就是从Resource对象中获得输入流,即输入的xml文件,并封装成InputSource类,交给doLoadBeanDefinitions方法来进行解析;

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            Document doc = doLoadDocument(inputSource, resource);
            return registerBeanDefinitions(doc, resource);
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (SAXParseException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        }
        catch (SAXException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "XML document from " + resource + " is invalid", ex);
        }
        catch (ParserConfigurationException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Parser configuration exception parsing XML from " + resource, ex);
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "IOException parsing XML document from " + resource, ex);
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Unexpected exception parsing XML document from " + resource, ex);
        }
    }

这里面要关注的就是:

    Document doc = doLoadDocument(inputSource, resource);
       return registerBeanDefinitions(doc, resource);

这两句代码,其中第一句就是讲xml文件解析成Ducnment对象,第二句是通过分析Document对象来注册类。这里我们来看第二个方法,第一个知道在这里xml文件被转为Document对象就行了;

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

看得出来,在这个方法中又创建了一个BeanDefinitionDocumentReader对象,利用他来解析Document对象内容。其中BeanDefinitionDocumentReader是个接口,他只有一个实现类DefaultBeanDefinitionDocumentReader,所以我们进入这个实现类的registerBeanDefinitions方法中去查看

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();
        doRegisterBeanDefinitions(root);
    }

一般spring里面do打头的方法,就是真正干活的方法了,让我们进doRegisterBeanDefinitions看看:

protected void doRegisterBeanDefinitions(Element root) {
        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);
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("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(root, this.delegate),在这里面有实际解析Document文件的步骤。另外preProcessXml(root)和postProcessXml(root)方法都是空实现,应该是留给用户自定义的。至于前面一段代码,是处理profile标签的内容;

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);
        }
    }

这个方法,循环解析Document中的每个节点(Bean),并判断是默认标签节点(bean,import,beans,alias)还是用户自定义标签节点(例如aop,tx),然后采用对应的解析方法parseDefaultElement(ele, delegate)和delegate.parseCustomElement(ele)。具体的解析标签,以后再说了。至此,我们大致明白了spring是怎么找到xml文件并将它解析为bean信息的宏观结构流程了。接下来,我们就该讨论一下,spring是怎么去获取这些bean信息创建出来的实例了。

ps.采用Applicationcontext的分析bean的步骤类似,接下来也接单看看;

首先我们打开ClassPathXmlApplicationContext的源码,该类变相实现了ApplicationContext接口;

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
...
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[] {configLocation}, true, null);
    }
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }
}

构造函数接受一个string类型的值,用来表示xml配置文件地址。重点是ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)这个方法,其中的setConfigLocations(configLocations)方法是将string解析,并设置为configLocations的值,否则spring会有默认值。实际的重点是refresh()方法,该方法里实现了beanfactory的初始化,即上面描述的加载解析bean信息的过程。另外,还有国际化等其他的操作。refresh方法属于AbstractApplicationContext这个抽象类:

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // 这里面就是初始化beanfactory
            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) {
                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }
        }
    }

上面的ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()方法就是加载beanfactory的方法

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

其中refreshBeanFactory()方法是初始化beanfactory地方法,在该类中是抽象方法,由子类AbstractRefreshableApplicationContext实现

@Override
    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);
        }
    }

我们直接看loadBeanDefinitions(beanFactory)方法,该方法也是抽象方法,由子类完成。

@Override
    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(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);
    }

看到这里,就和上面的步骤联系起来了,也是采用XmlBeanDefinitionReader来完成xml文件解析和bean的加载;

 

 

 

  

    

posted @ 2017-08-18 13:14  jkavor  阅读(208)  评论(0编辑  收藏  举报