zoukankan      html  css  js  c++  java
  • Spring源码解读(一)

    前期准备

    首先搭建一个简单的Spring Demo工程

    项目目录结构如下图所示:

    applicationContect.xml (可以取其他文件名,只要在加载配置文件时指定文件路径)

    <?xml version="1.0" encoding="UTF-8"?>
    <beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
        <bean id="chinese" class="com.bean.ChineseImpl">
            <property name="name">
                <value>小明</value>
            </property>
            <property name="age">
                <value>10</value>
            </property>
        </bean>
        <bean id="american" class="com.bean.AmericanImpl">
            <property name="name">
                <value>Tom</value>
            </property>
            <property name="age">
                <value>15</value>
            </property>
        </bean>
    </beans>

    Person.java

    package com.bean;
    
    public interface Person{
    
        public void Speak();
    }

    ChineseImpl.java

    package com.bean;
    
    public class ChineseImpl implements Person{
        private String name;
        private int age;
        
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public void Speak() {
            // TODO Auto-generated method stub
            System.out.println("I'm Chinese,My name is "+this.name+",I'm "+this.age+" years old!");
        }
    }

    AmericanImpl.java

    package com.bean;
    
    public class AmericanImpl implements Person{
        private String name;
        private int age;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        @Override
        public void Speak() {
            // TODO Auto-generated method stub
            System.out.println("I'm American,My name is "+this.name+",I'm "+this.age+" years old!");
        }
    }

    Test.java

    package com.spring;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import com.bean.Person;
    
    public class Test {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            Person person = (Person) context.getBean("chinese");
            person.Speak();
            person = (Person) context.getBean("american");
            person.Speak();
        }
    }

    下面将按照Spring初始化的过程:

    构造函数

    ClassPathXmlApplicationContext构造函数(ClassPathXmlApplicationContext[只能读放在web-info/classes目录下的配置文件],FileSystemXmlApplicationContext读具体路径)

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

    设置配置文件路径

    即AbstractRefreshableConfigApplicationContext.setConfigLocations

        public void setConfigLocations(String... locations) {
            if (locations != null) {
                Assert.noNullElements(locations, "Config locations must not be null");
                this.configLocations = new String[locations.length];
                for (int i = 0; i < locations.length; i++) {
                    this.configLocations[i] = resolvePath(locations[i]).trim();
                }
            }
            else {
                this.configLocations = null;
            }
        }

    resolvePath:

        protected String resolvePath(String path) {
            return getEnvironment().resolveRequiredPlaceholders(path);
        }

    此方法的目的在于将占位符(placeholder)解析成实际的地址。比如可以这么写: new ClassPathXmlApplicationContext("classpath:config.xml");那么classpath:就是需要被解析的

    refesh()

    Spring bean解析就在此方法,所以单独提出来。

    AbstractApplicationContext.refresh:

    @Override
        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();
                }
    
                catch (BeansException ex) {
                    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;
                }
            }
        }

    prepareRefresh

        protected void prepareRefresh() {
            this.startupDate = System.currentTimeMillis();
            this.active.set(true);
    
            if (logger.isInfoEnabled()) {
                logger.info("Refreshing " + this);
            }
    
            // Initialize any placeholder property sources in the context environment
            initPropertySources();
    
            // Validate that all properties marked as required are resolvable
            // see ConfigurablePropertyResolver#setRequiredProperties
            getEnvironment().validateRequiredProperties();
        }

    属性校验

    AbstractEnvironment.validateRequiredProperties:

        @Override
        public void validateRequiredProperties() throws MissingRequiredPropertiesException {
            this.propertyResolver.validateRequiredProperties();
        }

    AbstractPropertyResolver.validateRequiredProperties:

        @Override
        public void validateRequiredProperties() {
            MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
            for (String key : this.requiredProperties) {
                if (this.getProperty(key) == null) {
                    ex.addMissingRequiredProperty(key);
                }
            }
            if (!ex.getMissingRequiredProperties().isEmpty()) {
                throw ex;
            }
        }

    requiredProperties是通过setRequiredProperties方法设置的,保存在一个list里面,默认是空的,也就是不需要校验任何属性

    BeanFactory创建

    由 refesh() 中的 obtainFreshBeanFactory 调用 AbstractRefreshableApplicationContext.refreshBeanFactory:

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

    BeanFactory定制

    AbstractRefreshableApplicationContext.customizeBeanFactory方法用于给子类提供一个自由配置的机会,默认实现:

    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
            if (this.allowBeanDefinitionOverriding != null) {
                beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
            if (this.allowCircularReferences != null) {
                beanFactory.setAllowCircularReferences(this.allowCircularReferences);
            }
        }

    Bean加载

    AbstractXmlApplicationContext.loadBeanDefinitions,这个便是核心的bean加载了:

    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);
        }
  • 相关阅读:
    PHP开发APP接口(三)
    PHP开发APP接口(二)
    PHP开发APP接口(一)
    解密PHP模糊查询技术
    流程的问题
    德邦项目《表》
    微信公众号开发1
    在world2013中插入GB_2312
    HTML5笔记(一)
    蓝色文字显示
  • 原文地址:https://www.cnblogs.com/zyxiaohuihui/p/8621540.html
Copyright © 2011-2022 走看看