美文网首页首页投稿Java技术栈
Spring容器的基本实现之源码分析(上篇)

Spring容器的基本实现之源码分析(上篇)

作者: 程序员七哥 | 来源:发表于2019-01-29 07:55 被阅读11次

转眼间毕业已经两年多了,工作中一直也在使用Spring,中途也陆续的看过其中的用法以及部分的实现原理。 最近工作之余想整体的看一下Spring的具体原理,研究下源码,理解下这个经典框架的设计思路,以及其中的设计模式。 不过开始看了一周后,感觉也是似懂非懂,回想起来总是不知道该从哪说起。

为了防止这次看了又跟没看一样的惨剧发生,所以决定在看的过程中,记录一下相关细节。 说实话准备开始写这篇文章时,我还是有点不知道从哪说起,不过还是决定硬着头皮写吧,总要尝试一下的嘛。

这篇文章主要说明 Spring IOC 相关的源代码实现,使用 xml 配置的方式,虽然大家在使用中基本上不会使用这种方式,最起码不是完全使用 XML 配置,不过从研究源代码的角度,这种方式无疑是最合适的,理解了xml配置的方式,注解的实现基本上原理也是类似的过程,也许在过几天我抽空也会写篇文章讲解下注解实现的相关源代码。

由于文章篇幅较长,这里分为上下两篇,此篇为上篇,包含目录如下:

目录.png

《Spring容器基本实现之源码分析-下篇 传送门》

Spring容器的功能加载

ApplicationContext bf = new ClassPathXmlApplicationContext("beans.xml");

如上面这行代码,我们使用ApplicationCdontext方式加载XML。以ClassPathXmlApplicationContext作为切入点,开始对整体功能进行分析。

    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {

        super(parent);
        // 设置路径
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }

如上,设置路径是必不可少的步骤。

容器启动流程

设置好了路径之后,便可以根据路径做配置文件的解析以及各种功能的实现了。而Spring容器的整个启动过程几乎全部都在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.
            // 初始化BeanFactory,并进行XML文件读取
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            // 对BeanFactory进行各种功能填充
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                // 子类覆盖方法做额外的处理
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                // 激活各种BeanFactory处理器
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                // 注册拦截Bean创建的Bean处理器,此处只是注册,真正的调用在getBean时发生
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                // 为上下文初始化Message源,国际化处理
                initMessageSource();

                // Initialize event multicaster for this context.
                // 初始化应用消息广播器,并放入"applicationEventMulticaster"bean中
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                // 留给子类来初始化其他的Bean
                onRefresh();

                // Check for listener beans and register them.
                // 在所有注册的bean中查找listener bean,注册到消息广播器中
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                // 初始化剩下的所有单实例bean(非延迟加载)
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                // 完成刷新过程,通知生命周期处理器lifeCycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
                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();
            }
        }
    }

流程概括

下面主要概括一下上面初始化步骤,并简要解释一下它为我们提供的功能。

(1) 初始化前的准备工作,例如对系统属性以及环境变量进行准备和验证。

​ 在某些情况下,项目的使用需要读取某些系统变量,而这个变量可能影响着系统的正确性,那么prepareRefresh这些准备函数就显得非常必要,它可以在Spring启动前对必须的变量进行验证。

(2) 初始化BeanFactory,并进行XML文件读取

​ 这一步骤会复用BeanFactory中的配置文件读取解析以及其他功能,这一步之后,其实ClasspathXmlApplicationContext实际上就已经包含了BeanFactory所提供的功能,也就是可以进行Bean的提取等基础操作了。

(3) 对BeanFactory进行各种功能补充

​ @Qualifier与@Autowired应该是大家非常熟悉的注解,那么这两个注解正是在这一步骤中增加的支持

(4) 子类覆盖方法做额外的处理

​ Spring之所以强大,为世人所推崇,除了它功能上为大家提供了便利外,还有一方面是它的完美架构,开放式的架构让使用它的程序员很容易根据实际业务做相应的功能扩展。这种开放式的设计在Spring中随处可见,例如本例中postProcessBeanFactory函数就是提供的空函数用来让程序员在业务上做进一步的扩展。

(5) 激活各种BeanFactory处理器。

