zoukankan      html  css  js  c++  java
  • 功能扩展

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

    作者:Joe
    努力了的才叫梦想,不努力的就是空想,努力并且坚持下去,毕竟这是我相信的力量
  • 相关阅读:
    linux nat style
    vmware tools install
    linux network
    sql group by max
    实例 | tp5使用七牛云上传图片和文件/删除文件
    菜鸟如何使用composer
    浅谈数据库用户表结构设计,第三方登录
    app登陆,注册,第三方登陆数据设计及业务流程
    数据库设计——评论回复功能
    app接口设计之token的php实现
  • 原文地址:https://www.cnblogs.com/Joe-Go/p/10211419.html
Copyright © 2011-2022 走看看