spring的启动过程04.1-value注解替换过程

时间:2025-02-13 14:44:27

概述:

在上篇《 spring的启动过程04-bean后置处理器》文章中讲解了bean后置处理器的原理,这篇文章结合具体的处理器讲解spring@Value注解的处理过程。

spring容器会在两种场景用到properties文件的属性值,第一种替换XML文件中的占位符详情请查阅《spring的启动过程03.1-占位符替换过程-xml配置的参数》,第二种就是业务代码中采用@Value注解方式。

    @Value(value = "${alias}")
    private String alias;
    @Value(value = "${password}")
    private String password;

原理:

spring框架采用bean后置处理器来拦截bean属性值的注入,先看下AutowiredAnnotationBeanPostProcessor的静态类图。


核心方法触发点:

	/**
	 * 创建Bean过程中设置属性值
	 * Populate the bean instance in the given BeanWrapper with the property values
	 * from the bean definition.
	 * @param beanName the name of the bean
	 * @param mbd the bean definition for the bean
	 * @param bw BeanWrapper with bean instance
	 */
	protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
		PropertyValues pvs = ();
		.....
		boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
		boolean needsDepCheck = (() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);

		if (hasInstAwareBpps || needsDepCheck) {
			PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, );
			if (hasInstAwareBpps) {
				for (BeanPostProcessor bp : getBeanPostProcessors()) {
					if (bp instanceof InstantiationAwareBeanPostProcessor) {
						InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
						pvs = (pvs, filteredPds, (), beanName);
						if (pvs == null) {
							return;
						}
					}
				}
			}
			if (needsDepCheck) {
				checkDependencies(beanName, mbd, filteredPds, pvs);
			}
		}
		applyPropertyValues(beanName, mbd, bw, pvs);
	}
遍历所有InstantiationAwareBeanPostProcessor实例设置属性字段值。

看下 属性替换的整个流程:


流程中用到了StringValueResolver类最终解析属性值,看下该类何时注入到bean工厂中

	protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
			StringValueResolver valueResolver) {
		BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
		String[] beanNames = ();
		// 替换占位符
		for (String curName : beanNames) {
			// Check that we're not parsing our own bean definition,
			// to avoid failing on unresolvable placeholders in properties file locations.
			if (!(() && ())) {
				BeanDefinition bd = (curName);
				try {
					(bd);
				}
				catch (Exception ex) {
					throw new BeanDefinitionStoreException((), curName, (), ex);
				}
			}
		}
		// New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
		(valueResolver);
		
		// New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
		// 把value注解处理器加入到bean工厂,该处理器解析value注解注入值
		(valueResolver);
	}
spring在执行bean工厂后置处理器的过程中会创建value标签解析器。负责创建该解析器的主要有


看下创建处理器的代码

1. PropertySourcesPlaceholderConfigurer

	/**
	 * Visit each bean definition in the given bean factory and attempt to replace ${...} property
	 * placeholders with values from the given properties.
	 */
	@Override
	protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
			throws BeansException {

		StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
		doProcessProperties(beanFactoryToProcess, valueResolver);
	}
2. PropertySourcesPlaceholderConfigurer

	/**
	 * Visit each bean definition in the given bean factory and attempt to replace ${...} property
	 * placeholders with values from the given properties.
	 */
	protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
			final ConfigurablePropertyResolver propertyResolver) throws BeansException {

		();
		();
		();

		StringValueResolver valueResolver = new StringValueResolver() {
			@Override
			public String resolveStringValue(String strVal) {
				String resolved = ignoreUnresolvablePlaceholders ?
						(strVal) :
						(strVal);
				return ((nullValue) ? null : resolved);
			}
		};

		doProcessProperties(beanFactoryToProcess, valueResolver);
	}