(6) 注册各种拦截Bean创建的bean处理器,这里只是注册,真正的调用在getBean的时候。

(7) 为上下文初始化Message源,即对不同的语言消息体进行国际化处理。

(8) 初始化应用消息广播器,并放入applicationEventMulticaster中。

(9) 留给子类来初始化其他的bean。

(10) 在所有注册的bean中查找 listener bean ,注册到消息广播器中。

(11)初始化剩下的单实例(非惰性加载)bean。

(12) 完成刷新过程,通知生命周期处理器 lifecycleProcessor 刷新过程,同时发出 ContextRefreshEvent通知别人。

一探究竟

上面我们分析了Spring容器启动要经历的基本流程,接下来我们来一起探究具体每一步都做了什么事情。

环境准备

prepareRefresh函数主要是做准备工作,例如对系统属性的以及环境变量的初始化以及验证。

    /**
     * Prepare this context for refreshing, setting its startup date and
     * active flag as well as performing any initialization of property sources.
     */
    protected void prepareRefresh() {
        this.startupDate = System.currentTimeMillis();
        this.closed.set(false);
        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();

        // Allow for the collection of early ApplicationEvents,
        // to be published once the multicaster is available...
        this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
    }

我在第一次看到这个函数时,觉得好像没什么用,因为能看出来最后两句代码是关键,可能却没有什么逻辑,initPropertySources 是空的,而且 getEnvironment().validateRequiredProperties() 也因为没有需要验证的属性而没有任何处理。其实不然,是因为没有彻底理解才会有上面的错觉,这个函数如果用好了,作用是非常大的。下面先说下每个函数的作用,再结合一个例子我们一起看下。

(1) initPropertySources 正符合 Spring 的开放式设计,给用户最大扩展 Spring 的能力。用户可以根据自身的需要重写 initPropertySources 方法,并在方法中进行个性化的属性处理及设置。

(2) validateRequiredProperties 则是对属性的验证,那么如何验证呢?

假如,现在有这样一个需求,工程在运行过程中,用到的某个设置(例如PATH)是从系统环境变量中取得的,如果用户没有在系统环境变量中配置这个属性,那么运行启动肯定会报错。这一要求在 Spring 中可以这样做,你可以直接修改 Spring 的源码,例如修改ClasspathXmlApplicationContext。当然最好的方法还是对 Spring 源码进行扩展,如下:

public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext{

    public MyClassPathXmlApplicationContext(String configLocation) throws BeansException {
        super(configLocation);
    }

    @Override
    protected void initPropertySources() {
        // 添加验证要求
        getEnvironment().setRequiredProperties("PATH");

    }
}

​ 这里自定义了继承 ClassPathXmlApplicationContextMyClassPathXmlApplicationContext 并重写了 initPropertySources 方法,在方法中添加了我们的个性化需求,那么在验证的时候也就是程序走到 getEnvironment().validateRequiredProperties() 的时候,如果系统系统检测并没有需要的PATH 环境变量,那么将抛出异常。当然我们需要在使用的时候替换ClassPathXmlApplicationContext

    @Test
    public void testInitProperties() {
        ApplicationContext applicationContext = new MyClassPathXmlApplicationContext("beans.xml");
        Dog dog = (Dog) applicationContext.getBean("dog");
    }

加载 BeanFactory

obtainFreshBeanFactory 方法从字面上理解就是获取 BeanFactory 。 上面也有提过 ApplicatioContext 是对BeanFactory 的功能上的扩展,不但包含了 BeanFactory 的全部功能更在其基础上添加了大量的扩展应用,那么obtainFreshBeanFactory 正是实现 BeanFactory 的地方,也就是说经过了这个函数后 ApplicationContext 就已经拥有了 BeanFactory 的全部功能。

    /**
     * Tell the subclass to refresh the internal bean factory.
     * @return the fresh BeanFactory instance
     * @see #refreshBeanFactory()
     * @see #getBeanFactory()
     */
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        // 初始化BeanFactory,并进行XML文件读取,将得到的BeanFactory记录到当前实体的属性中
        refreshBeanFactory();
        // 返回当前实体的BeanFactory属性
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }


    /**
     * This implementation performs an actual refresh of this context's underlying
     * bean factory, shutting down the previous bean factory (if any) and
     * initializing a fresh bean factory for the next phase of the context's lifecycle.
     */
