ApplicationContext(四)BeanFactory 功能扩展

ApplicationContext(四)BeanFactory 功能扩展

上节我们提到容器刷新的第二步初始化 BeanFactory 工厂并解析配制文件,但此时 BeanFactory 的功能还很简单,需要对其进行扩展。这就涉及到下面第三步:BeanFactory 功能扩展。

那 Spring 究竟进行了那些功能扩展呢?

源代码【AbstractApplicationContext】

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 1. 设置 beanFactory 的 classLoader 为当前 context 的 classLoader
beanFactory.setBeanClassLoader(getClassLoader()); // 2. 设置 beanFactory 的表达式语言处理器,Spring3 增加了表达式语言的支持
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); // 3. 为 beanFactory 增加一个默认的 propertyEditor,这个主要是对 bean 的属性等设置管理的一个工具
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); // 4. 添加 BeanPostProcessor
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); // 5. 设置了几个忽略自动装配的接口
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.ignoreDependencyInterface(EnvironmentAware.class); // 6. 设置了几个自动装配的特殊规则
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this); // 7. 增加对 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()));
} // 8. 添加默认的系统环境 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) 增加 Aspect的支持(会在第7章中进行详细的讲解)

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

可能读者不是很理解每个步骤的具体含义,接下来我们会对各个步骤进行详细地分析。

一、增加对 SPEL 语言的支持

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

@Value("#{19 - 1}")
private int age3;

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

二、增加对属性编辑器的支持

2.1 属性编辑器的基本用法

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

public class UserManager {
private Date data;
// 省略get/set
}

上面代码中,需要对日期型属性进行注入:

<bean id="userManager" class="UserManager">
<property name="data" value="2018-08-01 00:00:00"/>
</bean>

如果直接这样使用,程序则会报异常,类型转换不成功。因为在 Usermanager 中的 data Value 属性是 Date 类型型的,而在 XML中配置的却是 String 类型的,所以当然会报异常。

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

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

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

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

public class DatePropertyEdit extends PropertyEditorSupport implements PropertyEditor {
private String format = "yyyy-MM-dd HH:mm:ss";
@Override
public void setAsText(String text) throws IllegalArgumentException {
SimpleDateFormat sdf = new SimpleDateFormat(format);
try {
Date date = sdf.parse(text);
this.setValue(date);
} catch (ParseException e) {
}
}
}

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

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date"
value="com.github.binarylei.spring01.day0728.propertyedit.DataPropertyEdit">
</entry>
</map>
</property>
</bean>

在配置文件中引入类型为 CustomEditorConfigurer 的 bean,并在属性 customeditors 中加入自定义的属性编辑器,其中 key 为属性编辑器所对应的类型。通过这样的配置,当 Spring 在注入 bean 的属性时一旦遇到了 java.uti.Date 类型的属性会自动调用自定义的 DatepropertyEditor 解解析器进行解析,并用解析结果代替配置属性进行注入。

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

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

(1) 定义属性编辑器

public class DatePropertyEditorRegistar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Date.class,
new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
}
}

(2) 注册到 Spring

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<bean class="spring.context.spring_di.DatePropertyEditorRegistar"/>
</list>
</property>
</bean>

通过在配置文件中将自定义的 DatePropertyEditorRegistar 注册进人 org.springframework.beans.factory.config.CustomEditorConfigurer 的 propertyEditorRegistrars 属性中,可以具有与方法 1 同样的效果我们了解了自定义属性编辑器的使用。

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

2.2 属性编辑器的内部原理

beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()))

源代码【AbstractBeanFactory】

public void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar) {
Assert.notNull(registrar, "PropertyEditorRegistrar must not be null");
this.propertyEditorRegistrars.add(registrar);
}

源代码【ResourceEditorRegistrar】

// 1. PropertyEditorRegistrar 将常用的资源编辑器注册到 PropertyEditorRegistry 上,相当于一个工具类
// 2. PropertyEditorRegistry 实际持有编辑器
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));
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));
}
} /**
* 注册对应的属性编辑器
*/
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 类的 registerCustomEditors 方法的核心功能,其实无非是注册了一系列的常用类型的属性编辑器,例如,代码 doRegisterEditor(registry, Class.class, new ClassEditor(classLoader)) 实现的功能就是注册 Class 类对应的属性编辑器。那么,注册后,一且某个实体 bean 中存在一些 Class 类型的属性,那么 Spring 会调用 Classeditor 将配置中定义的 String 类型转换为 Class 类型并进行赋值。

