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