// 此方法为核心实现
    @Override
    protected final void refreshBeanFactory() throws BeansException {
        // 关闭已经存在的beanFactory
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            // 创建DefaultListableBeanFactory
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            // 为了序列化指定id,如果需要的话让其从id反序列化为BeanFactory对象
            beanFactory.setSerializationId(getId());
            // 定制beanFactory,设置相关属性,包括是否允许覆盖同名称不同定义的对象以及循环依赖,以及设置@Autowired和@Qualifier注解解析器 QualifierAnnotationAutowireCandidateResolver
            customizeBeanFactory(beanFactory);
            // 初始化DocumentReader,并进行XML文件读取及解析
            loadBeanDefinitions(beanFactory);
            // 使用全局变量记录BeanFactroy类实例
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

定制 BeanFactory

上面已经提到这里开始了对 BeanFactory 的扩展,再基本容器的基础上,增加了是否允许覆盖是否允许扩展的设置并提供了注解 @Qualifier 和 @Autowired 的支持。

    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        // 如果allowBeanDefinitionOverriding 属性不为空,设置给beanFactory相应的属性,此属性的含义:是否允许覆盖同名称的不同定义的对象
        if (this.allowBeanDefinitionOverriding != null) {
            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        // 此属性的含义:是否允许bean之间存在循环依赖
        if (this.allowCircularReferences != null) {
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
    }

看了代码,我们可以得知对于上面是否允许覆盖的两个属性只是判断了是否为空,如果不为空要进行设置,但是并没有看到在哪里设置,究竟这个设置是在哪里呢?其实还是和上面一样,提现了Spring的高度扩展性,使用子类覆盖方法,例如:

    @Override
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        super.setAllowBeanDefinitionOverriding(false);
        super.setAllowCircularReferences(false);
        super.customizeBeanFactory(beanFactory);
    }

加载 BeanDefinition

再上一个步骤,我们看到已经初始化了 DefaultListableFactory , 这是实现配置文件加载的第一步,还需要XmlBeanDefinitionReader 来读取XML。那么接下来的步骤首先要做的就是初始化 XmlBeanDefinitionReader

    /**
     * Loads the bean definitions via an XmlBeanDefinitionReader.
     * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
     * @see #initBeanDefinitionReader
     * @see #loadBeanDefinitions
     */
    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        // 为指定beanFactory创建XmlBeanDefinitionReader
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
        // 为beanDefinitionReader进行环境变量的设置
        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.
        // 对beanDefinitionReader进行设置,可以覆盖
        initBeanDefinitionReader(beanDefinitionReader);
        
        loadBeanDefinitions(beanDefinitionReader);
    }

在初始化了 DefaultListableBeanFactoryXmlBeanDefinitionReader 后就可以进行配置文件的读取了。

    /**
     * Load the bean definitions with the given XmlBeanDefinitionReader.
     * <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
     * method; hence this method is just supposed to load and/or register bean definitions.
     * @param reader the XmlBeanDefinitionReader to use
     * @throws BeansException in case of bean registration errors
     * @throws IOException if the required XML document isn't found
     * @see #refreshBeanFactory
     * @see #getConfigLocations
     * @see #getResources
     * @see #getResourcePatternResolver
     */
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }
    }

使用 XmlBeanDefinitionReaderloadBeanDefinitions 方法进行配置文件的加载注册。因为 XmlBeanDefinitionReader 在创建时已经将初始化好的 DefaultListableBeanFactory 注册进去了,所以XmlBeanDefinitionReader 所读取的 BeanDefinitionHolder 都会注册到 DefaultListableBeanFactory 中,也就是经过此步骤,类型 DefaultListableBeanFactory 的实例变量 beanFactory 已经包含了所有解析好的配置。

功能扩展

进入函数 prepareBeanFactory 之前,Spring 已经完成了对配置的解析,而 ApplicationContext 在功能上的扩展就在此展开。

