前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring读源码系列06----容器扩展功能--上

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

作者头像
大忽悠爱学习
发布2022-05-10 16:26:08
5000
发布2022-05-10 16:26:08
举报
文章被收录于专栏:c++与qt学习

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


ApplicationContext

  • 使用BeanFactory方式加载XML
代码语言:javascript
复制
        ClassPathResource classPathResource = new ClassPathResource("bean.xml");
        BeanFactory xmlBeanFactory = new XmlBeanFactory(classPathResource);
  • 使用ApplicationContext方式加载XML
代码语言:javascript
复制
        ApplicationContext bc=new ClassPathXmlApplicationContext("bean.xml");

ClassPathXmlApplicationContext构造函数

代码语言:javascript
复制
    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—设置配置文件路径

代码语言:javascript
复制
    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;
        }

    }
代码语言:javascript
复制
    protected String resolvePath(String path) {
    //ConfigurablePropertyResolver负责解析path路径
        return this.getEnvironment().resolveRequiredPlaceholders(path);
    }

AbstractApplicationContext#refresh—刷新容器

代码语言:javascript
复制
    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—环境准备
代码语言:javascript
复制
	/**
准备此上下文以进行刷新、设置其启动日期和活动标志以及执行任何属性源的初始化。
	 */
	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
代码语言:javascript
复制
//告诉子类刷新内部 bean 工厂。返回:新鲜的 BeanFactory 实例
	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	//初始BeanFactory,并对XML文件进行读取,并将得到的BeanFactory记录在当前实体的属性中
		refreshBeanFactory();
		//返回当前实体的BeanFactory属性
		return getBeanFactory();
	}

AbstractRefreshableApplicationContext#refreshBeanFactory—初始化BeanFactory,读取xml啥的
代码语言:javascript
复制
	/**
	 * 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结果

代码语言:javascript
复制
	protected DefaultListableBeanFactory createBeanFactory() {
		return new DefaultListableBeanFactory(getInternalParentBeanFactory());
	}

AbstractRefreshableApplicationContext#customizeBeanFactory—定制BeanFactory
代码语言:javascript
复制
//自定义此上下文使用的内部 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
代码语言:javascript
复制
	/**
	 * 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);
	}
代码语言:javascript
复制
	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—功能扩展
代码语言:javascript
复制
//配置工厂的标准上下文特征,例如上下文的 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语言的支持
代码语言:javascript
复制
doCreateBean--->populateBean(beanName, mbd, instanceWrapper);
--->applyPropertyValues(beanName, mbd, bw, pvs);
--->BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
--->evaluateBeanDefinitionString
代码语言:javascript
复制
	@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));
	}

增加属性注册编译器
代码语言:javascript
复制
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 +
                '}';
    }
}
代码语言:javascript
复制
    <bean id="bean" class="org.deepSpring.Bean">
    <property name="localDateTime">
        <value>2022-04-15</value>
    </property>
    </bean>
代码语言:javascript
复制
        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这里先注册一个老旧的类型转换器作为演示:

代码语言:javascript
复制
/**
 * @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给出的官方示例,也是我们需要去做的:

代码语言:javascript
复制
   <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 将为每次编辑尝试创建它们的新实例,然后:

代码语言:javascript
复制
   <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后置处理器的源码:

代码语言:javascript
复制
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 注册自定义属性编辑器加测试
代码语言:javascript
复制
    <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>

测试:

代码语言:javascript
复制
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
代码语言:javascript
复制
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是专门面向用户来注册自定义属性编辑器的类:

代码语言:javascript
复制
public class DateTimePropertyEditorRegistrar implements PropertyEditorRegistrar {
    @Override
    public void registerCustomEditors(PropertyEditorRegistry registry) {
        registry.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
    }
}

将上面的注册器注入容器:

代码语言:javascript
复制
    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="propertyEditorRegistrars">
            <list>
                <bean class="org.deepSpring.DateTimePropertyEditorRegistrar"></bean>
            </list>
        </property>
    </bean>

测试:


ResourceEditorRegistrar干了啥

prepareBeanFactory方法中有这样一行代码:

代码语言:javascript
复制
	protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
          ....
		beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
        ....
}
代码语言:javascript
复制
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为类型转换准备了下面这几个属性:

代码语言:javascript
复制
	//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增加一个属性编辑登录器来注册一些自定义属性编辑器

代码语言:javascript
复制
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

addPropertyEditorRegistrar最终调用的到是AbstractBeanFactory,即往AbstractBeanFactory的propertyEditorRegistrars放入了这个属性编辑登录器

代码语言:javascript
复制
	@Override
	public void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar) {
		Assert.notNull(registrar, "PropertyEditorRegistrar must not be null");
		this.propertyEditorRegistrars.add(registrar);
	}

那么下面的重点就是看看这个集合会在哪里被用到,当然这有点麻烦,更简单的方法是直接追踪ResourceEditorRegistrar的registerCustomEditors的方法在哪里被调用:

通过追踪发现是在AbstractBeanFactory的registerCustomEditors方法中被调用的

代码语言:javascript
复制
	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方法调用时机分析

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

代码语言:javascript
复制
	protected void initBeanWrapper(BeanWrapper bw) {
		bw.setConversionService(getConversionService());
		registerCustomEditors(bw);
	}

真相已经浮出水面了,当bean在实例化过程中,会创建一个BeanWrapper对其进行包裹,然后返回,在创建完BeanWrapper后,会调用initBeanWrapper方法,主要就是在上面注册一些属性转换器,用户后面属性赋值过程中的类型转换。

BeanWrapper这个类我已经在番外篇中进行了详细讲解,不清楚的可以会去稳固一下


PropertyEditorRegistrySupport#createDefaultEditors的调用时机
代码语言:javascript
复制
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);
		}
	}

该方法是向默认的属性编辑器集合中注册默认属性编辑器,那么该方法的调用时机如何呢?

代码语言:javascript
复制
	@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类:

代码语言:javascript
复制
	@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方法中被调用:

代码语言:javascript
复制
	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完成

代码语言:javascript
复制
	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:

代码语言:javascript
复制
	@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:

代码语言:javascript
复制
	@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:

代码语言:javascript
复制
	@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调用的就是这个重载

代码语言:javascript
复制
	@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方法进行类型转换,这里不在多提

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-04-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Spring读源码系列06----容器扩展功能---上
  • ApplicationContext
    • ClassPathXmlApplicationContext构造函数
      • AbstractRefreshableConfigApplicationContext#setConfigLocations—设置配置文件路径
      • AbstractApplicationContext#refresh—刷新容器
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档