本文主要是想说明Spring 中如何区分线上线下环境,并且源码中是如何实现的。
User.java
@Data public class User { private String username; private Integer age; public User() { } public User(String username, Integer age) { this.username = username; this.age = age; } }
UserController.java
import com.alibaba.fastjson.JSON; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; import javax.servlet.ServletContext; import java.util.ArrayList; import java.util.List; public class UserController extends AbstractController { @Override protected ModelAndView handleRequestInternal(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws Exception { List userList = new ArrayList(); User userA = new User("zhangsan", 27); User userB = new User("lisi", 28); userList.add(userA); userList.add(userB); ServletContext servletContext = request.getSession().getServletContext(); WebApplicationContext context = (WebApplicationContext) servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); User user = (User) context.getBean("user"); System.out.println(JSON.toJSONString(user)); return new ModelAndView("userlist", "users", userList); } }
spring112.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"></property> <property name="suffix" value=".jsp"></property> </bean> <bean id="user" class="com.spring_101_200.test_111_120.test_112_env.User"> <property name="username" value="${username}" /> <property name="age" value="${age}" /> </bean> <beans profile="local"> <context:property-placeholder location="classpath:spring_101_200/config_111_120/spring112_env/config.local.properties" /> </beans> <beans profile="test"> <context:property-placeholder location="classpath:spring_101_200/config_111_120/spring112_env/config.test.properties" /> </beans> </beans>
config.local.properties
username=zhangsan age=20
config.test.properties
username=lisi age=30
spring_tiny-servlet.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="simpleUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/userlist.htm">userController</prop> </props> </property> </bean> <bean id="userController" class="com.spring_101_200.test_111_120.test_112_env.UserController"></bean> </beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee" xmlns:web="http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee"> <display-name>spring_tiny</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring_101_200/config_111_120/spring112_env/spring112.xml</param-value> </context-param> <context-param> <param-name>spring.profiles.default</param-name> <param-value>local</param-value> </context-param> <servlet> <servlet-name>spring_tiny</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring_tiny</servlet-name> <url-pattern>*.htm</url-pattern> </servlet-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 自己配置描述文件,需要多少个描述文件就配置多少 --> <jsp-config> <!-- 配置c描述文件-对应c标签,这里的taglib-uri对应jsp中引入的uri --> <taglib> <taglib-uri>http://www.codecoord.com <taglib-location>/WEB-INF/c.tld </taglib> </jsp-config> </web-app>
idea 配置
【结果输出】
当不做任何配置时
【结果输出】
也就是JAVA_OPTIONS 没有配置时,使用web.xml中默认配置local,当在JAVA_OPTIONS中配置了-Dspring.profiles.active=test时,使用test配置项。那这在Spring中是如何实现的呢?
下面我们进入源码
加载环境,在web项目中一般加载的是StandardServletEnvironment环境变量,而在普通的Spring项目中,一般加载的是StandardEnvironment环境变量
@Override public ConfigurableEnvironment getEnvironment() { if (this.environment == null) { this.environment = createEnvironment(); } return this.environment; }
AbstractRefreshableWebApplicationContext.java
@Override protected ConfigurableEnvironment createEnvironment() { return new StandardServletEnvironment(); }
我们来看看StandardServletEnvironment类的关系图
我们知道StandardServletEnvironment是继承了AbstractEnvironment类,因此在初始化StandardServletEnvironment会调用父类的构造方法。代码如下
AbstractEnvironment.java
public AbstractEnvironment() { customizePropertySources(this.propertySources); if (this.logger.isDebugEnabled()) { this.logger.debug(format( "Initialized %s with PropertySources %s", getClass().getSimpleName(), this.propertySources)); } }
由java多态性质得到,在父类中调用customizePropertySources方法,最终将调用子类 的customizePropertySources方法。
StandardServletEnvironment.java
@Override protected void customizePropertySources(MutablePropertySources propertySources) { propertySources.addLast(new StubPropertySource("servletConfigInitParams")); propertySources.addLast(new StubPropertySource("servletContextInitParams")); if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) { propertySources.addLast(new JndiPropertySource("jndiProperties")); } super.customizePropertySources(propertySources); }
这个属性servletContextInitParams非常重要,在web.xml中配置的context-param标签主要存储在这个属性中。不过目前只是初始化,还没有赋值,在后面将看到web容器对此属性赋值。
在super.customizePropertySources()方法中,调用了父类StandardEnvironment的customizePropertySources方法,下面我们来看看这个方法的实现。
@Override protected void customizePropertySources(MutablePropertySources propertySources) { propertySources.addLast(new MapPropertySource("systemProperties", getSystemProperties())); propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", getSystemEnvironment())); }
systemProperties,systemEnvironment 这两个变量主要将环境变量存储在这两个属性中,
我们看到了JAVA_OPTIONS配置的环境参数存储在了systemProperties这个属性中。
我们继承向下走。
因为Spring MVC 项目本身就是基于Servlet实现的,因此,在这里我们看到了
ContextLoaderListener.java
这个类的继承关系如下
因为实现了ServletContextListener接口,因此实现了其中的contextInitialized方法。代码如下
ContextLoaderListener.java
@Override public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); }
ContextLoader.java
/** * 这里涉及了一个常用的类,webApplicationContext 在 web 应用中,我们会用到webApplicationContext ,webApplicationContext * 继承自ApplicationContext,在ApplicationContext 的基础上又追加了一些特定的web操作,及属性,非常类似于我们通过编程方式使用 * Spring 时使用的classPathXmlApplicatoinContext 类提供以功能,继续跟踪代码 * initWebApplicationContext 函数主要体现了创建webApplicationContext 实例的一个功能构架,从函数中我们可以看到初始化的大致步骤 * (1) WebApplicationContext 存在性验证 * 在配置中只允许声明一次ServletContextListener ,声明会扰乱Spring 的执行逻辑,所以这里首先要做的就是对此验证,在Spring 中 * 如果创建WebApplicationContext 实例会记录在ServletContext 中以方便全局调用,而使用的key 就是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE * 所以验证的方式就是查看ServletContext 实例中是否有对应的key 的属性 */ public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { // web.xml 中存在多次ContextLoader的定义 throw new IllegalStateException( "Cannot initialize context because there is already a root application context present - " + "check whether you have multiple ContextLoader* definitions in your web.xml!"); } Log logger = LogFactory.getLog(ContextLoader.class); servletContext.log("Initializing Spring root WebApplicationContext"); if (logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); } long startTime = System.currentTimeMillis(); try { if (this.context == null) { // 初始化context // (2) 创建WebApplicationContext 实例 // 通过验证,则Spring 将创建的WebApplicationContext 实例的工作委托给了createWebApplicationContext函数 this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> // determine parent for root web application context, if any. ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } configureAndRefreshWebApplicationContext(cwac, servletContext); } } // 记录在servletContext 中 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } if (logger.isDebugEnabled()) { logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); } if (logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); } return this.context; } catch (RuntimeException ex) { logger.error("Context initialization failed", ex); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; } catch (Error err) { logger.error("Context initialization failed", err); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); throw err; } }
ContextLoader.java
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // The application context id is still set to its original default value // -> assign a more useful id based on available information String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam != null) { wac.setId(idParam); } else { // Generate default id... wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } wac.setServletContext(sc); String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); if (configLocationParam != null) { wac.setConfigLocation(configLocationParam); } // The wac environment's #initPropertySources will be called in any case when the context // is refreshed; do it eagerly here to ensure servlet property sources are in place for // use in any post-processing or initialization that occurs below prior to #refresh ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(sc, null); } customizeContext(sc, wac); wac.refresh(); }
StandardServletEnvironment.java
@Override public void initPropertySources(ServletContext servletContext, ServletConfig servletConfig) { WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig); } }
WebApplicationContextUtils.java
public static void initServletPropertySources( MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) { if (servletContext != null && propertySources.contains("servletContextInitParams") && propertySources.get("servletContextInitParams") instanceof StubPropertySource) { propertySources.replace("servletContextInitParams", new ServletContextPropertySource("servletContextInitParams", servletContext)); } if (servletConfig != null && propertySources.contains("servletConfigInitParams") && propertySources.get("servletConfigInitParams") instanceof StubPropertySource) { propertySources.replace("servletConfigInitParams", new ServletConfigPropertySource("servletConfigInitParams", servletConfig)); } }
在这个方法中将servletContextInitParams值给替换成servletContextInitParams,而此时servletContext的值中己经包含了spring.profiles.default的值local,如下图所示。
通过上面的源码分析得出JAVA_OPTIONS参数最终以PropertySource【name 为systemProperties ,source为 Object对象】 对象的形式存储于 MutablePropertySources 的 propertySourceList集合中, web.xml中配置的spring.profile.active属性,以同样的方式存储。
环境变量都存储到propertySources属性中。
下面我们来分析bean的加载
AbstractBeanDefinitionReader.java
public int loadBeanDefinitions(String location, Set actualResources) throws BeanDefinitionStoreException { // 获取在Ioc容器 初始化过程中设置的资源加载器 ResourceLoader resourceLoader = getResourceLoader(); LogUtils.info(" loadBeanDefinitions ennter " + resourceLoader.getClass().getName(),8); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { try { // 将指定的位置的Bean配置信息解析为Spring Ioc容器封装的资源 // 加载多个指定位置的Bean的配置信息 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); // 委派调用其子类的XmlBeanDefinitionReader的方法实现加载功能 int loadCount = loadBeanDefinitions(resources); log.info(" loadBeanDefinitions arrays " + Arrays.toString(resources) + " , count = " + loadCount); if (actualResources != null) { for (Resource resource : resources) { actualResources.add(resource); } } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]"); } return loadCount; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { // 将指定位置的Bean的配置信息解析为Spring IoC容器封装的资源 // 加载单个指定位置的Bean配置信息 Resource resource = resourceLoader.getResource(location); // 委派调用其子类 XmlBeanDefinitionReader的方法,实现加载功能 int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } log.info("Loaded " + loadCount + " bean definitions from location [" + location + "]"); return loadCount; } }
AbstractBeanDefinitionReader.java
@Override public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int counter = 0; //遍历所有的资源,加载所有的bean for (Resource resource : resources) { counter += loadBeanDefinitions(resource); } return counter; }
XmlBeanDefinitionReader.java
@Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { log.info(" loadBeanDefinitions resource:" + resource); //将读入的XML资源进行特殊的编码处理 return loadBeanDefinitions(new EncodedResource(resource)); }
XmlBeanDefinitionReader.java
/** * 我们再次来整理一下数据准备阶段的逻辑,首先对传入的resource参数做封装,目的就是考虑到Resource可能存在编码要求的情况,其次 * SAX读取XML文件的方式来准备InputSource对象,最后将准备的数据通过参数传入真正的核心处理部分,doLoadBeanDefinition(inputResource,encodedResource.getResource()) */ public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { // 通过属性来记录己经加载的资源 Set currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { // 将资源文件转为InputStream的I/O流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { // 从InputStream中得到XML的解析源 // InputSource这个类并不是来自到Spring,而是来自到org.xml.sax InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } // 这里是具体的读取过程 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { // 关闭输出流 inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
XmlBeanDefinitionReader.java
/** * 从特定的XML文件中实际载入Bean资源的配置方法 */ protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { // 将XML文件转换成DOM对象,解析过程由documentLoader()方法实现 Document doc = doLoadDocument(inputSource, resource); // 根据Document注册Bean信息 return registerBeanDefinitions(doc, resource); } catch (Exception ex) { throw ex; } }
XmlBeanDefinitionReader.java
/** * 按照Spring的Bean的语义要求将Bean的配置信息解析并转换为容器内部的数据结构 * 其中的参数doc是通过上一节的loadDocument加载转换出来的,在这个方法中很好的应用了面向对象中的单一职责原则,将逻辑处理委托给单一 * 的类进行处理,而这个逻辑处理类就是BeanDefinitionDocumentReader,BeanDefinitionDocumentReader是一个接口,而实例化的工作就是在 * createBeanDefinitionDocumentReader()中完成的,而通过此方法,BeanDefinitionDocumentReader 真正的类型就是DefaultBeanDefinitionDocumentReader * 了,发现这个方法的重要目的就是提取root ,以便于再次将root作为参数继续BeanDefinition的注册 */ public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // 得到BeanDefinitionDocumentReader来对XML格式的BeanDefinition进行解析 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // 获取容器中的注册的Bean的数量 int countBefore = getRegistry().getBeanDefinitionCount(); // 解析过程的入口,这里使用了委派模式,BeanDefinitionDocumentReader只是一个接口 // 具体的解析过程实现类DefaultBeanDefinitionDocumentReader来完成 ,加载并注册bean documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // 统计解析的Bean的数量 return getRegistry().getBeanDefinitionCount() - countBefore; }
DefaultBeanDefinitionDocumentReader.java
/** * 根据Spring DTD对Bean定义规则解析Bean定义的文档对象 */ @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { //获取XML描述符 this.readerContext = readerContext; // 获取Document的根元素 Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); }
DefaultBeanDefinitionDocumentReader.java
protected void doRegisterBeanDefinitions(Element root) { // 具体的解析过程由BeanDefinitionParserDelegate实现 // BeanDefinitionParserDelegate 中定义了Spring Bean定义的XML文件中的各种元素 BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { // 通过层层代码的分析,这里终于到了 处理profile属性代码 String profileSpec = root.getAttribute("profile"); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { return; } } } // 在解析Bean定义之前,进行自定义解析,增强解析过程的扩展性 ,主要是留给子类实现 preProcessXml(root); // 从文档的根元素开始进行bean定义的文档对象来解析 parseBeanDefinitions(root, this.delegate); // 在解析Bean定义之后进行自定义解析,增加解析过程的可扩展性 ,主要是留给子类实现 postProcessXml(root); this.delegate = parent; }
AbstractEnvironment.java
@Override public boolean acceptsProfiles(String... profiles) { Assert.notEmpty(profiles, "Must specify at least one profile"); for (String profile : profiles) { if (profile != null && profile.length() > 0 && profile.charAt(0) == '!') { if (!isProfileActive(profile.substring(1))) { return true; } } else if (isProfileActive(profile)) { return true; } } return false; }
AbstractEnvironment.java
protected boolean isProfileActive(String profile) { validateProfile(profile); return doGetActiveProfiles().contains(profile) || (doGetActiveProfiles().isEmpty() && doGetDefaultProfiles().contains(profile)); }
AbstractEnvironment.java
protected Set<String> doGetActiveProfiles() { if (this.activeProfiles.isEmpty()) { String profiles = getProperty("spring.profiles.active"); if (StringUtils.hasText(profiles)) { setActiveProfiles(commaDelimitedListToStringArray(trimAllWhitespace(profiles))); } } return this.activeProfiles; }
我们继续来看getProperty方法
AbstractEnvironment.java
PropertySourcesPropertyResolver.java
@Override public String getProperty(String key) { return getProperty(key, String.class, true); } protected T getProperty(String key, Class targetValueType, boolean resolveNestedPlaceholders) { if (this.propertySources != null) { //遍历系统环境及web环境中配置的属性资源 for (PropertySource<?> propertySource : this.propertySources) { Object value; if ((value = propertySource.getProperty(key)) != null) { Class<?> valueType = value.getClass(); if (resolveNestedPlaceholders && value instanceof String) { //下面的这一行代码主要是将${user},解析出user的值,在另外一篇博客中有详细说明 //https://blog.csdn.net/quyixiao/article/details/108482668 value = resolveNestedPlaceholders((String) value); } if (!this.conversionService.canConvert(valueType, targetValueType)) { throw new IllegalArgumentException(String.format( "Cannot convert value [%s] from source type [%s] to target type [%s]", value, valueType.getSimpleName(), targetValueType.getSimpleName())); } //最终根据传入的Class转换成需要的类型 return this.conversionService.convert(value, targetValueType); } } } return null; }
经过多次循环后, 我们终于在systemProperties 的PropertySource中拿到了spring.profile.active的值test
而这个值就是本篇博客开头时,在解析环境变量时所述,将JAVA_OPTIONS的值存储到以systemProperties为name的PropertySource对象中。因此,我们拿到了 系统配置的环境变量是test
我们回到刚刚的代码
因为doGetActiveProfiles()返回值是test ,而doGetActiveProfiles().isEmpty()肯定是false ,所以doGetDefaultProfiles()方法不会执行,因此isProfileActive()方法返回值是false ,
因此下面的beans将不会被解析
web.xml
<beans profile="local"> <context:property-placeholder location="classpath:spring_101_200/config_111_120/spring112_env/config.local.properties" /> </beans>
同理,如果没有配置JAVA_OPTIONS参数 ,程序将按下面的方式执行。
AbstractEnvironment.java
protected boolean isProfileActive(String profile) { validateProfile(profile); return doGetActiveProfiles().contains(profile) || (doGetActiveProfiles().isEmpty() && doGetDefaultProfiles().contains(profile)); }
AbstractEnvironment.java
protected Set doGetDefaultProfiles() { if (this.defaultProfiles.equals(getReservedDefaultProfiles())) { String profiles = getProperty("spring.profiles.default"); if (StringUtils.hasText(profiles)) { setDefaultProfiles(commaDelimitedListToStringArray(trimAllWhitespace(profiles))); } } return this.defaultProfiles; }
同样,我们到getProperty属性中去查找,在这个方法中,我们看到从ServletContextPropertySource [name=‘servletContextInitParams’] 中获得了spring.profiles.default的值为local
因此,如果没有配置JAVA_OPTIONS值时,系统默认是使用local的配置的,而local这个属性又是在web.xml中context-param标签中配置
<beans profile="local"> <context:property-placeholder location="classpath:spring_101_200/config_111_120/spring112_env/config.local.properties" /> </beans>
到这里我们来总结一下,如果在JAVA_OPTIONS中配置了-Dspring.profiles.active=test属性,则使用
<beans profile="test"> <context:property-placeholder location="classpath:spring_101_200/config_111_120/spring112_env/config.test.properties" /> </beans>
因为在systemProperties中找到了spring.profiles.active的属性为test ,
而如果没有配置JAVA_OPTIONS属性,则使用默认的spring.profiles.default变量到PropertySource中去查找,最终在ServletContextPropertySource中找到了spring.profiles.default的属性值为local,因此配置了local的beans将继续被解析。
下面我们继续来分析这个的解析,这次不使用JAVA_OPTIONS参数
<beans profile="local"> <context:property-placeholder location="classpath:spring_101_200/config_111_120/spring112_env/config.local.properties" /> </beans>
DefaultBeanDefinitionDocumentReader.java
/** * 使用Spring的Bean规则从文档的根元素开始Bean定义的文档对象解析 */ protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { // Bean 的定义的文档对象使用了Spring默认的命名空间, 自定义的命名空间的还是自定义的命名空间, // 并与Spring中固定的命名空间 "http://www.springframework.org/schema/beans" ,进行比对, // 如果一致是默认,否则就认为是自定义的 if (delegate.isDefaultNamespace(root)) { // 获取Bean定义的文档对象根元素的所有子节点 NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { // 获取文档节点的XML元素的节点 Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { // 开始默认标签解析 , 如<bean id="test" class="test.TestBean"> parseDefaultElement(ele, delegate); } else { // 如果没有使用Spring默认的xml命名空间,则使用用户自定义的解析规则解析元素的节点,开始自定义标签两种格式的区分 // Spring 拿到一个元素时首先要做的是根据命名空间进行解析,如果是默认的命名空间,则使用 parseDefaultElement方法 // 进行元素解析,否则使用 parseCustomElment 元素进行解析 // 如:<ltx:annotation-driven/> // <context:property-placeholde /> delegate.parseCustomElement(ele); } } } } else { // 文档对象根节点没有使用Spring默认的命名空间 // 使用自定义的解析规则解析文档的根节点 delegate.parseCustomElement(root); } }
我们继续跟进代码
BeanDefinitionParserDelegate.java
public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); }
BeanDefinitionParserDelegate.java
// containingBd 为父类的 BeanDefinition ,对顶层元素的解析应该设置为 null // 其实思路非常的简单,无非是根据对应的Bean获取对应的命名空间,根据命名空间解析对应的处理器,然后根据用户自定义的处理器进行解析, // 可是有些事情说起来简单做起来难,我们先看看如何获取命名空间吧。 public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { // 获取对应的命名空间 ,http://www.springframework.org/schema/context String namespaceUri = getNamespaceURI(ele); // 根据命名空间找到对应的 NamespaceHandler, // 在 readerContext 初始化的时候,其属性 namespaceHandlerResolver 已经被初始化成了DefaultNamespaceHandlerResolver // 的实例了,所以这里调用 resolver 方法其实调用的是 DefaultNamespaceHandlerResolver 类中的方法,我们进入了 // DefaultNamespaceHandlerResolver 的 resolver 方法进行查看 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } // 调用自定义的 NamespaceHandler 进行解析 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
我们知道this.readerContext 是XmlReaderContext对象,而这个值是哪里来的呢?
我们回顾一下之前的代码。
XmlBeanDefinitionReader.java
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
XmlBeanDefinitionReader.java
public XmlReaderContext createReaderContext(Resource resource) { return new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, getNamespaceHandlerResolver()); }
XmlBeanDefinitionReader.java
public NamespaceHandlerResolver getNamespaceHandlerResolver() { if (this.namespaceHandlerResolver == null) { this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver(); } return this.namespaceHandlerResolver; }
XmlBeanDefinitionReader.java
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() { return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader()); }
DefaultNamespaceHandlerResolver.java
public DefaultNamespaceHandlerResolver(ClassLoader classLoader) { this(classLoader, "META-INF/spring.handlers"); }
因此在 this.readerContext.getNamespaceHandlerResolver()返回了DefaultNamespaceHandlerResolverResolver对象。
再调用resolve方法
DefaultNamespaceHandlerResolver.java
/** * 上面的函数清晰的单阐述解析自定义NamespaceHandler的过程,通过之前的示例程序,,我们了解到中如果要使用自定义的标签,那么其中一个必不可少的 * 操作就是Spring.handlers文件中配置命名空间与命名空间处理映射关系,只有这样,Spring才能根据映射关系找到匹配的处理器,而寻找匹配的处理器就是在上面的函数 * 中实现了,当获取到自定义的NamespaceHandler之后就可以进行处理器初始化并解析了 * 注册后,命名空间处理器就可以根据标签的不同来调用不同的解析器进行解析了,那,根据上面的函数之间介绍过的例子,我们基本上可以推断, * getHandlerMappings 中的主要功能就是读取 Spring.handlers 配置文件并将配置文件缓存到 map 中 */ @Override public NamespaceHandler resolve(String namespaceUri) { // 获取所有已经配置的 handler 映射 Map<String, Object> handlerMappings = getHandlerMappings(); // 根据命名空间找到对应的信息 Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } else if (handlerOrClassName instanceof NamespaceHandler) { // 已经做过解析的情况,直接从缓存中读取 return (NamespaceHandler) handlerOrClassName; } else { // 没有做过解析,则返回的是类路径 String className = (String) handlerOrClassName; try { // 使用反射将类路径转化成类 Class<?> handlerClass = ClassUtils.forName(className, this.classLoader); if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } // 初始化类 NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); // 调用自定义的 NamespaceHandler 的初始化方法 namespaceHandler.init(); // 记录到缓存中 handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } catch (ClassNotFoundException ex) { throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", ex); } catch (LinkageError err) { throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", err); } } } /** * 同我们想象中的一样,借助于工具类PropertiesLoaderUtils对属性handlerMappingsLocation进行了配置文件的读取,handlerMappingsLocation * 被默认初始化为"META-INF/Spring.handlers" */ private Map<String, Object> getHandlerMappings() { // 如果没有被缓存则,开始缓存 if (this.handlerMappings == null) { synchronized (this) { if (this.handlerMappings == null) { try { // this.handlerMappingsLocation 在构造函数中已经被初始化为:META-INF/Spring.handlers Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (logger.isDebugEnabled()) { logger.debug("Loaded NamespaceHandler mappings: " + mappings); } Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size()); // 将 properties 格式文件合并到 Map 格式的 handlerMapping 中 CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); this.handlerMappings = handlerMappings; } catch (IOException ex) { throw new IllegalStateException( "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex); } } } } return this.handlerMappings; }
通过上面的代码,我们得到了handlerMapping的值
handlerMapping主要是加载classpath* 下的spring.handlers文中的内容,如http://www.springframework.org/schema/context -> org.springframework.context.config.ContextNamespaceHandler 则是加载context包下的spring.handlers文件内容。
从代码来看通过反射调用得到了ContextNamespaceHandler对象,再调用init方法
在ContextNamespaceHandler中,我们终于看到了property-placeholder标签是由PropertyPlaceholderBeanDefinitionParser类来解析BeanDefinition的。
NamespaceHandlerSupport.java
@Override public BeanDefinition parse(Element element, ParserContext parserContext) { return findParserForElement(element, parserContext).parse(element, parserContext); } //获取parser类 private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) { // localName = property-placeholder String localName = parserContext.getDelegate().getLocalName(element); BeanDefinitionParser parser = this.parsers.get(localName); if (parser == null) { parserContext.getReaderContext().fatal( "Cannot locate BeanDefinitionParser for element [" + localName + "]", element); } return parser; }
parse方法调用的是AbstractBeanDefinitionParser.java中的parse方法,下面我们来看一下继承关系图
因此调用了继承方法parse()
AbstractBeanDefinitionParser.java
@Override public final BeanDefinition parse(Element element, ParserContext parserContext) { // 虽说是对自定义配置文件的解析,但是,我们可以看到,这个函数中大部分的代码是用来处理解析后的 AbstractBeanDefinition // 转化为 BeanDefinitionHolder 并注册的功能,而真正的去做解析的事情委托给了函数 ParseInternal,正是这名代码调用了我们 // 自定义的解析函数 // 在 parseInternal 中并不是直接调用自定义的 doParse 函数,而是进行了一系列的数据准备,包括对 beanClass,scope ,lazyInit // 等属性的准备 AbstractBeanDefinition definition = parseInternal(element, parserContext); if (definition != null && !parserContext.isNested()) { try { String id = resolveId(element, definition, parserContext); if (!StringUtils.hasText(id)) { parserContext.getReaderContext().error( "Id is required for element '" + parserContext.getDelegate().getLocalName(element) + "' when used as a top-level tag", element); } String[] aliases = null; if (shouldParseNameAsAliases()) { String name = element.getAttribute(NAME_ATTRIBUTE); if (StringUtils.hasLength(name)) { aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name)); } } // 将 AbstractBeanDefinition 转为 BeanDefinitionHolder 并注册 BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases); registerBeanDefinition(holder, parserContext.getRegistry()); if (shouldFireEvents()) { // 需要通过通知监听器则进行处理 BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder); postProcessComponentDefinition(componentDefinition); parserContext.registerComponent(componentDefinition); } } catch (BeanDefinitionStoreException ex) { parserContext.getReaderContext().error(ex.getMessage(), element); return null; } } return definition; }
AbstractSingleBeanDefinitionParser.java
/** * 回顾一下全部的自定义标签处理过程,虽然在实例中我们定义的 UserBeanDefinitionParser,但是其中我们只做了与自己业务逻辑相关的部分 * ,不过,我们没有做,但是并不是代表没有,在这个处理过程中同样的是按照 Spring 中默认的标签处理方式进行的,包括创建 BeanDefintion * 以及进行相应的默认的属性的设置,对于这些工作,Spring都默默的帮我们实现了,只是暴露出一些接口,来借用户实现修改业务 */ @Override protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); String parentName = getParentName(element); if (parentName != null) { builder.getRawBeanDefinition().setParentName(parentName); } // 获取自定义标签中的 class Class<?> beanClass = getBeanClass(element); if (beanClass != null) { builder.getRawBeanDefinition().setBeanClass(beanClass); } else { // 如果子类没有重写 getBeanClass 方法,则尝试检查子类 是否重写了 getBeanClassName 方法 String beanClassName = getBeanClassName(element); if (beanClassName != null) { builder.getRawBeanDefinition().setBeanClassName(beanClassName); } } builder.getRawBeanDefinition().setSource(parserContext.extractSource(element)); if (parserContext.isNested()) { // 如果存在父类则使用父类的 scope 属性 builder.setScope(parserContext.getContainingBeanDefinition().getScope()); } if (parserContext.isDefaultLazyInit()) { // 配置了延迟加载 builder.setLazyInit(true); } // 调用子类 重写的 doParse 方法进行解析 doParse(element, parserContext, builder); return builder.getBeanDefinition(); }
PropertyPlaceholderBeanDefinitionParser.java
@Override protected Class<?> getBeanClass(Element element) { if (element.getAttribute("system-properties-mode").equals("ENVIRONMENT")) { return PropertySourcesPlaceholderConfigurer.class; } return PropertyPlaceholderConfigurer.class; }
在代码中我找了很久都没有找到在哪里默认设置了system-properties-mode属性,最后全局搜索才找到
在spring的.xsd文件中system-properties-mode属性默认的配置的值是ENVIRONMENT
代码通过getBeanClass得到了PropertySourcesPlaceholderConfigurer类,再调用子类
PropertyPlaceholderBeanDefinitionParser.java
@Override protected void doParse(Element element, BeanDefinitionBuilder builder) { //准备初始化数据 super.doParse(element, builder); builder.addPropertyValue("ignoreUnresolvablePlaceholders", Boolean.valueOf(element.getAttribute("ignore-unresolvable"))); String systemPropertiesModeName = element.getAttribute("system-properties-mode"); if (StringUtils.hasLength(systemPropertiesModeName) && !systemPropertiesModeName.equals("system-properties-mode")) { builder.addPropertyValue("systemPropertiesModeName", "SYSTEM_PROPERTIES_MODE_"+systemPropertiesModeName); } }
AbstractPropertyLoadingBeanDefinitionParser.java
@Override protected void doParse(Element element, BeanDefinitionBuilder builder) { //这个属性很重要 ,classpath:spring_101_200/config_111_120/spring112_env/config.local.properties // 会将location的值保存到BeanDefinition的propertyValueList属性中 String location = element.getAttribute("location"); if (StringUtils.hasLength(location)) { String[] locations = StringUtils.commaDelimitedListToStringArray(location); builder.addPropertyValue("locations", locations); } String propertiesRef = element.getAttribute("properties-ref"); if (StringUtils.hasLength(propertiesRef)) { builder.addPropertyReference("properties", propertiesRef); } String fileEncoding = element.getAttribute("file-encoding"); if (StringUtils.hasLength(fileEncoding)) { builder.addPropertyValue("fileEncoding", fileEncoding); } String order = element.getAttribute("order"); if (StringUtils.hasLength(order)) { builder.addPropertyValue("order", Integer.valueOf(order)); } builder.addPropertyValue("ignoreResourceNotFound", Boolean.valueOf(element.getAttribute("ignore-resource-not-found"))); builder.addPropertyValue("localOverride", Boolean.valueOf(element.getAttribute("local-override"))); builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); }
到这里我们得到了PropertySourcesPlaceholderConfigurer类的BeanDefinition,
我们继续回到刚刚的代码
AbstractBeanDefinitionParser.java
@Override public final BeanDefinition parse(Element element, ParserContext parserContext) { // 虽说是对自定义配置文件的解析,但是,我们可以看到,这个函数中大部分的代码是用来处理解析后的 AbstractBeanDefinition // 转化为 BeanDefinitionHolder 并注册的功能,而真正的去做解析的事情委托给了函数 ParseInternal,正是这名代码调用了我们 // 自定义的解析函数 // 在 parseInternal 中并不是直接调用自定义的 doParse 函数,而是进行了一系列的数据准备,包括对 beanClass,scope ,lazyInit // 等属性的准备 AbstractBeanDefinition definition = parseInternal(element, parserContext); if (definition != null && !parserContext.isNested()) { try { //对于bean id的获取,也是一门学问,我在另外一篇博客中来写吧 // https://blog.csdn.net/quyixiao/article/details/108564636 String id = resolveId(element, definition, parserContext); if (!StringUtils.hasText(id)) { parserContext.getReaderContext().error( "Id is required for element '" + parserContext.getDelegate().getLocalName(element) + "' when used as a top-level tag", element); } String[] aliases = null; if (shouldParseNameAsAliases()) { String name = element.getAttribute(NAME_ATTRIBUTE); if (StringUtils.hasLength(name)) { aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name)); } } // 将 AbstractBeanDefinition 转为 BeanDefinitionHolder 并注册 BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases); registerBeanDefinition(holder, parserContext.getRegistry()); if (shouldFireEvents()) { // 需要通过通知监听器则进行处理 BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder); postProcessComponentDefinition(componentDefinition); parserContext.registerComponent(componentDefinition); } } catch (BeanDefinitionStoreException ex) { parserContext.getReaderContext().error(ex.getMessage(), element); return null; } } return definition; }
将得到的BeanDefinition封装成BeanDefinitionHolder并注册到beanDefinitionMap中
AbstractBeanDefinitionParser.java
protected void registerBeanDefinition(BeanDefinitionHolder definition, BeanDefinitionRegistry registry) { BeanDefinitionReaderUtils.registerBeanDefinition(definition, registry); }
BeanDefinitionReaderUtils.java
/** * 将解析的BeanDefinitionHolder 注册到Spring IoC容器中 * 当调用BeanDefintionReaderUtils向Spring IoC容器中注册解析的BeanDefintion时,真正完成注册功能是DefaultListableBeanFactory * 从这个代码中,我们可以看出,解析的beanDefinition被注册到BeanDefinitionRegistry 类型的实例registry中,而对于 beanDefintion 的注册分成两个部分, * 通过 beanName 的注册,以及通过别名来注册 */ public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { //获取解析的BeanDefinition的名称, 使用 BeanName 做为唯一的标识注册 String beanName = definitionHolder.getBeanName(); //向Spring Ioc容器注册BeanDefinition registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 如果解析的BeanDefinition有别名,向Spring IoC容器中注册别名,注册所有的别名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
DefaultListableBeanFactory.java
// 向Spring IoC容器中注册解析的BeanDefinition // 至此,Bean配置信息中配置了Bean解析后,已经注册到了Spring Ioc容器中,被容器管理起来,真正的完成了Spring IoC容器的初始化工作 // 现在Spring IoC 容器中已经建立起了所有的Bean的配置信息,Bean定义信息已经可以使用了,并且可以被检索, // Spring IoC容器的作用就是对这些注册的bean定义的信息进行处理和维护,注册的Bean定义的信息 // 是Spring IoC容器的控制反转的基础,也正是有了这些信息,容器才进行依赖注入的 // Spring IoC容器对于类级别的注解和类内部的注解处理策略如下 // 类级别的注解,如@Component,@Repository,@Controller,@Service,以及JavaEE 6的@ManageBean @Named,都是添加在类上的类级别的注解 // Spring IoC容器根据注解的过滤规则,扫描读取注解的Bean的定义类,并将其注册到Spring IoC容器中 // 2.类内部的注解,如@Autoweire,@Value,@Resource,以及EJB和WebService相关的注解等,都是添加在类内部的字段或者方式上的类部注解 // Spring IoC容器通过Bean后置注解处理解析Bean内部的注解 // 下面将分析Spring 处理注解相关的源码 // 对于 beanDefinition注册,或许很多的人认为的方式就是将 beanDefinition 直接放入到 map中就好了,使用 BeanName 作为key ,确实 // Spring 就是这样做的,只不过除此之外,它还做了一些其他的事情 // 1.对于 abstractBeanDefinition 的校验,在解析 XML文件的时候,我们提过校验,但是些校验非彼校验,之前的校验时针对XML 格式的校验,而 // 此时的校验是针对于 AbstractBeanDefinition 的 methodOverrides 属性的 // 2.对 beanName 已经注册的情况的处理,如果设置了不允许 bean覆盖,则需要抛出异常,否则直接覆盖 // 3.加入 map 缓存 // 4.清除解析之前留下的对应的 beanName 的缓存 @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { // 校验解析BeanDefinition if (beanDefinition instanceof AbstractBeanDefinition) { try { //注册前的最后一次校验,这里检验不同于之前的 XML 文件的校验,主要是对于 AbstractBeanDefinition 属性中的 methodOverrides 校验 // 校验的 methodsOverrides 是否是工厂方法并存或者 methodOverrides 对应的方法根本不存在 ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } BeanDefinition oldBeanDefinition; // beanDefinitionMap 是一个全局的变量,这里定会存在并发访问的情况 oldBeanDefinition = this.beanDefinitionMap.get(beanName); // 处理注册己经注册的beanName的情况 if (oldBeanDefinition != null) { // 如果对应的了 BeanName 已经注册且配置了 Bean 不允许被覆盖,则抛出异常 if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound."); } else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) { if (this.logger.isWarnEnabled()) { this.logger.warn("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(oldBeanDefinition)) { if (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else { if (this.logger.isDebugEnabled()) { this.logger.debug("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } } else { //记录 beanName this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); this.frozenBeanDefinitionNames = null; } // 注册 beanDefinition this.beanDefinitionMap.put(beanName, beanDefinition); // 检查是否已经注册过同名的BeanDefinition if (oldBeanDefinition != null || containsSingleton(beanName)) { // 重置所有已经注册过的BeanDefinition中的缓存 resetBeanDefinition(beanName); } }
到这里己经完成了property-placeholder的解析
我们来分析一下User对象的解析
<bean id="user" class="com.spring_101_200.test_111_120.test_112_env.User"> <property name="username" value="${username}" /> <property name="age" value="${age}" /> </bean>
因为篇符比较长,因此我写到了另外的一篇博客中。
https://blog.csdn.net/quyixiao/article/details/108565765 有兴趣的小伙伴可以去看一下。
到这里,我们己经完成了对property-placeholder标签和对user对象的解析,在什么时候完成对${username}对象的替换的呢?
在创建 org.springframework.context.support.PropertySourcesPlaceholderConfigurer#0 时对属性填充
locations属性在后面有重要作用
AbstractApplicationContext.java
/*** * refresh()方法主要为IoC容器Bean的生命周期管理提供条件,Spring Ioc 容器载入了Bean的配置,信息从其子类容器的refreshBeanFactory * 方法启动,所以整个refresh()方法中的"ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory" 以后的代码都是在 * 注册容器信息源和生命周期事件,我们前面说的载入就是通过这个代码启动的 * * refresh()方法的主要作是: 在创建Ioc容器前,如果已经有容器存在,需要把已有的容器销毁和关闭,以保证在refresh()方法之后的使用的是新 * 创建的Ioc容器,它类似于对Ioc容器的重启,在新创建的容器中对容器进行初始化,对Bean配置资源进行载入 * * * 我们已经知道,IoC容器初始化过程就是对Bean定义资源的定位,载入和注册,此时容器对Bean的依赖注入并没有发生,依赖注入是在应用程序 * 第一次向索取Bean时通过getBean()方法来完成的 * * 当Bean定义资源的Bean元素中配置了lazy-init=false 属性时,容器将会在初始化时对所配置的Bean进行实例化,Bean的依赖注入在容器在容器 * 初始化时就已经完成,这样应用程序第一次向容器索取被管理的Bean时,就不用再初始化和对应的Bean进行依赖注入了,而是直接从容器中获取 * 已经完成依赖注入Bean,提高了应用程序第一次向容器获取Bean的性能 * * refresh()方法 * IOC 容器读入已经定位的Bean 定义资源是从refresh()方法开始的,我们从AbstractApplicationContext类的refresh()方法入手分析 * * 1.下面概括一下 ClassPathXmlApplicationContext 初始化的步骤,并从中解释一下它为我们提供的功能 * 1. 初始化前的准备工作,例如对系统属性或者环境变量进行准备及验证 * 在某种情况下,项目的使用需要读取某些系统变量,而这个变量的设置很可能会影响到系统的正确性,那么 ClassPathXmlApplicationContext * 为我们提供的准备函数就显得非常的必要了,它可以在 Spring 启动的时候提前对必需的变量进行存在性验证 * 2.初始化 beanFactory 进行 Xml 文件的读取 * 之前有提到的 ClasspathXmlApplicationContext包含着 BeanFactory 所提供的一切特征,在这一步骤中将会复用 BeanFActory 中的配置 * 文件读取及解析其他的功能,这一步之后,ClassPathXmlApplicationContext 实际上就已经包含了 BeanFactory 所提供的功能,也就是可以 * 进行 Bean 的提取等基础操作了 * 3.对 BeanFactory 进行各种功能的填充 * @Qualifier 与@Autowired 应该是大家非常熟悉的注解了,那么这两个注册正是这一步骤增加的支持 * 4.Spring 之所以强大,除了它功能上为大家提供了便例外,还有一方面它的完美架构,开放式的架构让使用它的程序员很容易根据业务需要扩展 * 已经存在的功能,这种开放式的设置在 Spring中随处可见,例如在配合中就提供了一个空间函数的实现,postProcessBeanFactory 来方便程序员 * 在业务上做进步的扩展 * 5. 激活各种 beanFactory 的处理器 * 6.注册拦截 bean 创建的 bean 处理器,这里只是注册,真正的调用是在 getBean时候 * 7.为上下文初始化 Message源,即对不同的语言的消息进行国际化的处理 * 8. 初始化应用的消息广播器,并放入到"applicationEventMulticaster" bean 中 * 9.留给子类来初始化其他的 bean * 10,所有的注册的 bean 中查找 listener bean 注册到的消息广播器中 * 11.初始化剩下的单例(非惰性的) * 12. 完成刷新的过程,通知生命周期处理器 lifecycleProcessor 刷新过程,同时发出 contextRefreshEvent 通知别人 * * */ @Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 1.调用容器准备刷新的方法,获取容器的当前时间,同时给容器设置同步标识 prepareRefresh(); // 告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从子类 的refreshBeanFactory()方法启动 // 在refresh()方法中 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory() 启动了Bean的注册 // Bean定义资源的载入,注册过程,finishBeanFactoryInitialization() 方法是对注册后的Bean定义中的预实例化(lazy-init=false) // Spring 默认进行预实例化,即为true的Bean 进行处理的地方 // 初始化 bean ,并野德 xml 文件的读取 // obtainFreshBeanFactory 方法从字面的理解是获取 BeanFactory ,之前有说过,ApplicationContext 是对 BeanFactory // 的功能上基础上添加了大量的扩展应用,那么 obtainFreshBeanFactory 正是实现 BeanFactory 的地方,也就是经过这个函数之后 // ApplicationContext 就已经拥有 BeanFactory 的全部功能 // 这里是在子类中启动refreshBeanFactory()的地方 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 3.为BeanFactory配置容器,例如类加载器,事件处理器 , 为 BeanFactory 进行各种功能进行填充 prepareBeanFactory(beanFactory); try { // 4.为容器的某些子类指定特殊的Post事件处理器 postProcessBeanFactory(beanFactory); // 5.调用所有的注册的beanFactoryPostProcessor的bean invokeBeanFactoryPostProcessors(beanFactory); // 6.为BeanFactory注册Post事件处理器 // BeanPostProcessor(BeanFactory) // 注册拦截 bean 创建 bean 处理器,这里只是注册,真正的调用在 getBean 时候 registerBeanPostProcessors(beanFactory); // 为上下文初始化 Message源,即不同的语言的消息体,国际化处理 initMessageSource(); // 8.初始化容器事件传播器 | 初始化应用消息广播器,并放入到"applicationEventMulticaster" bean 中 initApplicationEventMulticaster(); // 9.调用子类的某些特殊bean的初始化方法= onRefresh(); // 为事件传播器注册事件监听器,所有的注册的 bean 中查找 Listener Bean ,注册到消息广播器中 registerListeners(); // 11.初始化所有剩余的单例Bean finishBeanFactoryInitialization(beanFactory); // 12.初始化容器的生命周期事件处理器,为发布容器的生命周期事件 // 过程,同时发出 contextRefreshEvent 通知别人 finishRefresh(); } catch (BeansException ex) { // 13.销毁已经创建的bean destroyBeans(); // 14.取消刷新操作,重置容器的同步标识 cancelRefresh(ex); throw ex; } finally { // 设置公共缓存 resetCommonCaches(); } } }
AbstractApplicationContext.java
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); }
PostProcessorRegistrationDelegate.java
public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) { Set processedBeans = new HashSet(); // 1.判断beanFactory是否为BeanDefinitionRegistry,beanFactory为DefaultListableBeanFactory, // 而DefaultListableBeanFactory实现了BeanDefinitionRegistry接口,因此这边为true ,对 BeanDefinitionRegistry 类型处理 if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; // 记录通过硬编码的方式注册的 BeanFactoryPostProcessor 类型的处理器 List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<BeanFactoryPostProcessor>(); // 记录通过硬编码的方式注册的 BeanDefinitionRegistryPostProcessor List<BeanDefinitionRegistryPostProcessor> registryPostProcessors = new LinkedList<BeanDefinitionRegistryPostProcessor>(); // 遍历所有的beanFactoryPostProcessors, 将BeanDefinitionRegistryPostProcessor和普通BeanFactoryPostProcessor区分开 for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { BeanDefinitionRegistryPostProcessor registryPostProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor; //调用它的后置方法 ,对于 beanDefinitionRegistryPostProcessor 类型,在 BeanFactoryPostProcessor 的基础上还有自己定义的方法需要先调用 registryPostProcessor.postProcessBeanDefinitionRegistry(registry); //添加到我们用于保存的BeanDefinitionRegistryPostProcessor的集合中 registryPostProcessors.add(registryPostProcessor); } else { //若没有实现BeanDefinitionRegistryPostProcessor接口,那么他就是BeanFactoryPostProcessor 把当前的后置处理器加入到regularPostProcessors中 regularPostProcessors.add(postProcessor); } } //第一步:去容器中获取BeanDefinitionRegistryPostProcessor的bean的处理器名称 String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); List priorityOrderedPostProcessors = new ArrayList(); for (String ppName : postProcessorNames) { //判断是否实现了PriorityOrdered接口的 if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { //显示的调用getBean()的方式获取出该对象然后加入到currentRegistryProcessors集合中去 priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } //对currentRegistryProcessors集合中BeanDefinitionRegistryPostProcessor进行排序 sortPostProcessors(beanFactory, priorityOrderedPostProcessors); //把他加入到用于保存到registryProcessors中 registryPostProcessors.addAll(priorityOrderedPostProcessors); /** * 在这里典型的BeanDefinitionRegistryPostProcessor就是ConfigurationClassPostProcessor * 用于进行bean定义的加载 比如我们的包扫描,@import等 */ invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry); postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); List<BeanDefinitionRegistryPostProcessor> orderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>(); for (String ppName : postProcessorNames) { //表示没有被处理过,且实现了Ordered接口的 if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) { //显示的调用getBean()的方式获取出该对象然后加入到currentRegistryProcessors集合中去 orderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); //同时也加入到processedBeans集合中去 processedBeans.add(ppName); } } sortPostProcessors(beanFactory, orderedPostProcessors); registryPostProcessors.addAll(orderedPostProcessors); // 调用他的后置处理方法 invokeBeanDefinitionRegistryPostProcessors(orderedPostProcessors, registry); // 调用没有实现任何优先级接口的BeanDefinitionRegistryPostProcessor // 定义一个重复处理的开关变量 默认值为true boolean reiterate = true; // 第一次就可以进来 while (reiterate) { //进入循环马上把开关变量给改为false reiterate = false; //去容器中获取BeanDefinitionRegistryPostProcessor的bean的处理器名称 postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { //没有被处理过的 if (!processedBeans.contains(ppName)) { //显示的调用getBean()的方式获取出该对象然后加入到currentRegistryProcessors集合中去 BeanDefinitionRegistryPostProcessor pp = beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class); registryPostProcessors.add(pp); //同时也加入到processedBeans集合中去 processedBeans.add(ppName); //同时也加入到processedBeans集合中去 pp.postProcessBeanDefinitionRegistry(registry); //再次设置为true reiterate = true; } } } // 调用实现了 BeanDefinitionRegistryPostProcessor 的接口 他也同时实现了BeanFactoryPostProcessor的方法 // 激活 postProcessBeanFactory 方法,之前激活的是 postProcessBeanDefinitionRegistry // 硬编码设置 BeanDefinitionRegistryPostProcessor invokeBeanFactoryPostProcessors(registryPostProcessors, beanFactory); //调用BeanFactoryPostProcessor invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); } else { //若当前的beanFactory没有实现了BeanDefinitionRegistry 直接调用beanFactoryPostProcessor接口的方法进行后置处理 invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); } //最后一步 获取容器中所有的 BeanFactoryPostProcessor ,下面这个方法很重要,等以后有机会再来详细解析这一个工具方法,我们知道 String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); //保存BeanFactoryPostProcessor类型实现了priorityOrdered List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); //保存BeanFactoryPostProcessor类型实现了Ordered接口的 List<String> orderedPostProcessorNames = new ArrayList<String>(); //保存BeanFactoryPostProcessor没有实现任何优先级接口的 List<String> nonOrderedPostProcessorNames = new ArrayList<String>(); // 对后处理进行分类 for (String ppName : postProcessorNames) { //processedBeans包含的话,表示在上面处理BeanDefinitionRegistryPostProcessor的时候处理过了 if (processedBeans.contains(ppName)) { //判断是否实现了PriorityOrdered } else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); //判断是否实现了Ordered } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); //没有实现任何的优先级接口的 } else { nonOrderedPostProcessorNames.add(ppName); } } //先调用BeanFactoryPostProcessor实现了PriorityOrdered接口的 sortPostProcessors(beanFactory, priorityOrderedPostProcessors); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory); //再调用BeanFactoryPostProcessor实现了Ordered. List orderedPostProcessors = new ArrayList(); for (String postProcessorName : orderedPostProcessorNames) { orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } // 按照order 进行排序 sortPostProcessors(beanFactory, orderedPostProcessors); invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory); //调用没有实现任何方法接口的 ,无序,直接调用 List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); for (String postProcessorName : nonOrderedPostProcessorNames) { nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory); beanFactory.clearMetadataCache(); }
根据下图的类关系图所示,因为PropertySoucesPlaceholderConfigurer类继承PropertyResourceConfigurer,并实现了PriorityOrdered,因此会将PropertySoucesPlaceholderConfigurer的Bean加入到priorityOrderedPostProcessors中,并,调用invokeBeanFactoryPostProcessors方法。
PostProcessorRegistrationDelegate.java
private static void invokeBeanFactoryPostProcessors( Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) { for (BeanFactoryPostProcessor postProcessor : postProcessors) { postProcessor.postProcessBeanFactory(beanFactory); } }
PropertySourcesPlaceholderConfigurer.java
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { if (this.propertySources == null) { this.propertySources = new MutablePropertySources(); if (this.environment != null) { this.propertySources.addLast( new PropertySource("environmentProperties", this.environment) { @Override public String getProperty(String key) { return this.source.getProperty(key); } } ); } try { PropertySource<?> localPropertySource = new PropertiesPropertySource("localProperties", mergeProperties()); if (this.localOverride) { this.propertySources.addFirst(localPropertySource); } else { this.propertySources.addLast(localPropertySource); } } catch (IOException ex) { throw new BeanInitializationException("Could not load properties", ex); } } processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources)); this.appliedPropertySources = this.propertySources; }
在postProcessBeanFactor方法中做了一个重要的事情 ,将PropertySoucesPlaceholderConfigurer的location 的资源属性转换成Property。
PropertiesLoaderSupport.java
/** * Return a merged Properties instance containing both the * loaded properties and properties set on this FactoryBean. */ protected Properties mergeProperties() throws IOException { Properties result = new Properties(); if (this.localOverride) { // Load properties from file upfront, to let local properties override. loadProperties(result); } if (this.localProperties != null) { for (Properties localProp : this.localProperties) { CollectionUtils.mergePropertiesIntoMap(localProp, result); } } if (!this.localOverride) { // Load properties from file afterwards, to let those properties override. loadProperties(result); } return result; }
加载属性资源
/** * Load properties into the given instance. * @param props the Properties instance to load into * @throws IOException in case of I/O errors * @see #setLocations */ protected void loadProperties(Properties props) throws IOException { if (this.locations != null) { for (Resource location : this.locations) { if (logger.isInfoEnabled()) { logger.info("Loading properties file from " + location); } try { PropertiesLoaderUtils.fillProperties( props, new EncodedResource(location, this.fileEncoding), this.propertiesPersister); } catch (IOException ex) { if (this.ignoreResourceNotFound) { if (logger.isWarnEnabled()) { logger.warn("Could not load properties from " + location + ": " + ex.getMessage()); } } else { throw ex; } } } } }
PropertiesLoaderUtils.java
static void fillProperties(Properties props, EncodedResource resource, PropertiesPersister persister) throws IOException { InputStream stream = null; Reader reader = null; try { String filename = resource.getResource().getFilename(); if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) { stream = resource.getInputStream(); persister.loadFromXml(props, stream); } else if (resource.requiresReader()) { reader = resource.getReader(); persister.load(props, reader); } else { stream = resource.getInputStream(); persister.load(props, stream); } } finally { if (stream != null) { stream.close(); } if (reader != null) { reader.close(); } } }
最终将config.local.propeties的属性加载为PropertySource对象,并存储到PropertySourcesPlaceholderConfigurer的propertySources属性中。
PropertySourcesPlaceholderConfigurer.java
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, final ConfigurablePropertyResolver propertyResolver) throws BeansException { propertyResolver.setPlaceholderPrefix(this.placeholderPrefix); propertyResolver.setPlaceholderSuffix(this.placeholderSuffix); propertyResolver.setValueSeparator(this.valueSeparator); StringValueResolver valueResolver = new StringValueResolver() { @Override public String resolveStringValue(String strVal) { String resolved = ignoreUnresolvablePlaceholders ? propertyResolver.resolvePlaceholders(strVal) : propertyResolver.resolveRequiredPlaceholders(strVal); return (resolved.equals(nullValue) ? null : resolved); } }; doProcessProperties(beanFactoryToProcess, valueResolver); }
PlaceholderConfigurerSupport.java
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess, StringValueResolver valueResolver) { BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver); String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames(); //对当前注册的所有的BeanDefinition遍历 for (String curName : beanNames) { //排除掉自己 if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) { BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName); try { //访问当前beanName的BeanDefinition visitor.visitBeanDefinition(bd); } catch (Exception ex) { throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex); } } } // New in Spring 2.5: resolve placeholders in alias target names and aliases as well. beanFactoryToProcess.resolveAliases(valueResolver); // New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes. beanFactoryToProcess.addEmbeddedValueResolver(valueResolver); }
BeanDefinitionVisitor.java
public void visitBeanDefinition(BeanDefinition beanDefinition) { visitParentName(beanDefinition); visitBeanClassName(beanDefinition); visitFactoryBeanName(beanDefinition); visitFactoryMethodName(beanDefinition); visitScope(beanDefinition); visitPropertyValues(beanDefinition.getPropertyValues()); ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues(); visitIndexedArgumentValues(cas.getIndexedArgumentValues()); visitGenericArgumentValues(cas.getGenericArgumentValues()); }
BeanDefinitionVisitor.java
protected void visitPropertyValues(MutablePropertyValues pvs) { PropertyValue[] pvArray = pvs.getPropertyValues(); for (PropertyValue pv : pvArray) { // 对第一个beanDefinition的属性值进行遍历 Object newVal = resolveValue(pv.getValue()); if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) { pvs.add(pv.getName(), newVal); } } }
BeanDefinitionVisitor.java
protected Object resolveValue(Object value) { //如果属性值是BeanDefinition,递归访问 visitBeanDefinition方法 if (value instanceof BeanDefinition) { visitBeanDefinition((BeanDefinition) value); } //如果是BeanDefinitionHolder,取出BeanDefinition ,递归访问visitBeanDefinition else if (value instanceof BeanDefinitionHolder) { visitBeanDefinition(((BeanDefinitionHolder) value).getBeanDefinition()); } else if (value instanceof RuntimeBeanReference) { RuntimeBeanReference ref = (RuntimeBeanReference) value; String newBeanName = resolveStringValue(ref.getBeanName()); //如果是一个引用对象,则取出引用beanName,重新解析value ,再重新封装RuntimeBeanReference if (!newBeanName.equals(ref.getBeanName())) { return new RuntimeBeanReference(newBeanName); } } else if (value instanceof RuntimeBeanNameReference) { RuntimeBeanNameReference ref = (RuntimeBeanNameReference) value; String newBeanName = resolveStringValue(ref.getBeanName()); if (!newBeanName.equals(ref.getBeanName())) { return new RuntimeBeanNameReference(newBeanName); } } else if (value instanceof Object[]) { visitArray((Object[]) value); } else if (value instanceof List) { visitList((List) value); } else if (value instanceof Set) { visitSet((Set) value); } else if (value instanceof Map) { visitMap((Map) value); } else if (value instanceof TypedStringValue) { TypedStringValue typedStringValue = (TypedStringValue) value; String stringValue = typedStringValue.getValue(); if (stringValue != null) { String visitedString = resolveStringValue(stringValue); typedStringValue.setValue(visitedString); } } else if (value instanceof String) { return resolveStringValue((String) value); } return value; }
BeanDefinitionVisitor.java
protected String resolveStringValue(String strVal) { if (this.valueResolver == null) { throw new IllegalStateException("No StringValueResolver specified - pass a resolver " + "object into the constructor or override the 'resolveStringValue' method"); } String resolvedValue = this.valueResolver.resolveStringValue(strVal); // Return original String if not modified. return (strVal.equals(resolvedValue) ? strVal : resolvedValue); }
PropertySourcesPlaceholderConfigurer.java
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, final ConfigurablePropertyResolver propertyResolver) throws BeansException { propertyResolver.setPlaceholderPrefix(this.placeholderPrefix); propertyResolver.setPlaceholderSuffix(this.placeholderSuffix); propertyResolver.setValueSeparator(this.valueSeparator); StringValueResolver valueResolver = new StringValueResolver() { @Override public String resolveStringValue(String strVal) { String resolved = ignoreUnresolvablePlaceholders ? propertyResolver.resolvePlaceholders(strVal) : propertyResolver.resolveRequiredPlaceholders(strVal); return (resolved.equals(nullValue) ? null : resolved); } }; doProcessProperties(beanFactoryToProcess, valueResolver); }
AbstractPropertyResolver.java
@Override public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException { if (this.strictHelper == null) { this.strictHelper = createPlaceholderHelper(false); } return doResolvePlaceholders(text, this.strictHelper); } private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) { return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() { @Override public String resolvePlaceholder(String placeholderName) { return getPropertyAsRawString(placeholderName); } }); }
最终调用了getProperty获得了username属性。
总结 : 到这里己经完成了对线上线下环境区分解析,下面我们来总结一下。
1.首先Spring解析元素时,看有没有profile属性,如果有,则去环境变量中查找spring.profile.active属性是否存在,如果存在,则和当前profile配置的属性值比对,相等,则继续解析,如果spring.profile.active属性不存在,则继续从环境变量中查找spring.profile.default值,存在,使用默认值。
2.解析property-placeholder标签,标签中存在property-placeholder时,生成一个PropertySourcesPlaceholderConfigurer的BeanDefinition,保存到beanDefinitionMap中。
3.将其他配置的bean解析成BeanDefinition
4.调用PropertySourcesPlaceholderConfigurer的postProcessBeanFactor方法,将location属性转换成PropertiesPropertySource存储于PropertySourcesPlaceholderConfigurer的propertySources属性中。
5.遍历容器下所有BeanDefinition,将所有以 ${} 包裹的属性值,用存储于 propertySources 属性替换掉,找不到抛出BeanDefinitionStoreException异常。
6.到此己实现了不同启动参数,加载不同的属性文件。