分析到这里,我们不禁有个疑问,虽说 ResourceEditorRegistrar 类的 registerCustomEditors 方法实现了批量注册的功能,但是 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())) 仅仅是注册了 ResourceEditorRegistrar 实例,却并没有调用 ResourceEditorRegistrar 的 registerCustomEditors 方法进行注册,那么到底是在什么时候进行注册的呢?

源代码【Abstractbeanfactory】

// 如此,BeanWrapper 就注册了各种属性编辑器,可以在 populate() 时解析各种 String 类型的字段进行注入
protected void initBeanWrapper(BeanWrapper bw) {
bw.setConversionService(getConversionService());
registerCustomEditors(bw);
} // 调用 propertyEditorRegistrars 为 registry 注入属性编辑器
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) {
registrar.registerCustomEditors(registry);
}
}
if (!this.customEditors.isEmpty()) {
for (Map.Entry<Class<?>, Class<? extends PropertyEditor>> entry : this.customEditors.entrySet()) {
Class<?> requiredType = entry.getKey();
Class<? extends PropertyEditor> editorClass = entry.getValue();
registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass));
}
}
}

既然提到了 BeanWrapper,这里也有必要强调下, Spring 中用于封装 bean 的是 BeanWrapper 类型,而它又间接继承了 PropertyEditorRegistry 类型,也就是我们之前反复看到的方法参数 PropertyEditorRegistry registry,其实大部分情况下都是 BeanWrapper,对于 BeanWrapper 在 Spring 中的默认实现是 BeanWrapperlmpl,而 BeanWrapperlmpl 除了实现 BeanWrapper 接口外还继承了 PropertyEditorRegistrySupport,在 PropertyEditorRegistrySupport 中有这样一个方法

源代码【PropertyEditorRegistrySupport】

public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
// 在 PropertyEditorRegistrySupport 中注册了一系列默认的属性编辑器
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());
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);
}
}
}

2.3 添加 ApplicationContextAwareProcessor 处理器

在 Spring 中可以注入一些底层的组件,怎么实现的呢?这就是 beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)) 的作用了。首先回顾一下 ApplicationContextAware 的使用方法:

// 可以使用 ApplicationContext 组件
@Service
public class Bean implements ApplicationContextAware {
private ApplicationContext context; @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
}

很显然 context 是在 bean 实例化完成,属性注入后准备进行 init-method 方法前完成的。我们看一下代码:

源代码【PropertyEditorRegistrySupport】

// 抛开各种异常处理 Spring 作了如下的处理,这里我们重点关注 applyBeanPostProcessorsBeforeInitialization
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { // 1. 注入 Spring 组件
invokeAwareMethods(beanName, bean); // 2. 执行 init-method 前的回调,ApplicationContextAware 就是在这一步注入属性
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); // 3. 执行 init-method 方法
invokeInitMethods(beanName, wrappedBean, mbd); // 4. 执行 init-method 后的回调
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
} // 注入 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
} // 执行后置处理器,ApplicationContextAware 在这里注入了部分其它的 Aware
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException { Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}

下面我们看一下 ApplicationContextAwareProcessor 都做了那些事情,postProcessBeforeInitialization 实际上事情委托给了 invokeAwareInterfaces()

class ApplicationContextAwareProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException { 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(
new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
}
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);
}
}
}
}

2.4 设置忽略依赖

beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);

很明显 ApplicationContextAware 不能再注入到 bean 中:

public class Bean {
@Autowired
private ApplicationContextAware applicationContextAware;
}

2.5 注册依赖

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

Spring 还简化了底层组件的注入问题:

public class Bean {
@Autowired
private ApplicationContext context;
}

每天用心记录一点点。内容也许不重要,但习惯很重要!

上一篇:python3获取网页天气预报信息并打印


下一篇:EVEREST Ultimate Edition 5.50 正式版 序列号