功能扩展

在进入函数prepareBeanFactory前,Spring已经完成了对配置的解析,而ApplicationContext在功能上的扩展也由此展开------也就是对BeanFactory进行各种功能上的填充。

功能填充对BeanFactory

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        //设置BeanFactory的ClassLoader为当前的Context的ClassLoader
        beanFactory.setBeanClassLoader(getClassLoader());
        //设置beanFactory的表达式语言处理器,Spring3增加了表达式语言的支持。默认可以使用#{bean.xxxx}的形式来调用相关属性值
        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.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);

        
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

        // 增加对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());
        }
    }

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

(1)增加对SPEL语言的支持;

(2)增加对属性编辑器的支持;

(3)增加一些内置类,比如EnvironmentAware、MessageSourceAware的信息注入;

(4)设置了依赖功能可忽略的接口;

(5)注册一些固定依赖的属性;

(6)增加AspectJ的支持;

(7)将相关环境变量及属性注册以单例模式注册;

接下来我们对上述步骤进行详细的分析:

增加对SPEL语言的支持

Spring表达式语言全称为“Spring Expression Language”,缩写为SpEL,类似于Struts2中使用的OGNL表达式语言,能在运行时构建复杂表达式、存取对象图属性、对象方法调用等,并且能与Spring功能完美整合,比如能用来配置bean定义。SpEL是单独模块,只依赖于core模块,不依赖与其他模块,可以单独使用。

SpEL使用#{...}来作为定界符,所有在大括号中的字符都会被认为是SpEL,使用的方式如下:

<bean id="hero" class="com.joe.test.Hero/>
<bean>
    <property name= "instrument" value="#{hero}"/>
</bean>

相当于:

<bean id="hero" class="com.joe.test.Hero/>
<bean>
    <property name= "instrument" ref="hero"/>
</bean>

当然上面的例子只是最简单的使用方式,SpEL的功能非常强大,使用好可以大大的提高开发效率。

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

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

protected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) {
        if (this.beanExpressionResolver == null) {
            return value;
        }

        Scope scope = null;
        if (beanDefinition != null) {
            String scopeName = beanDefinition.getScope();
            if (scopeName != null) {
                scope = getRegisteredScope(scopeName);
            }
        }
        return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
    }

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

增加属性注册编辑器

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

public class User {

    private Date date;

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    @Override
    public String toString() {
        return "date: " + date;
    }


    public static void main(String[] args){
        ApplicationContext ap = new ClassPathXmlApplicationContext("spring.xml");
        User user = (User) ap.getBean("user");
        System.out.println(user.toString());
    }
}
    <bean id="user" class="com.joe.mytag.application.User">
        <property name="date" value="2019-01-03"></property>
    </bean>

运行测试方法:

Caused by: org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'date'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.util.Date' for property 'date': no matching editors or conversion strategy found
    at org.springframework.beans.AbstractNestablePropertyAccessor.convertIfNecessary(AbstractNestablePropertyAccessor.java:603)
    at org.springframework.beans.AbstractNestablePropertyAccessor.convertForProperty(AbstractNestablePropertyAccessor.java:615)
    at org.springframework.beans.BeanWrapperImpl.convertForProperty(BeanWrapperImpl.java:216)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.convertForProperty(AbstractAutowireCapableBeanFactory.java:1579)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1538)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1280)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
    ... 11 more

发现报错,发现是转型失败。因为在User中的data属性是Date的,但是XML中配置的却是String类型的,所以报错。

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

(1)使用自定义属性编辑器

  使用自定义的属性编辑器,通过继承PropertyEditorSupport,重写了setAsText方法,具体的步骤如下:

首先编写自定义的属性编辑器

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 date = simpleDateFormat.parse(text);
            this.setValue(date);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

再将自定义的属性编辑器注册到Spring中:

<bean class="com.joe.mytag.application.DatePropertyEditor" id ="date">
        <property name="format" value="yyyy-MM-dd"/>
    </bean>

    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map>
                <entry key="java.util.Date">
                    <ref bean="date"/>
                </entry>
            </map>
        </property>
    </bean>

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

但是,上述例子没有成功。目前还不知道怎么回事?

(2)注册Spring自带的属性编辑器CustomDateEditor

  通过注册Spring自带的属性编辑器CustomDateEditor,具体的步骤如下:

定义属性编辑器:

public class DatePropertyEditorRegister implements PropertyEditorRegistrar {
    @Override
    public void registerCustomEditors(PropertyEditorRegistry propertyEditorRegistry) {
        propertyEditorRegistry.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.joe.mytag.application.DatePropertyEditorRegister"/>
            </list>
        </property>
    </bean>

运行上述的例子:

一月 03, 2019 4:32:02 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@5910e440: startup date [Thu Jan 03 16:32:02 CST 2019]; root of context hierarchy
一月 03, 2019 4:32:02 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [spring.xml]
date: Thu Jan 03 00:00:00 CST 2019

  通过在配置文件中将自定义的DatePropertyEditorRegister注册进入org.springframework.beans.factory.config.CustomEditorConfigurer的propertyEditorRegisters属性中,可以跟第一个例子一样的效果。

了解了自定义的属性编辑器之后,但是,本节围绕的核心代码beanFactory.addPropertyEditorRegistrar(new ResourcesEditorRegistrar(this,getEnvironment()))并无联系,因为在注册自定义属性编辑器的时候使用的是PropertyEditorRegistry的registerCustomEditor方法,而这里使用的是ConfigurableListableBeanFactory的addPropertyEditorRegistrar方法,来探索一下ResourcesEditorRegistrar的内部实现,其中我们最关心的就是registerCustomEditors方法:

protected void registerCustomEditors(PropertyEditorRegistry registry) {
        PropertyEditorRegistrySupport registrySupport =
                (registry instanceof PropertyEditorRegistrySupport ? (PropertyEditorRegistrySupport) registry : null);
        if (registrySupport != null) {
            registrySupport.useConfigValueEditors();
        }
        if (!this.propertyEditorRegistrars.isEmpty()) {
            for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
                try {
                    registrar.registerCustomEditors(registry);
                }
                catch (BeanCreationException ex) {
                    Throwable rootCause = ex.getMostSpecificCause();
                    if (rootCause instanceof BeanCurrentlyInCreationException) {
                        BeanCreationException bce = (BeanCreationException) rootCause;
                        String bceBeanName = bce.getBeanName();
                        if (bceBeanName != null && isCurrentlyInCreation(bceBeanName)) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("PropertyEditorRegistrar [" + registrar.getClass().getName() +
                                        "] failed because it tried to obtain currently created bean '" +
                                        ex.getBeanName() + "': " + ex.getMessage());
                            }
                            onSuppressedException(ex);
                            continue;
                        }
                    }
                    throw ex;
                }
            }
        }
        if (!this.customEditors.isEmpty()) {
            this.customEditors.forEach((requiredType, editorClass) ->
                    registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass)));
        }
    }

 在doRegisterEditor函数中,可以看到咋之前提到的自定义属性中使用的关键代码:registry.registerCustomEditor(requiredTypr editor),回过头来看ResourceEditorRegistrar类的registerCustomEditor方法的核心功能,其实无非就是注册了一系列的常用类型的属性编辑器。那么注册后,一旦某个实体bean中存在一些Class类型的属性,那么Spring会调用ClassEditor将配置文件中定义的String类型转换为Class类型并进行赋值。

 添加ApplicationContextAwareProcessor处理器

   了解了属性编辑器的使用后,接下来我们继续通过AbstractApplicationContext的prepareBeanFactory方法的主线来进行函数跟踪。对于BeanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this))其实主要目的就是注册个BeanPostProcessor,而真正的逻辑还是在ApplicationContextAwareProcessor中。

   ApplicationContextAwareProcessor实现BeanPostProcessor接口,在bean实例化的时候,也就是Spring激活bean的init-method的前后,会调用BeanPostProcessor的postProcessBeforeInitialization方法和postProcessAfterInitialization方法,同样,对于ApplicationContextAwareProcessor我们也关心着两个方法。

  对于postProcessAfterInitialization方法,在ApplicationContextAwareProcessor中并没有做过多逻辑处理。

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

那么,重点看一下postProcessBeforeInitialization方法:

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((PrivilegedAction<Object>) () -> {
                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了,如ResourceLoaderAware、ApplicationEventPublisherAware等,那么当然需要在Spring做bean的依赖注入的时候忽略它们,而ignoreDependencyInterface的作用正是如此。

注册依赖

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源码深度解析》 郝佳 编著:

转载于:https://www.cnblogs.com/Joe-Go/p/10211419.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值