1. spring5.0源码讲解-bean的加载过程|一

#spring-core源码解析-(1)
- 基本
- ClassPathXmlApplicationContext
- 构造器
- 设置配置文件路径
- Environment接口
- Profile
- Property
- Environment构造器
- PropertySources接口
- PropertySource接口
- 路径Placeholder处理
- PropertyResolver接口
- 解析
- Environment接口
- ClassPathXmlApplicationContext
基础
本部分从最基本的Spring开始。配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean class="base.SimpleBean"></bean></beans>
启动代码:
public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml"); SimpleBean bean = context.getBean(SimpleBean.class); bean.send(); context.close();}
SimpleBean:
public class SimpleBean { public void send() { System.out.println("I am send method from SimpleBean!"); }}
ClassPathXmlApplicationContext
整个继承体系如下:
ResourceLoader代表了加载资源的一种方式,正是策略模式的实现。
构造器源码:
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) { super(parent); setConfigLocations(configLocations); //默认true if (refresh) { refresh(); }}
构造器
首先看父类构造器,沿着继承体系一直向上调用,直到AbstractApplicationContext:
public AbstractApplicationContext(ApplicationContext parent) { this(); setParent(parent);}public AbstractApplicationContext() { this.resourcePatternResolver = getResourcePatternResolver();}
getResourcePatternResolver:
protected ResourcePatternResolver getResourcePatternResolver() { return new PathMatchingResourcePatternResolver(this);}
PathMatchingResourcePatternResolver支持Ant风格的路径解析。
设置配置文件路径
即AbstractRefreshableConfigApplicationContext.setConfigLocations:
public void setConfigLocations(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] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; }}
resolvePath:
protected String resolvePath(String path) { return getEnvironment().resolveRequiredPlaceholders(path);}
此方法的目的在于将占位符(placeholder)解析成实际的地址。比如可以这么写: new ClassPathXmlApplicationContext("classpath:config.xml");
那么classpath:就是需要被解析的。
getEnvironment方法来自于ConfigurableApplicationContext接口,源码很简单,如果为空就调用createEnvironment创建一个。AbstractApplicationContext.createEnvironment:
protected ConfigurableEnvironment createEnvironment() { return new StandardEnvironment();}
Environment接口
StandardEnvironment继承体系:
Environmen接口**代表了当前应用所处的环境。**从此接口的方法可以看出,其主要和profile、Property相关。
Profile
Spring Profile特性是从3.1开始的,其主要是为了解决这样一种问题: 线上环境和测试环境使用不同的配置或是数据库或是其它。有了Profile便可以在 不同环境之间无缝切换。**Spring容器管理的所有bean都是和一个profile绑定在一起的。**使用了Profile的配置文件示例:
<beans profile="develop"> <context:property-placeholder location="classpath*:jdbc-develop.properties"/> </beans> <beans profile="production"> <context:property-placeholder location="classpath*:jdbc-production.properties"/> </beans> <beans profile="test"> <context:property-placeholder location="classpath*:jdbc-test.properties"/> </beans>
在启动代码中可以用如下代码设置活跃(当前使用的)Profile:
context.getEnvironment().setActiveProfiles("dev");
当然使用的方式还有很多(比如注解),参考:
spring3.1 profile 配置不同的环境
Spring Profiles example
Property
这里的Property指的是程序运行时的一些参数,引用注释:
properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects,Maps, and so on.
Environment构造器
private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);public AbstractEnvironment() { customizePropertySources(this.propertySources);}
PropertySources接口
继承体系:
此接口实际上是PropertySource的容器,默认的MutablePropertySources实现内部含有一个CopyOnWriteArrayList作为存储载体。
StandardEnvironment.customizePropertySources:此接口实际上是PropertySource的容器,默认的MutablePropertySources实现内部含有一个CopyOnWriteArrayList作为存储载体。
StandardEnvironment.customizePropertySources:
/** System environment property source name: {@value} */public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";/** JVM system properties property source name: {@value} */public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";@Overrideprotected void customizePropertySources(MutablePropertySources propertySources) { propertySources.addLast(new MapPropertySource (SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); propertySources.addLast(new SystemEnvironmentPropertySource (SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));}
PropertySource接口
PropertySource接口代表了键值对的Property来源。继承体系:
AbstractEnvironment.getSystemProperties:
@Overridepublic Map<String, Object> getSystemProperties() { try { return (Map) System.getProperties(); } catch (AccessControlException ex) { return (Map) new ReadOnlySystemAttributesMap() { @Override protected String getSystemAttribute(String attributeName) { try { return System.getProperty(attributeName); } catch (AccessControlException ex) { if (logger.isInfoEnabled()) { logger.info(format("Caught AccessControlException when accessing system " + "property [%s]; its value will be returned [null]. Reason: %s", attributeName, ex.getMessage())); } return null; } } }; }}
这里的实现很有意思,如果安全管理器阻止获取全部的系统属性,那么会尝试获取单个属性的可能性,如果还不行就抛异常了。
getSystemEnvironment方法也是一个套路,不过最终调用的是System.getenv,可以获取jvm和OS的一些版本信息。
路径Placeholder处理
AbstractEnvironment.resolveRequiredPlaceholders:
@Overridepublic String resolveRequiredPlaceholders(String text) throws IllegalArgumentException { //text即配置文件路径,比如classpath:config.xml return this.propertyResolver.resolveRequiredPlaceholders(text);}
propertyResolver是一个PropertySourcesPropertyResolver对象:
private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
PropertyResolver接口
PropertyResolver继承体系(排除Environment分支):
此接口正是用来解析PropertyResource。
解析
AbstractPropertyResolver.resolveRequiredPlaceholders:
@Overridepublic String resolveRequiredPlaceholders(String text) throws IllegalArgumentException { if (this.strictHelper == null) { this.strictHelper = createPlaceholderHelper(false); } return doResolvePlaceholders(text, this.strictHelper);}
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) { //三个参数分别是${, }, : return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix, this.valueSeparator, ignoreUnresolvablePlaceholders);}
doResolvePlaceholders:
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) { //PlaceholderResolver接口依然是策略模式的体现 return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() { @Override public String resolvePlaceholder(String placeholderName) { return getPropertyAsRawString(placeholderName); } });}
其实代码执行到这里的时候还没有进行xml配置文件的解析,那么这里的解析placeHolder是什么意思呢,原因在于可以这么写:
System.setProperty("spring", "classpath");ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("${spring}:config.xml");SimpleBean bean = context.getBean(SimpleBean.class);
这样就可以正确解析。placeholder的替换其实就是字符串操作,这里只说一下正确的属性是怎么来的。实现的关键在于PropertySourcesPropertyResolver.getProperty:
@Overrideprotected String getPropertyAsRawString(String key) { return getProperty(key, String.class, false);}protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) { if (this.propertySources != null) { for (PropertySource<?> propertySource : this.propertySources) { Object value = propertySource.getProperty(key); return value; } } return null;}
很明显了,就是从System.getProperty和System.getenv获取,但是由于环境变量是无法自定义的,所以其实此处只能通过System.setProperty指定。
注意,classpath:XXX这种写法的classpath前缀到目前为止还没有被处理。
关注公众号: 太上老君007
spring源码分析第一时间通知你
相关内容
公共CPU接口类型的详细描述
公共CPU接口类型的详细描述,,我们知道CPU是电脑的大脑, CPU的...
如何解决errtor加载操作系统的引导
如何解决errtor加载操作系统的引导,,电脑启动后,屏幕上出现了er...
电脑显卡接口红色和黑色|显卡接口发黑
电脑显卡接口红色和黑色|显卡接口发黑,,显卡接口发黑试试吃块...
FM1和AM3接口将在今年年底前淘汰
FM1和AM3接口将在今年年底前淘汰,,据来自主板制造商的消息,AMD...
电脑主板四个硬盘接口推荐|主板上的硬盘的接口
电脑主板四个硬盘接口推荐|主板上的硬盘的接口有哪几种,,主板...
联想笔记本电脑主板颜色|联想笔记本颜色配置文
联想笔记本电脑主板颜色|联想笔记本颜色配置文件,,联想笔记本...
将XP系统加载到虚拟硬盘并启动计算机
将XP系统加载到虚拟硬盘并启动计算机,,注释:您可以将XP系统加载...
Windows8开发版系统高清系统接口图
Windows8开发版系统高清系统接口图,,今天,微软发布,目前由开发者...
小米电脑无网线口|小米笔记本没有网线接口怎么
小米电脑无网线口|小米笔记本没有网线接口怎么插网线,,小米笔...
关闭wps加载项|Word开启和关闭加载项,启用被禁
关闭wps加载项|Word开启和关闭加载项,启用被禁用的项,启用被...
「显卡上的接口」显卡上的接口有几种
「显卡上的接口」显卡上的接口有几种,显卡上的接口,本篇文章给...
电脑显示器联接方法图片|电脑连接显示器接口有
电脑显示器联接方法图片|电脑连接显示器接口有几种图,,1. 电脑...
Win7旗舰版系统中u盘加载缓慢怎么办?|win7电脑打
Win7旗舰版系统中u盘加载缓慢怎么办?|win7电脑打开u盘缓慢怎...
支持DDR3!AM3接口790gx主板的评价
支持DDR3!AM3接口790gx主板的评价,,二月,在主板市场,各种各样的AM...
电脑显示器接口漏电|显示器电源线漏电
电脑显示器接口漏电|显示器电源线漏电,,显示器电源线漏电显示...