/**
     * Configure the factory's standard context characteristics,
     * such as the context's ClassLoader and post-processors.
     * @param beanFactory the BeanFactory to configure
     */
    protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 设置beanFactory的classloader为当前context的classLoader
        beanFactory.setBeanClassLoader(getClassLoader());
        // 设置beanFactory的表达式语言处理器,spring3开始增加了表达式语言的支持,默认可以使用#{bean.xxx}的形式调用相关属性值。
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        // 为beanFactory增加一个默认的propertyEditor,这个主要是对bean的属性设置等管理的一个工具
        beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

        // 添加beanPostProcessor
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        // 设置几个忽略自动装配的接口
        beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
        beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
        beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
        beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

        // BeanFactory interface not registered as resolvable type in a plain factory.
        // MessageSource registered (and found for autowiring) as a bean.
        // 设置几个自动装配的特殊规则
        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);

        // Register early post-processor for detecting inner beans as ApplicationListeners.
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

        // Detect a LoadTimeWeaver and prepare for weaving, if found.
        // 增加对AspectJ的支持
        if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            // Set a temporary ClassLoader for type matching.
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }

        // 添加默认的系统环境bean
        if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
        }
    }

上面函数中主要进行了几个方面的扩展:

  • 增加对SPEL语言的支持
  • 增加对属性编辑器的支持;
  • 增加对一些内置类,比如 EnvironmentAware、MessageSourceAware 的信息注入。
  • 设置了依赖功能可忽略的接口;
  • 注册一些固定依赖的属性;
  • 增加AspectJ的支持;
  • 将相关环境变量以及系统属性以单例模式注册;

上面我们总结了这一阶段的几个步骤,但是对于具体含义可能并不理解,下面将对各个步骤进行分析;

增加SPEL语言支持

Spring 表达式语言全称为“Spring Expression Language”,缩写为"SPEL",能在运行时构建复杂表达式、存取对象属性、对象方法调用等,并且能与Spring功能完美整合,比如能用来配置bean定义。SPEL是单独模块,只依赖于core 模块,不依赖于其他模块,可以单独使用。SpEL 使用#{...} 作为界定符。这里只是为了唤醒大家的记忆帮助我们来理解源码,有兴趣的可以进一步深入研究。

在源码中通过代码 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())) 注册语言解析器,就可以对SPEL进行解析了,那么注册解析器后Spring又是在什么时候调用解析器的呢?

Spring 在 bean进行初始化的时候会有属性填充的一步,而这一步会调用 AbstractAutowireCapableBeanFactory 类的 applyPropertyValues 函数来完成功能。而就在这个函数中,会通过构造 BeanDefinitionValueResolver 类型实例valueResolver 来进行属性值的解析。同时,也就是这个步骤中一般通过AbstractBeanFactory 中的evaluateBeanDefinitionString 方法去完成SPEL的解析。

    /**
     * Evaluate the given String as contained in a bean definition,
     * potentially resolving it as an expression.
     * @param value the value to check
     * @param beanDefinition the bean definition that the value comes from
     * @return the resolved value
     * @see #setBeanExpressionResolver
     */
    protected Object evaluateBeanDefinitionString(String value, BeanDefinition beanDefinition) {
        if (this.beanExpressionResolver == null) {
            return value;
        }
        Scope scope = (beanDefinition != null ? getRegisteredScope(beanDefinition.getScope()) : null);
        return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
    }

当调用这个方法的时候,会判断是否存在语言解析器,如果存在则调用解析器的方法进行解析,解析的过程是在Spring的epression 的包内,这里不做过多解释。我们通过对 evaluateBeanDefinitionString 方法的调用层次可以看出,应用语言解析器的调用主要是在解析依赖注入bean的时候,以及在完成bean的初始化和属性获取后进行属性填充的时候。

增加属性注册编辑器

在Spring DI的时候可以把普通属性注入进来,但是像Date类型就无法被识别,例如:

public class UserManager {
    private Date dateValue;

    @Override
    public String toString() {
        return "UserManager{" +
                "dateValue=" + dateValue +
                '}';
    }

    public Date getDateValue() {
        return dateValue;
    }

    public void setDateValue(Date dateValue) {
        this.dateValue = dateValue;
    }
    
    // 上面代码中需要对日期进行属性注入
    <bean id="userManager" class="com.ccgogoing.domain.UserManager">
        <property name="dateValue">
            <value>2019-01-27</value>
        </property>
    </bean>
        
