https://github.com/seaswalker/Spring
使用版本spring4.2.2或者4.1.1
基本
本部分从最基本的Spring开始。配置文件:
<?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-3.0.xsd">
<bean class="base.SimpleBean"></bean>
</beans>
###原作者没有写beans的命名空间及约束 导致xml验证失败,这里加上了
###bean部分就是类的引用地址
启动代码:
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) {
//null
super(parent);
setConfigLocations(configLocations);
//默认true
if (refresh) {
refresh();
}
}ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml"); 调用只会是上述两个方法其中一个,这里明显是ClassPathXmlApplicationContext(String configLocation)
后面实际调用的都是本类的这个方法,如下图:
其实ClassPathXmlApplicationContext类里面的构造器(包括无参的共有9个在这个版本)。
构造器
。。。。。。此处省略几个向上的父类,一直到底抽象类 AbstractApplicationContext
首先看父类构造器,沿着继承体系一直向上调用,直到AbstractApplicationContext:
public AbstractApplicationContext(ApplicationContext parent) {
this();
setParent(parent);
}
public AbstractApplicationContext() {
this.resourcePatternResolver \= getResourcePatternResolver();
}
PathMatchingResourcePatternResolver支持Ant风格的路径解析。下面简单概述下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();//看下面的继承关系可以知道StandardEnvironment是ConfigurableEnvironment的实现类
}
Environment接口
继承体系:
Environmen接口**代表了当前应用所处的环境。**从此接口的方法可以看出,其主要和profile、Property相关。
Profile
Spring Profile特性是从3.1开始的,其主要是为了解决这样一种问题: 线上环境和测试环境使用不同的配置或是数据库或是其它。有了Profile便可以在 不同环境之间无缝切换。**Spring容器管理的所有bean都是和一个profile绑定在一起的。**使用了Profile的配置文件示例:
在启动代码中可以用如下代码设置活跃(当前使用的)Profile:
context.getEnvironment().setActiveProfiles("dev");
当然使用的方式还有很多(比如注解),参考:
spring3.1 profile 配置不同的环境(打开失败)
Property
这里的Property指的是程序运行时的一些参数,引用注释:
properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects,Maps, and so on.
Environment实现类 AbstractEnvironment ****构造器
private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
PropertySources接口
继承体系:
此接口实际上是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:(此方法在org.springframework.core.env.AbstractEnvironment里面)
image@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分支):
路径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分支):
image此接口正是用来解析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前缀到目前为止还没有被处理。
refresh
super(parent)和this.setConfigLocations(configLocations);说完 下面说是refresh这个方法
Spring bean解析就在此方法,所以单独提出来。
AbstractApplicationContext.refresh:
作者版本:
@Overridepublic void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
} catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
prepareRefresh
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
// Initialize any placeholder property sources in the context environment
//空实现
initPropertySources();
// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}
属性校验
AbstractEnvironment.validateRequiredProperties:
@Overridepublic void validateRequiredProperties() throws MissingRequiredPropertiesException {
this.propertyResolver.validateRequiredProperties();
}
AbstractPropertyResolver.validateRequiredProperties:
@Overridepublic void validateRequiredProperties() {
MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
for (String key : this.requiredProperties) {
if (this.getProperty(key) \== null) {
ex.addMissingRequiredProperty(key);
}
}
if (! ex.getMissingRequiredProperties().isEmpty()) {
throw ex;
}
}
requiredProperties是通过setRequiredProperties方法设置的,保存在一个set里面,默认是空的,也就是不需要校验任何属性。
作者说是list集合里,实际是错的,当然也许是版本不同而已。BeanFactory创建
在refresh的这个方法里面
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();//下面会讲到这个
this.prepareBeanFactory(beanFactory);
由obtainFreshBeanFactory调用AbstractRefreshableApplicationContext.refreshBeanFactory:(见下图)
(这部分与作者源码一致)
BeanFactory接口
此接口实际上就是Bean容器,其继承体系:
BeanFactory定制
AbstractRefreshableApplicationContext.customizeBeanFactory方法用于给子类提供一个自由配置的机会,默认实现:
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
//默认false,不允许覆盖
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
//默认false,不允许循环引用
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
Bean加载
AbstractXmlApplicationContext.loadBeanDefinitions,这个便是核心的bean加载了:
@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
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读取器设置SAX xml解析器,****下面会说到这个 **
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
//默认空实现
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
EntityResolver
此处只说明用到的部分继承体系:
EntityResolver接口在org.xml.sax中定义。DelegatingEntityResolver用于schema和dtd的解析。
BeanDefinitionReader
继承体系:
路径解析(Ant)
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
//here
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
AbstractBeanDefinitionReader.loadBeanDefinitions:
下面作者这个肯定是不对的,因为调用了方法一样但参数列表不一样,实际是重载的loadBeanDefinitions方法 ,上图两个是4.1.1的方法内容,与作者的版本不同。故贴出来。
4.1.1版本与这个稍微有些不同
@Overridepublic int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int counter = 0;
for (String location : locations) {
counter += loadBeanDefinitions(location);
}
return counter;
}
之后调用:
//第二个参数为空
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = this.getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException("Cannot import bean definitions from location \[" + location + "\]: no ResourceLoader available");
} else {
int loadCount;
if (!(resourceLoader instanceof ResourcePatternResolver)) {
Resource resource = **resourceLoader.getResource**(location);
loadCount = this.loadBeanDefinitions((Resource)resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Loaded " + loadCount + " bean definitions from location \[" + location + "\]");
}
return loadCount;
} else {
try {
Resource\[\] resources = ((ResourcePatternResolver)resourceLoader).**getResources**(location);
loadCount = this.loadBeanDefinitions(resources);
if (actualResources != null) {
Resource\[\] var6 = resources;
int var7 = resources.length;
for(int var8 = 0; var8 < var7; ++var8) {
Resource resource = var6\[var8\];
actualResources.add(resource);
}
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Loaded " + loadCount + " bean definitions from location pattern \[" + location + "\]");
}
return loadCount;
} catch (IOException var10) {
throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern \[" + location + "\]", var10);
}
}
}
}
getResources(作者写成getResource,一字之差缪之千里),在AbstractApplicationContext.getResources:
@Override
public Resource[] getResources(String locationPattern) throws IOException {
//构造器中初始化,PathMatchingResourcePatternResolver对象
return this.resourcePatternResolver.getResources(locationPattern);
}
进入第二个方法,因为第一个是掉本类的方法。
public Resource[] getResources(String locationPattern) throws IOException {
//如果是ResourcePatternResolver
return this.resourceLoader instanceof ResourcePatternResolver ? ((ResourcePatternResolver)this.resourceLoader).getResources(locationPattern) : super.getResources(locationPattern);
}
PathMatchingResourcePatternResolver是ResourceLoader继承体系的一部分。
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
if (locationPattern.startsWith("classpath*:")) {
//matcher是一个AntPathMatcher对象
return this.getPathMatcher().isPattern(locationPattern.substring("classpath\*:".length())) ? this.findPathMatchingResources(locationPattern) : this.findAllClassPathResources(locationPattern.substring("classpath\*:".length()));
} else {
int prefixEnd = locationPattern.indexOf(":") + 1;
return this.getPathMatcher().**isPattern**(locationPattern.substring(prefixEnd)) ? this.findPathMatchingResources(locationPattern) : new Resource\[\]
{this.getResourceLoader().getResource(locationPattern)};
}
}
isPattern:
注:本来以为是数字 ,后面才发现ASCII代表的数字:详情可查https://baike.baidu.com/item/ASCII/309296?fr=aladdin
十进制指的是42
也经过测试发现是根据十进制的ASCII码参与运算的。
也就是可以这么写:
new ClassPathXmlApplicationContext("con*.xml");
更多的例子
具体怎么解析ant风格的就不写了。
配置文件加载
入口方法在AbstractBeanDefinitionReader:
//加载Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//解析int loadCount = loadBeanDefinitions(resources);
最终逐个调用XmlBeanDefinitionReader的loadBeanDefinitions方法:
Resource是代表一种资源的接口,其类图:
EncodedResource扮演的其实是一个装饰器的模式,为InputStreamSource添加了字符编码(虽然默认为null)。这样为我们自定义xml配置文件的编码方式提供了机会。
之后关键的源码只有两三行:(这个是4.1.1版本中的源码,关键部分为标红的地方)
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (this.logger.isInfoEnabled()) {
this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!((Set)currentResources).add(encodedResource)) {
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
} else {
int var5;
try {
**InputStream inputStream = encodedResource.getResource().getInputStream()****;**
try {
**InputSource inputSource =** **new** **InputSource(inputStream)****;**
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
**var5 =** **this****.doLoadBeanDefinitions(inputSource****,** **encodedResource.getResource())****;**
} finally {
inputStream.close();
}
} catch (IOException var15) {
throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
} finally {
((Set)currentResources).remove(encodedResource);
if (((Set)currentResources).isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
return var5;
}
}
其实也差不多。
InputSource是org.xml.sax的类。
doLoadBeanDefinitions:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) {
Document doc = doLoadDocument(inputSource, resource);//下面先讲讲这部分 加载document
return registerBeanDefinitions(doc, resource);//在说说这部分 主要是注册bean
}(4.1.1版本有些不同,主要在抛出异常方面)
()doLoadDocument:
protected Document doLoadDocument(InputSource inputSource, Resource resource) {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
//loadDocument方法实际调用的是****DefaultDocumentLoader里面****的****loadDocument方法 #DefaultDocumentLoader.loadDocument********
}
实际这个documentLoader就是DefaultDocumentLoader. 此类是DocumentLoader接口的唯一实现。getEntityResolver方法返回ResourceEntityResolver,上面说过了。errorHandler是一个SimpleSaxErrorHandler对象。
校验模型其实就是确定xml文件使用xsd方式还是dtd方式来校验,忘了的话左转度娘。Spring会通过读取xml文件的方式判断应该采用哪种。
NamespaceAware默认false,因为默认配置了校验为true。(其实这个默认校验为true我还没找到)
DefaultDocumentLoader.loadDocument:
@Overridepublic Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) {
//这里就是老套路了,可以看出,Spring还是使用了dom的方式解析,即一次全部load到内存
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
createDocumentBuilderFactory比较有意思:
如上图所示,4.1.1版本的是这样,我跟确信下面的版本更成熟,因为很多地方是常量控制,4.1.1都是写死的常量。
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware{
DocumentBuilderFactory factory \= DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(namespaceAware);
if (validationMode != XmlValidationModeDetector.VALIDATION\_NONE) {
//此方法设为true仅对dtd有效,xsd(schema)无效
factory.setValidating(true);
if (validationMode \== XmlValidationModeDetector.VALIDATION\_XSD) {
// Enforce namespace aware for XSD...
//开启xsd(schema)支持
factory.setNamespaceAware(true);
//这个也是Java支持Schema的套路,可以问度娘
factory.setAttribute(SCHEMA\_LANGUAGE\_ATTRIBUTE, XSD\_SCHEMA\_LANGUAGE);
}
}
return factory;
}
Bean解析(这部分放在 spring源码解析---spring-core(二))
本文来源于:宋文超super,专属平台有csdn、思否(SegmentFault)、 简书、 开源中国(oschina),转载请注明出处。
网友评论