ClassPathResource classPathResource = new ClassPathResource("bean.xml");
BeanFactory xmlBeanFactory = new XmlBeanFactory(classPathResource);
ApplicationContext bc=new ClassPathXmlApplicationContext("bean.xml");
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[]{configLocation}, true, (ApplicationContext)null);
}
|
|
\|/
|
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent);
//设置配置文件路径
this.setConfigLocations(configLocations);
if (refresh) {
//刷新容器
this.refresh();
}
}
public void setConfigLocations(@Nullable String... locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for(int i = 0; i < locations.length; ++i) {
//解析给定配置文件路径
this.configLocations[i] = this.resolvePath(locations[i]).trim();
}
} else {
this.configLocations = null;
}
}
protected String resolvePath(String path) {
//ConfigurablePropertyResolver负责解析path路径
return this.getEnvironment().resolveRequiredPlaceholders(path);
}
public void refresh() throws BeansException, IllegalStateException {
//startupShutdownMonitor可以确保当前应用在刷新过程中是同步的
synchronized(this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
//准备此上下文以进行刷新、设置其启动日期和活动标志以及执行任何属性源的初始化。
this.prepareRefresh();
//初始化BeanFactory,并进行xml文件读取---原beanFactory的初始化步骤在这一步完成
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
//配置工厂的标准上下文特征,例如上下文的 ClassLoader 和后处理器。
this.prepareBeanFactory(beanFactory);
try {
//子类覆盖父类方法做额外处理:
//在标准初始化之后修改应用程序上下文的内部 bean 工厂。
//所有 bean 定义都将被加载,但还没有 bean 被实例化。
//这允许在某些 ApplicationContext 实现中注册特殊的 BeanPostProcessors 等。
this.postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
//触发bean工厂后置处理器
this.invokeBeanFactoryPostProcessors(beanFactory);
//注册拦截bean创建的bean处理器,这里只是注册,真正调用是在getBean的时候
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
//为上下文初始化Message源,即不同语言的消息体,国际化处理
this.initMessageSource();
//初始化应用消息广播器,并放入ApplicationEventMulticaster中
this.initApplicationEventMulticaster();
//保留给子类来初始化其他bean
this.onRefresh();
//在所有注册的bean中查找Listener bean,注册到消息广播器中
this.registerListeners();
//初始化剩下的单实例(非懒加载的bean)
this.finishBeanFactoryInitialization(beanFactory);
//完成此上下文的刷新,调用 LifecycleProcessor 的 onRefresh() 方法并发布 ContextRefreshedEvent。
this.finishRefresh();
} catch (BeansException var10) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
}
this.destroyBeans();
this.cancelRefresh(var10);
throw var10;
} finally {
//重置 Spring 核心中的常见自省缓存,因为我们可能不再需要单例 bean 的元数据......
this.resetCommonCaches();
contextRefresh.end();
}
}
}
/**
准备此上下文以进行刷新、设置其启动日期和活动标志以及执行任何属性源的初始化。
*/
protected void prepareRefresh() {
// Switch to active.
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
// 初始化上下文环境中的任何占位符属性源---该方法也是留给子类去覆盖的(例如:我们可以自定义占位符)
initPropertySources();
// 验证标记为必需的所有属性都是可解析的---需要的属性文件是否都已经放入环境中
getEnvironment().validateRequiredProperties();
//监听器的预处理
// Store pre-refresh ApplicationListeners...
//earlyApplicationListeners: 刷新前注册的本地侦听器
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
//允许收集早期应用程序事件,一旦多播器可用就发布...
this.earlyApplicationEvents = new LinkedHashSet<>();
}
设置状态为激活,初始化属性源,验证必须的属性是否都是可解析的,预处理监听器
//告诉子类刷新内部 bean 工厂。返回:新鲜的 BeanFactory 实例
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//初始BeanFactory,并对XML文件进行读取,并将得到的BeanFactory记录在当前实体的属性中
refreshBeanFactory();
//返回当前实体的BeanFactory属性
return getBeanFactory();
}
/**
* 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 {
//因为ApplicationContext内部会拥有一个BeanFactory实例(又因为ApplicationContext继承了BeanFactory,个人感觉有点像装饰器模式)
//这里判断当前ApplicationContext是否有旧的BeanFactory实例,如果有的话,就销毁BeanFactory工厂里面的单例bean,然后清空BeanFacotry
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
//为了序列化指定id,如果需要的话,让这个BeanFactory从id反序列化到BeanFactory对象
beanFactory.setSerializationId(getId());
//定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖
customizeBeanFactory(beanFactory);
//初始DocumentReader,并进行XML文件读取及解析
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
在ApplicationContext内部会维护一个BeanFactory的实例,并且ApplicationContext继承了BeanFactory,这是不是很像设计模式中的装饰器模式
因此在我看来,spring在此处就是运用了装饰器模式,对BeanFactory完成了功能的扩展
创建的就是DefaultListableBeanFactory,getInternalParentBeanFactory()方法是在设置了父类容器的情况下才会返回非NULL结果
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
//自定义此上下文使用的内部 bean 工厂。为每次 refresh() 尝试调用。
//默认实现应用此上下文的“allowBeanDefinitionOverriding”
//和“allowCircularReferences”设置(如果指定)。
//可以在子类中重写以自定义任何 DefaultListableBeanFactory 的设置。
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
//是否允许bean定义的覆盖
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//是否允许循环依赖
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//xml--->BeanDefinition
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
//允许子类提供阅读器的自定义初始化,然后继续实际加载 bean 定义。---这里只是简单对BeanDefinitionReader做个校验
initBeanDefinitionReader(beanDefinitionReader);
//这个方法比较重要
loadBeanDefinitions(beanDefinitionReader);
}
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);
}
}
//配置工厂的标准上下文特征,例如上下文的 ClassLoader 和后处理器。
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//设置ApplicationContext内部的BeanFactory使用当前context的classLoader
beanFactory.setBeanClassLoader(getClassLoader());
//指示 Spring 是否需要忽略 SpEL,即不初始化 SpEL 基础结构。
//private static final boolean shouldIgnoreSpel = SpringProperties.getFlag("spring.spel.ignore");
//默认为false
if (!shouldIgnoreSpel) {
//默认支持SPEL功能--即可以用#{bean.xxx}来调用相关属性值
//!!!
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
}
//为BeanFactory增加一个ResourceEditorRegistrar
//PropertyEditorRegistrar:属性编辑器的登记注册,该类用来注册相关属性编辑器
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
//添加BeanPostProcessor
//这里默认添加的一个ApplicationContextAwareProcessor是用来处理实现aware接口的注入功能的
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.ignoreDependencyInterface(ApplicationStartupAware.class);
//设置了几个自动装配的特殊规则---如果我们在程序中注入下面这个几个bean,是可以成功的
// 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.
//将用于检测内部 bean 的早期后处理器注册为 ApplicationListener。----检测实现 ApplicationListener 接口的 bean
//如果找到这样的bean,并且是单例的,那么就可以添加进applicationListeners集合中去
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
//增加对AspectJ的支持
// Detect a LoadTimeWeaver and prepare for weaving, if found.
if (!NativeDetector.inNativeImage() && 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()));
}
// Register default environment beans.
//添加系统默认的环境bean,并且是在不存在的情况下才会去添加
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
//getEnvironment()默认返回Environent---这也是为什么我们可以直接注入使用
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());
}
if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {
beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());
}
}
其实从源码中我们可以很明显看出来几点:
doCreateBean--->populateBean(beanName, mbd, instanceWrapper);
--->applyPropertyValues(beanName, mbd, bw, pvs);
--->BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
--->evaluateBeanDefinitionString
@Nullable
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));
}
public class Bean {
LocalDateTime localDateTime;
public LocalDateTime getLocalDateTime() {
return localDateTime;
}
public void setLocalDateTime(LocalDateTime localDateTime) {
this.localDateTime = localDateTime;
}
@Override
public String toString() {
return "Bean{" +
"localDateTime=" + localDateTime +
'}';
}
}
<bean id="bean" class="org.deepSpring.Bean">
<property name="localDateTime">
<value>2022-04-15</value>
</property>
</bean>
ClassPathXmlApplicationContext xmlApplicationContext = new ClassPathXmlApplicationContext("bean.xml");
xmlApplicationContext.getBean("bean");
大家思考一下,属性能注入成功吗?---->即2022-4-15到LocalDateTime成立吗?
更进一步: 是否存在相关转换器可以把String转换为LocalDateTime类型,并且String类型的格式必须是yyyy-MM-dd呢?
符合预期,不存在相关的转换器,具体报错是在AbstractAutowireCapableBeanFactory#populateBean—>applyPropertyValues属性注入过程中发现的,如果不清楚回看之前的bean加载过程
LocalDateTimePropertyEditor这里先注册一个老旧的类型转换器作为演示:
/**
* @author 大忽悠
* @create 2022/4/15 12:55
*/
public class LocalDateTimePropertyEditor extends PropertyEditorSupport {
private static final String LOCALDATETIME_FORMAT="yyyy-MM-dd";
private static DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(LOCALDATETIME_FORMAT);
static {
timeFormatter = new DateTimeFormatterBuilder().append(timeFormatter)
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.toFormatter();
}
@Override
public void setAsText(String dateTimeStr) throws IllegalArgumentException {
setValue(format(dateTimeStr));
}
private final LocalDateTime format(String dateTimeStr)
{
return LocalDateTime.parse(dateTimeStr,timeFormatter);
}
}
下面就是将自定义的编辑器注册到spring,通常是放入CustomEditorConfigurer中进行保管,然后在何时的时机注册到转换器注册中心中
先来看一下官方对该类的一个介绍:
BeanFactoryPostProcessor 实现,允许方便地注册自定义属性编辑器,因为是BeanFactory后置处理器,因此可以拿到BeanFactory,然后通过BeanFactory来注册自定义属性编辑器
如果您想注册 PropertyEditor 实例,从 Spring 2.0 开始推荐的用法是使用自定义 PropertyEditorRegistrar 实现,然后在给定注册表上注册任何所需的编辑器实例。每个 PropertyEditorRegistrar 可以注册任意数量的自定义编辑器。
这里也看出spring推荐个人使用PropertyEditorRegistrar来注册自定义属性编辑器,后面也会将这种注册方式
这是spring给出的官方示例,也是我们需要去做的:
<bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<bean class="mypackage.MyCustomDateEditorRegistrar"/>
<bean class="mypackage.MyObjectEditorRegistrar"/>
</list>
</property>
</bean>
通过 customEditors 属性注册 PropertyEditor 类是非常好的。 Spring 将为每次编辑尝试创建它们的新实例,然后:
<bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date" value="mypackage.MyCustomDateEditor"/>
<entry key="mypackage.MyObject" value="mypackage.MyObjectEditor"/>
</map>
</property>
</bean>
请注意,您不应通过 customEditors 属性注册 PropertyEditor bean 实例,因为 PropertyEditor 是有状态的,并且每次编辑尝试都必须同步实例。如果您需要控制 PropertyEditor 的实例化过程,请使用 PropertyEditorRegistrar 来注册它们。
还支持“java.lang.String[]”样式的数组类名和原始类名(例如“boolean”)。委托给 ClassUtils 以进行实际的类名解析。
注意:使用此配置器注册的自定义属性编辑器不适用于数据绑定。数据绑定的自定义编辑器需要在 org.springframework.validation.DataBinder 上注册:使用通用基类或委托给通用 PropertyEditorRegistrar 实现以重用编辑器注册。
下面是该工厂bean后置处理器的源码:
public class CustomEditorConfigurer implements BeanFactoryPostProcessor, Ordered {
protected final Log logger = LogFactory.getLog(getClass());
private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered
@Nullable
private PropertyEditorRegistrar[] propertyEditorRegistrars;
@Nullable
private Map<Class<?>, Class<? extends PropertyEditor>> customEditors;
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
/**
指定要应用于当前应用程序上下文中定义的 bean 的 PropertyEditorRegistrar。
这允许与 DataBinders 等共享 PropertyEditorRegistrar。
此外,它避免了在自定义编辑器上同步的需要:PropertyEditorRegistrar 将始终为每个 bean 创建尝试创建新的编辑器实例。
*/
public void setPropertyEditorRegistrars(PropertyEditorRegistrar[] propertyEditorRegistrars) {
this.propertyEditorRegistrars = propertyEditorRegistrars;
}
/**
通过 Map 指定要注册的自定义编辑器,使用所需类型的类名作为键,使用关联的 PropertyEditor 的类名作为值。
*/
public void setCustomEditors(Map<Class<?>, Class<? extends PropertyEditor>> customEditors) {
this.customEditors = customEditors;
}
//该方法会在何时的时机被调用,下面会讲到,最终是通过beanFactory进行注册的
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.propertyEditorRegistrars != null) {
for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
beanFactory.addPropertyEditorRegistrar(propertyEditorRegistrar);
}
}
if (this.customEditors != null) {
this.customEditors.forEach(beanFactory::registerCustomEditor);
}
}
}
<bean id="bean" class="org.deepSpring.Bean">
<property name="localDateTime">
<value>2022-4-15</value>`在这里插入代码片`
</property>
</bean>
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.time.LocalDateTime">
<bean class="org.deepSpring.LocalDateTimePropertyEditor">
</bean>
</entry>
</map>
</property>
</bean>
测试:
public class DeepSpringStudy {
public static void main(String[] args) {
ClassPathXmlApplicationContext xmlApplicationContext = new ClassPathXmlApplicationContext("bean.xml");
Bean bean = (Bean) xmlApplicationContext.getBean("bean");
System.out.println(bean);
}
}
public class CustomDateEditor extends PropertyEditorSupport {
private final DateFormat dateFormat;
private final boolean allowEmpty;
private final int exactDateLength;
public CustomDateEditor(DateFormat dateFormat, boolean allowEmpty) {
this.dateFormat = dateFormat;
this.allowEmpty = allowEmpty;
this.exactDateLength = -1;
}
public CustomDateEditor(DateFormat dateFormat, boolean allowEmpty, int exactDateLength) {
this.dateFormat = dateFormat;
this.allowEmpty = allowEmpty;
this.exactDateLength = exactDateLength;
}
@Override
public void setAsText(@Nullable String text) throws IllegalArgumentException {
if (this.allowEmpty && !StringUtils.hasText(text)) {
// Treat empty String as null value.
setValue(null);
}
else if (text != null && this.exactDateLength >= 0 && text.length() != this.exactDateLength) {
throw new IllegalArgumentException(
"Could not parse date: it is not exactly" + this.exactDateLength + "characters long");
}
else {
try {
setValue(this.dateFormat.parse(text));
}
catch (ParseException ex) {
throw new IllegalArgumentException("Could not parse date: " + ex.getMessage(), ex);
}
}
}
@Override
public String getAsText() {
Date value = (Date) getValue();
return (value != null ? this.dateFormat.format(value) : "");
}
}
此属性编辑器只能对Date类型进行转换,不能对LocalDateTime类型进行转换,因此把上面例子中需要转换的LocalDateTime替换为Date进行测试:
PropertyEditorRegistrar是专门面向用户来注册自定义属性编辑器的类:
public class DateTimePropertyEditorRegistrar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
}
}
将上面的注册器注入容器:
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<bean class="org.deepSpring.DateTimePropertyEditorRegistrar"></bean>
</list>
</property>
</bean>
测试:
prepareBeanFactory方法中有这样一行代码:
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
....
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
....
}
public class ResourceEditorRegistrar implements PropertyEditorRegistrar {
private final PropertyResolver propertyResolver;
private final ResourceLoader resourceLoader;
public ResourceEditorRegistrar(ResourceLoader resourceLoader, PropertyResolver propertyResolver) {
this.resourceLoader = resourceLoader;
this.propertyResolver = propertyResolver;
}
//注册一些基础的类型转换器供spring使用
@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));
doRegisterEditor(registry, Path.class, 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);
}
}
}
AbstractBeanFactory为类型转换准备了下面这几个属性:
//bean 定义值中表达式的解析策略。
@Nullable
private BeanExpressionResolver beanExpressionResolver;
//使用 Spring ConversionService 代替 PropertyEditors。
@Nullable
private ConversionService conversionService;
//自定义 PropertyEditorRegistrars 应用到这个工厂的 bean
//beanFactory.addPropertyEditorRegistrar是将PropertyEditorRegistrar放入该集合中保存
private final Set<PropertyEditorRegistrar> propertyEditorRegistrars = new LinkedHashSet<>(4);
//自定义 PropertyEditors 应用到这个工厂的 bean。
private final Map<Class<?>, Class<? extends PropertyEditor>> customEditors = new HashMap<>(4);
//要使用的自定义 TypeConverter,覆盖默认的 PropertyEditor 机制
//默认为空,这样在BeanWrapper进行类型转换的时候,TypeConverter 会默认为对应的BeanWrapper,否则为设置好的TypeConverter
@Nullable
private TypeConverter typeConverter;
/** String resolvers to apply e.g. to annotation attribute values. */
private final List<StringValueResolver> embeddedValueResolvers = new CopyOnWriteArrayList<>();
调用DefaultListableBeanFactory的addPropertyEditorRegistrar增加一个属性编辑登录器来注册一些自定义属性编辑器
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
addPropertyEditorRegistrar最终调用的到是AbstractBeanFactory,即往AbstractBeanFactory的propertyEditorRegistrars放入了这个属性编辑登录器
@Override
public void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar) {
Assert.notNull(registrar, "PropertyEditorRegistrar must not be null");
this.propertyEditorRegistrars.add(registrar);
}
那么下面的重点就是看看这个集合会在哪里被用到,当然这有点麻烦,更简单的方法是直接追踪ResourceEditorRegistrar的registerCustomEditors的方法在哪里被调用:
通过追踪发现是在AbstractBeanFactory的registerCustomEditors方法中被调用的
protected void registerCustomEditors(PropertyEditorRegistry registry) {
if (registry instanceof PropertyEditorRegistrySupport) {
((PropertyEditorRegistrySupport) registry).useConfigValueEditors();
}
//默认只有一个,即ResourceEditorRegistrar
if (!this.propertyEditorRegistrars.isEmpty()) {
for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
try {
//默认只会调用ResourceEditorRegistrar的registerCustomEditors方法来注册一些默认关于资源的转换器
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;
}
}
}
//这里还会检查是否像customEditors集合中注册了一些转换器
if (!this.customEditors.isEmpty()) {
this.customEditors.forEach((requiredType, editorClass) ->
registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass)));
}
}
还有疑问: 传入的PropertyEditorRegistry在哪里被传入的,即AbstractBeanFactory#registerCustomEditors方法在哪里被调用的
当然该方法不只在一处被调用,只是说该处调用的地方我们最为关心:
protected void initBeanWrapper(BeanWrapper bw) {
bw.setConversionService(getConversionService());
registerCustomEditors(bw);
}
真相已经浮出水面了,当bean在实例化过程中,会创建一个BeanWrapper对其进行包裹,然后返回,在创建完BeanWrapper后,会调用initBeanWrapper方法,主要就是在上面注册一些属性转换器,用户后面属性赋值过程中的类型转换。
BeanWrapper这个类我已经在番外篇中进行了详细讲解,不清楚的可以会去稳固一下
private void createDefaultEditors() {
this.defaultEditors = new HashMap<>(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());
if (!shouldIgnoreXml) {
this.defaultEditors.put(InputSource.class, new InputSourceEditor());
}
this.defaultEditors.put(Locale.class, new LocaleEditor());
this.defaultEditors.put(Path.class, 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());
this.defaultEditors.put(ZoneId.class, 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);
}
}
该方法是向默认的属性编辑器集合中注册默认属性编辑器,那么该方法的调用时机如何呢?
@Nullable
public PropertyEditor getDefaultEditor(Class<?> requiredType) {
//defaultEditorsActive是否激活默认属性编辑器,如果不激活,那么我们无法获取到默认的属性编辑器
if (!this.defaultEditorsActive) {
return null;
}
if (this.overriddenDefaultEditors != null) {
PropertyEditor editor = this.overriddenDefaultEditors.get(requiredType);
if (editor != null) {
return editor;
}
}
//懒加载--第一次用到的时候,才会去创建
if (this.defaultEditors == null) {
createDefaultEditors();
}
return this.defaultEditors.get(requiredType);
}
getDefaultEditor在哪里被调用?
TypeConverterDelegate类:
@Nullable
private PropertyEditor findDefaultEditor(@Nullable Class<?> requiredType) {
PropertyEditor editor = null;
if (requiredType != null) {
// No custom editor -> check BeanWrapperImpl's default editors.
editor = this.propertyEditorRegistry.getDefaultEditor(requiredType);
if (editor == null && String.class != requiredType) {
// No BeanWrapper default editor -> check standard JavaBean editor.
editor = BeanUtils.findEditorByConvention(requiredType);
}
}
return editor;
}
那么TypeConverterDelegate类中的findDefaultEditor方法又会在何时被调用呢?
在TypeConverterDelegate的convertIfNecessary方法中被调用:
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
// Custom editor for this type?
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException conversionAttemptEx = null;
// No custom editor but custom ConversionService specified?
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
conversionAttemptEx = ex;
}
}
}
Object convertedValue = newValue;
// Value not of required type?
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
convertedValue instanceof String) {
TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
if (elementTypeDesc != null) {
Class<?> elementType = elementTypeDesc.getType();
if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
}
}
//当上面尝试从propertyEditorRegistry的CustomEditor集合和conversionService不存在的情况下,会去尝试从
//propertyEditorRegistry的DefaultEditors集合中寻找默认的属性编辑器
if (editor == null) {
editor = findDefaultEditor(requiredType);
}
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
TypeConverterSupport会调用convertIfNecessary完成类型转换,但是真正的转化工作委托给了TypeConverterDelegate完成
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
try {
//委托处理
return this.typeConverterDelegate.convertIfNecessary(null, null, value, requiredType, typeDescriptor);
}
catch (ConverterNotFoundException | IllegalStateException ex) {
throw new ConversionNotSupportedException(value, requiredType, ex);
}
catch (ConversionException | IllegalArgumentException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
}
TypeConverterSupport的convertIfNecessary方法又在何时被调用呢?
applyPropertyValues方法会通过convertForProperty方法间接调用到TypeConverterSupport的convertIfNecessary方法,完成bean的属性注入
AbstractAutowireCapableBeanFactory:
@Nullable
private Object convertForProperty(
@Nullable Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {
//除非手动设置了TypeConvert,否则默认的 converter 为当前BeanWrapperImpl
if (converter instanceof BeanWrapperImpl) {
return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
}
else {
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);
}
}
BeanWrapperImpl:
@Nullable
public Object convertForProperty(@Nullable Object value, String propertyName) throws TypeMismatchException {
//从bean的内省中拿到当前bean的属性描述符
CachedIntrospectionResults cachedIntrospectionResults = getCachedIntrospectionResults();
PropertyDescriptor pd = cachedIntrospectionResults.getPropertyDescriptor(propertyName);
if (pd == null) {
throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName,
"No property '" + propertyName + "' found");
}
//再拿到当前需要转换的属性的类型描述符
TypeDescriptor td = cachedIntrospectionResults.getTypeDescriptor(pd);
if (td == null) {
//添加进缓存
td = cachedIntrospectionResults.addTypeDescriptor(pd, new TypeDescriptor(property(pd)));
}
//进行类型转换
return convertForProperty(propertyName, null, value, td);
}
BeanWrapperImpl:
@Nullable
protected Object convertForProperty(
String propertyName, @Nullable Object oldValue, @Nullable Object newValue, TypeDescriptor td)
throws TypeMismatchException {
return convertIfNecessary(propertyName, oldValue, newValue, td.getType(), td);
}
当然,这里我最终convertIfNecessary不没有调用到TypeConvertSupport的convertIfNecessary方法,而是:
AbstractNestablePropertyAccessor继承了TypeConvertSupport,但是又额外增加了一个convertIfNecessary的重载,因此最终BeanWrapperImpi调用的就是这个重载
@Nullable
private Object convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue,
@Nullable Object newValue, @Nullable Class<?> requiredType, @Nullable TypeDescriptor td)
throws TypeMismatchException {
Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
try {
//委托类
return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td);
}
....异常捕获
}
当然还有构造器参数解析的时候,也会用到TypeConvert的convertIfNecessary方法进行类型转换,这里不在多提