    // 测试代码    
    @Test
    public void testDate() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        UserManager userManager = (UserManager) applicationContext.getBean("userManager");
        System.out.println(userManager);
    }

如果直接这样使用,程序会报错,类型转换失败。因为在UserManager中的dateValue 属性是Date类型的,而在XML中配置的确实String类型的,因此当然会报错。

Spring针对此问题提供了两种解决办法:

  1. 使用自定义属性编辑器

    (1) 编写自定义的属性编辑器

public class DatePropertyEditor extends PropertyEditorSupport {
    private String format = "yyyy-MM-dd";

    public void setFormat (String format) {
        this.format = format;
    }

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        System.out.println("text = " + text);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
        try {
            Date d = simpleDateFormat.parse(text);
            this.setValue(d);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        super.setAsText(text);
    }
}

​ (2) 将自定义属性编辑器注册到Spring中

    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map>
                <entry key="java.util.Date">
                    <bean class="com.ccgogoing.extend.DatePropertyEditor">
                        <property name="format" value="yyyy-MM-dd"/>
                    </bean>
                </entry>
            </map>
        </property>
    </bean>

在配置文件中引入类型为 org.springframework.beans.factory.config.CustomEditorConfigurer 的bean,并在属性 customEditors 中加入自定义的属性编辑器,其中key 为属性编辑器所对应的类型。 通过这样的配置,当spring 注入bean 的属性时,一旦碰到Date类型就会调用自定义的DatePropertyEditor解析器进行解析,并用解析结果代替配置属性进行注入。

  1. 注册Spring自带的属性编辑器CustomDateEditor

    具体代码如下:

    // 定义属性编辑器
    public class DatePropertyEditorRegistrar implements PropertyEditorRegistrar {
    
        @Override
        public void registerCustomEditors(PropertyEditorRegistry registry) {
            registry.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
        }
    }
    // 注册到Spring中
        <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
            <property name="propertyEditorRegistrars">
                <list>
                    <bean class="com.ccgogoing.extend.DatePropertyEditorRegistrar"/>
                </list>
            </property>
        </bean>
    

我们了解了自定义属性编辑器的作用,但是与 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())) 并无联系,因为在注册自定义属性编辑器的时候使用的是 PropertyEditorRegistry 的 registerCustomEditor 方法,而这里使用的是 ConfigurableListableBeanFactory 的 addPropertyEditorRegistrar 方法。 我们不妨深入探索一下 ResourceEditorRegistrar 的内部实现,在 ResourceEditorRegistrar 中,我们最关心的方法是 registerCustomEditors .

/**
  * Populate the given {@code registry} with the following resource editors:
  * ResourceEditor, InputStreamEditor, InputSourceEditor, FileEditor, URLEditor,
  * URIEditor, ClassEditor, ClassArrayEditor.
  * <p>If this registrar has been configured with a {@link ResourcePatternResolver},
  * a ResourceArrayPropertyEditor will be registered as well.
  * @see org.springframework.core.io.ResourceEditor
  * @see org.springframework.beans.propertyeditors.InputStreamEditor
  * @see org.springframework.beans.propertyeditors.InputSourceEditor
  * @see org.springframework.beans.propertyeditors.FileEditor
  * @see org.springframework.beans.propertyeditors.URLEditor
  * @see org.springframework.beans.propertyeditors.URIEditor
  * @see org.springframework.beans.propertyeditors.ClassEditor
  * @see org.springframework.beans.propertyeditors.ClassArrayEditor
  * @see org.springframework.core.io.support.ResourceArrayPropertyEditor
  */
 @Override
 public void registerCustomEditors(PropertyEditorRegistry registry) {
     ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
     doRegisterEditor(registry, Resource.class, baseEditor);
     doRegisterEditor(registry, ContextResource.class, baseEditor);
     doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
     doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
     doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
     if (pathClass != null) {
         doRegisterEditor(registry, pathClass, new PathEditor(baseEditor));
     }
     doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
     doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));

     ClassLoader classLoader = this.resourceLoader.getClassLoader();
     doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
     doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
     doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));

     if (this.resourceLoader instanceof ResourcePatternResolver) {
         doRegisterEditor(registry, Resource[].class,
                 new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
     }
 }

 /**
  * Override default editor, if possible (since that's what we really mean to do here);
  * otherwise register as a custom editor.
  */
 private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {
     if (registry instanceof PropertyEditorRegistrySupport) {
         ((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);
     }
     else {
         registry.registerCustomEditor(requiredType, editor);
     }
 }

doRegisterEditor 方法中,可以看到自定义属性编译器中使用的关键代码 registry.registerCustomEditor(requiredType, editor) ; 回过头来看,其实 ResourceEditorRegistrar 类其实无非就是注册了一系列的常用类型的属性编辑器。例如,代码 doRegisterEditor(registry, Class.class, new ClassEditor(classLoader)) 实现的功能就是注册Class类对应的属性编辑器。那么,注册之后,一旦某个实体bean中存在一些Class类型的属性,那么Spring会调用ClassEditor将配置中定义的String类型转换为Class类型并进行赋值。

分析到这里,依然有个疑问那就是 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())) 仅仅是注册了 ResourceEditorRegistrar 实例,但是却没有调用 registerCustomEditors进行注册 ,那么到底什么时候进行这些属性编辑器的注册呢?进一步我们查看ResourceEditorRegistrar#registerCustomEditors 的调用栈:

1.png

可以看到一个比较熟悉的方法在调用,那就是 AbstractBeanFactory#initBeanWrapper ,这是在bean 初始化时使用的一个方法,主要是将BeanDefinition 转换为BeanWrapper 后用于对属性的填充。到此逻辑已经明了,在bean的初始化后,会调用 ResourceEditorRegistrar#registerCustomEditors 方法进行批量通用属性编辑器注册。注册后,在属性填充时,Spring便可以使用这些属性编辑器进行属性的解析注入了。

既然提到了BeanWrapper, 这里需要强调下,Spring中用于封装Bean的是BeanWrapper类型,而它又间接继承了 PropertyEditorRegistry ,也就是我们上面自定义的属性编辑器注册时的方法参数 PropertyEditorRegistry registry ,其实大部分情况下都是BeanWrapper,对于BeanWrapper 在 Spring 中的默认实现为 BeanWrapperImpl , 而 BeanWrapperImpl 除了实现 BeanWrapper 接口外还间接继承了 PropertyEditorRegistrySupport ,在 PropertyEditorRegistrySupport 中有这样一个方法:

    /**
     * Actually register the default editors for this registry instance.
     */
    private void createDefaultEditors() {
        this.defaultEditors = new HashMap<Class<?>, PropertyEditor>(64);

        // Simple editors, without parameterization capabilities.
        // The JDK does not contain a default editor for any of these target types.
        this.defaultEditors.put(Charset.class, new CharsetEditor());
        this.defaultEditors.put(Class.class, new ClassEditor());
        this.defaultEditors.put(Class[].class, new ClassArrayEditor());
        this.defaultEditors.put(Currency.class, new CurrencyEditor());
        this.defaultEditors.put(File.class, new FileEditor());
        this.defaultEditors.put(InputStream.class, new InputStreamEditor());
        this.defaultEditors.put(InputSource.class, new InputSourceEditor());
        this.defaultEditors.put(Locale.class, new LocaleEditor());
        if (pathClass != null) {
            this.defaultEditors.put(pathClass, new PathEditor());
        }
        this.defaultEditors.put(Pattern.class, new PatternEditor());
        this.defaultEditors.put(Properties.class, new PropertiesEditor());
        this.defaultEditors.put(Reader.class, new ReaderEditor());
        this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
        this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
        this.defaultEditors.put(URI.class, new URIEditor());
        this.defaultEditors.put(URL.class, new URLEditor());
        this.defaultEditors.put(UUID.class, new UUIDEditor());
        if (zoneIdClass != null) {
            this.defaultEditors.put(zoneIdClass, new ZoneIdEditor());
        }

        // Default instances of collection editors.
        // Can be overridden by registering custom instances of those as custom editors.
        this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
        this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
        this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
        this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
        this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));

        // Default editors for primitive arrays.
        this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
        this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());

        // The JDK does not contain a default editor for char!
        this.defaultEditors.put(char.class, new CharacterEditor(false));
        this.defaultEditors.put(Character.class, new CharacterEditor(true));

        // Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
        this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
        this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));

        // The JDK does not contain default editors for number wrapper types!
        // Override JDK primitive number editors with our own CustomNumberEditor.
        this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
        this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
        this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
        this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
        this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
        this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
        this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
        this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
        this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
        this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
        this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
        this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
        this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
        this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));

        // Only register config value editors if explicitly requested.
        if (this.configValueEditorsActive) {
            StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
            this.defaultEditors.put(String[].class, sae);
            this.defaultEditors.put(short[].class, sae);
            this.defaultEditors.put(int[].class, sae);
            this.defaultEditors.put(long[].class, sae);
        }
    }