看下PropertyPlaceholderConfigurer类处理器调用的核心代码

	
	/**
	 * Resolve the given placeholder using the given properties, performing
	 * a system properties check according to the given mode.
	 * <p>The default implementation delegates to {@code resolvePlaceholder
	 * (placeholder, props)} before/after the system properties check.
	 * <p>Subclasses can override this for custom resolution strategies,
	 * including customized points for the system properties check.
	 * @param placeholder the placeholder to resolve
	 * @param props the merged properties of this configurer
	 * @param systemPropertiesMode the system properties mode,
	 * according to the constants in this class
	 * @return the resolved value, of null if none
	 * @see #setSystemPropertiesMode
	 * @see System#getProperty
	 * @see #resolvePlaceholder(String, )
	 */
	protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) {
		String propVal = null;
		// 默认采用locations配置的文件属性
		
		if (systemPropertiesMode == SYSTEM_PROPERTIES_MODE_OVERRIDE) {
			// 加载系统参数
			propVal = resolveSystemProperty(placeholder);
		}
		if (propVal == null) {
			// 从locations中加载参数
			propVal = resolvePlaceholder(placeholder, props);
		}
		if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) {
			// 如果从locations中没有发现参数,加载系统参数
			propVal = resolveSystemProperty(placeholder);
		}
		return propVal;
	}
	/**
	 * Resolve the given key as JVM system property, and optionally also as
	 * system environment variable if no matching system property has been found.
	 * @param key the placeholder to resolve as system property key
	 * @return the system property value, or {@code null} if not found
	 * @see #setSearchSystemEnvironment
	 * @see System#getProperty(String)
	 * @see System#getenv(String)
	 */
	protected String resolveSystemProperty(String key) {
		try {
			String value = (key);
			if (value == null && ) {
				value = (key);
			}
			return value;
		}
		catch (Throwable ex) {
			return null;
		}
	}

看下PropertySourcesPlaceholderConfigurer类处理器调用的核心代码

	protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
		if ( != null) {
			for (PropertySource<?> propertySource : ) {
				if (debugEnabled) {
					(("Searching for key '%s' in [%s]", key, ()));
				}
				//获取参数值,参数值为env和配置的locations文件中的属性值
				Object value = (key);
				if (value != null) {
					Class<?> valueType = ();
					if (resolveNestedPlaceholders && value instanceof String) {
						value = resolveNestedPlaceholders((String) value);
					}
					if (!(valueType, targetValueType)) {
					}
					return (value, targetValueType);
				}
			}
		}
		return null;
	}

客户端使用引入方式:

第一种:

	<context:property-placeholder location="classpath:" ignore-resource-not-found="true" ignore-unresolvable="true" />

该种方式默认的处理类为:

设置属性localOverride为true则location参数的优先级要大于系统变量。默认为false

第二种:

	<bean class="">
		<property name="ignoreUnresolvablePlaceholders" value="true" />
		<property name="ignoreResourceNotFound" value="true" />
		<property name="locations">
           <list>
              <value>classpath:conf/*.properties</value>
           </list>
		</property>
		<property name="fileEncoding" value="UTF-8" />
		<property name="order" value="-128" />
	</bean>	

设置属性systemPropertiesMode为2标识系统参数优先级最高。默认为1

来看下上述两种方式参数的来源,从源码中可以看出优先级。

1. PropertyPlaceholderConfigurer

	/**
	 * {@linkplain #mergeProperties Merge}, {@linkplain #convertProperties convert} and
	 * {@linkplain #processProperties process} properties against the given bean factory.
	 * @throws BeanInitializationException if any properties cannot be loaded
	 */
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		try {
			Properties mergedProps = mergeProperties();

			// Convert the merged properties, if necessary.
			convertProperties(mergedProps);

			// Let the subclass process the properties.
			processProperties(beanFactory, mergedProps);
		}
		catch (IOException ex) {
			throw new BeanInitializationException("Could not load properties", ex);
		}
	}
2. PropertySourcesPlaceholderConfigurer

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		if ( == null) {
			 = new MutablePropertySources();
			if ( != null) {
				(
					new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, ) {
						@Override
						public String getProperty(String key) {
							return (key);
						}
					}
				);
			}
			try {
				PropertySource<?> localPropertySource =
						new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
				if () {
					(localPropertySource);
				}
				else {
					(localPropertySource);
				}
			}
			catch (IOException ex) {
				throw new BeanInitializationException("Could not load properties", ex);
			}
		}

		processProperties(beanFactory, new PropertySourcesPropertyResolver());
		 = ;
	}

总结:

spring通过bean工厂后置处理器处理beanDefinition对象中属性值占位符的替换功能并注册@value注解处理器到bean工厂中。在装饰bean属性的过程中会触发bean后置处理器处理@value注解并设置属性值,@value注解处理器从bean工厂中获得。