zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Spring读源码系列06----容器扩展功能--上

Spring扩展源码容器 -- 系列 功能 ----
2023-09-14 09:02:32 时间


ApplicationContext

在这里插入图片描述
在这里插入图片描述

  • 使用BeanFactory方式加载XML
        ClassPathResource classPathResource = new ClassPathResource("bean.xml");
        BeanFactory xmlBeanFactory = new XmlBeanFactory(classPathResource);
  • 使用ApplicationContext方式加载XML
        ApplicationContext bc=new ClassPathXmlApplicationContext("bean.xml");

在这里插入图片描述


ClassPathXmlApplicationContext构造函数

    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();
        }

    }  

在这里插入图片描述
在这里插入图片描述


AbstractRefreshableConfigApplicationContext#setConfigLocations—设置配置文件路径

    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);
    }

AbstractApplicationContext#refresh—刷新容器

在这里插入图片描述

    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();
            }

        }
    }

在这里插入图片描述
在这里插入图片描述


AbstractApplicationContext#prepareRefresh—环境准备

	/**
准备此上下文以进行刷新、设置其启动日期和活动标志以及执行任何属性源的初始化。
	 */
	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<>();
	}

设置状态为激活,初始化属性源,验证必须的属性是否都是可解析的,预处理监听器
在这里插入图片描述
在这里插入图片描述


AbstractApplicationContext#obtainFreshBeanFactory—加载BeanFactory

在这里插入图片描述
在这里插入图片描述

//告诉子类刷新内部 bean 工厂。返回:新鲜的 BeanFactory 实例
	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	//初始BeanFactory,并对XML文件进行读取,并将得到的BeanFactory记录在当前实体的属性中
		refreshBeanFactory();
		//返回当前实体的BeanFactory属性
		return getBeanFactory();
	}

AbstractRefreshableApplicationContext#refreshBeanFactory—初始化BeanFactory,读取xml啥的
	/**
	 * 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的实例,并且ApplicationContext继承了BeanFactory,这是不是很像设计模式中的装饰器模式
在这里插入图片描述
因此在我看来,spring在此处就是运用了装饰器模式,对BeanFactory完成了功能的扩展


AbstractRefreshableApplicationContext#createBeanFactory—创建bean工厂

创建的就是DefaultListableBeanFactory,getInternalParentBeanFactory()方法是在设置了父类容器的情况下才会返回非NULL结果

	protected DefaultListableBeanFactory createBeanFactory() {
		return new DefaultListableBeanFactory(getInternalParentBeanFactory());
	}

AbstractRefreshableApplicationContext#customizeBeanFactory—定制BeanFactory
//自定义此上下文使用的内部 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);
		}
	}

AbstractXmlApplicationContext#loadBeanDefinitions—加载BeanDefinition

在这里插入图片描述

	/**
	 * 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);
		}
	}

在这里插入图片描述


AbstractApplicationContext#prepareBeanFactory—功能扩展

在这里插入图片描述

//配置工厂的标准上下文特征,例如上下文的 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());
		}
	}

其实从源码中我们可以很明显看出来几点:

  • 为啥applicationContext可以自动识别实现了aware接口的类,并完成相应的注入呢?—ApplicationContextAwareProcessor后置处理器干的呗
  • 为啥可以自动识别实现了监听器接口的类呢?----ApplicationListenerDetector还是猴子处理器干的呗

在这里插入图片描述


增加SPEL语言的支持

在这里插入图片描述
在这里插入图片描述

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加载过程


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

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中进行保管,然后在何时的时机注册到转换器注册中心中


1.1 保管并注册自定义属性编辑器的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);
		}
	}

}

1.2 注册自定义属性编辑器加测试
    <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);
    }

}

在这里插入图片描述


2.注册spring自带的属性编辑器CustomDateEditor
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>

测试:
在这里插入图片描述


ResourceEditorRegistrar干了啥

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);
		}
	}

}

在这里插入图片描述


0.AbstractBeanFactory为类型转换做的相关准备

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<>();

1.调用流程分析

调用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方法在哪里被调用的


AbstractBeanFactory#registerCustomEditors方法调用时机分析

当然该方法不只在一处被调用,只是说该处调用的地方我们最为关心:

	protected void initBeanWrapper(BeanWrapper bw) {
		bw.setConversionService(getConversionService());
		registerCustomEditors(bw);
	}

真相已经浮出水面了,当bean在实例化过程中,会创建一个BeanWrapper对其进行包裹,然后返回,在创建完BeanWrapper后,会调用initBeanWrapper方法,主要就是在上面注册一些属性转换器,用户后面属性赋值过程中的类型转换。
在这里插入图片描述
BeanWrapper这个类我已经在番外篇中进行了详细讲解,不清楚的可以会去稳固一下


PropertyEditorRegistrySupport#createDefaultEditors的调用时机

在这里插入图片描述

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在哪里被调用?


PropertyEditorRegistrySupport#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);
		}


TypeConverterDelegate#convertIfNecessary何时被调用

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方法又在何时被调用呢?


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方法进行类型转换,这里不在多提