通过这个方法我们已经知道了在Spring中定义了一系列常用的属性编辑器使我们可以方便的进行配置。 如果我们定义的bean中有某个属性不在上面的类型中,我们才需要进行个性化的属性编辑器的注册。

添加 ApplicationContextAwareProcessor 处理器

​ 继续跟踪 org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory 方法的主线,接下来的 beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)) 其实就是注册个BeanPostProcessor,而真正的逻辑还是在 ApplicationContextAwareProcessor 中。

ApplicationContextAwareProcessor 实现了 BeanPostProcessor 接口,在这里需要讲一下,在bean实例化的时候,也就是Spring 激活bean 的 init-method 前后,会调用BeanPostProcessor 的 postProcessBeforeInitialization 和 postProcessAfterInitialization 方法。同样对于 ApplicationContextAwareProcessor 我们同样关注于这两个方法。

在 postProcessAfterInitialization 方法中并没有做什么逻辑处理。

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }


那么,我们着重看下 postProcessBeforeInitialization

    @Override
    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
        AccessControlContext acc = null;

        if (System.getSecurityManager() != null &&
                (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
                        bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
                        bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
            acc = this.applicationContext.getBeanFactory().getAccessControlContext();
        }

        if (acc != null) {
            AccessController.doPrivileged(new PrivilegedAction<Object>() {
                @Override
                public Object run() {
                    invokeAwareInterfaces(bean);
                    return null;
                }
            }, acc);
        }
        else {
            invokeAwareInterfaces(bean);
        }

        return bean;
    }


    private void invokeAwareInterfaces(Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof EnvironmentAware) {
                ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
            }
            if (bean instanceof EmbeddedValueResolverAware) {
                ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
            }
            if (bean instanceof ResourceLoaderAware) {
                ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
            }
            if (bean instanceof ApplicationEventPublisherAware) {
                ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
            }
            if (bean instanceof MessageSourceAware) {
                ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
            }
            if (bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
            }
        }
    }

postProcessBeforeInitialization 方法中调用了 invokeAwareInterfaces。 从invokeAwareInterfaces 方法中,我们或多或少已经了解了Spring 的用意,实现这些Aware接口的bean在被初始化之后,可以取得一些对应的资源。

设置忽略依赖

当 Spring 上一步将 ApplicationContextAwareProcessor注册后,那么在 invokeAwareInterfaces 方法中间接调用的 Aware 类 已经不是普通的bean了,如 MessageSourceAwareResourceLoaderAware 等,那么当前需要在 Spring 做bean的依赖注入的时候忽略它们,而 ignoreDependencyInterface 的作用正是如此。

        // 设置几个忽略自动装配的接口
        beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
        beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
        beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
        beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

注册依赖

Spring 中有了忽略依赖的功能,必不可少的也会有注册依赖的功能。

        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);

当注册了依赖解析后,例如当注册了对BeanFactory.class 的解析依赖后,当bean的属性注入的时候,一旦检测到属性为BeanFactory类型将会把beanFactory的实例注入进去。

由于Spring容器启动这篇文章篇幅较长,本篇暂时介绍到这里,下一篇文章会接着分析BeanFactory的后处理、初始化非延迟加载单例、finishRefresh等方法的具体细节。

请务必点击阅读此文的下篇 《Spring容器基本实现之源码分析-下篇 传送门》 ,看完后必定会对 Spring 的整体把控会有更深一步的理解。

相关文章

网友评论

    本文标题:Spring容器的基本实现之源码分析(上篇)

    本文链接:https://www.haomeiwen.com/subject/guctsqtx.html