Spring源码解读(一)
前期准备
首先搭建一个简单的Spring Demo工程
项目目录结构如下图所示:
applicationContect.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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean id="chinese" class="com.bean.ChineseImpl">
<property name="name">
<value>小明</value>
</property>
<property name="age">
<value>10</value>
</property>
</bean>
<bean id="american" class="com.bean.AmericanImpl">
<property name="name">
<value>Tom</value>
</property>
<property name="age">
<value>15</value>
</property>
</bean>
</beans>
Person.java
package com.bean; public interface Person{ public void Speak();
}
ChineseImpl.java
package com.bean; public class ChineseImpl implements Person{
private String name;
private int age; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public void Speak() {
// TODO Auto-generated method stub
System.out.println("I'm Chinese,My name is "+this.name+",I'm "+this.age+" years old!");
}
}
AmericanImpl.java
package com.bean; public class AmericanImpl implements Person{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public void Speak() {
// TODO Auto-generated method stub
System.out.println("I'm American,My name is "+this.name+",I'm "+this.age+" years old!");
}
}
Test.java
package com.spring; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.bean.Person; public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = (Person) context.getBean("chinese");
person.Speak();
person = (Person) context.getBean("american");
person.Speak();
}
}
下面将按照Spring初始化的过程:
构造函数
ClassPathXmlApplicationContext构造函数(ClassPathXmlApplicationContext[只能读放在web-info/classes目录下的配置文件],FileSystemXmlApplicationContext读具体路径)
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
根据传入参数的不同,调用不同的构造函数,最终调用以下构造函数
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException { super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
设置配置文件路径
即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:就是需要被解析的
refesh()
Spring bean解析就在此方法,所以单独提出来。
AbstractApplicationContext.refresh:
@Override
public 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) {
logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex); // Destroy already created singletons to avoid dangling resources.
destroyBeans(); // Reset 'active' flag.
cancelRefresh(ex); // Propagate exception to caller.
throw ex;
}
}
}
prepareRefresh
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.active.set(true); if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
} // Initialize any placeholder property sources in the context environment
initPropertySources(); // Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
}
属性校验
AbstractEnvironment.validateRequiredProperties:
@Override
public void validateRequiredProperties() throws MissingRequiredPropertiesException {
this.propertyResolver.validateRequiredProperties();
}
AbstractPropertyResolver.validateRequiredProperties:
@Override
public 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方法设置的,保存在一个list里面,默认是空的,也就是不需要校验任何属性
BeanFactory创建
由 refesh() 中的 obtainFreshBeanFactory 调用 AbstractRefreshableApplicationContext.refreshBeanFactory:
@Override
protected final void refreshBeanFactory() throws BeansException {
//如果存在就销毁
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
BeanFactory定制
AbstractRefreshableApplicationContext.customizeBeanFactory方法用于给子类提供一个自由配置的机会,默认实现:
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
Bean加载
AbstractXmlApplicationContext.loadBeanDefinitions,这个便是核心的bean加载了:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 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)); // Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
最新文章
- 文件上传之——用SWF插件实现文件异步上传和头像截取
- 千万用户级别应用系统背后的SOA组件化容器
- try : finally语句
- P2312 解方程
- WCF服务中,[DataMember]属性标记的属性一定要有set访问器
- curl 报错记录,mark
- android sdk manager 闪退 打不开问题
- (转载)mysql查询一天,查询一周,查询一个月的数据
- java mail 使用 gmail smtp 发送邮件
- BootStrap 智能表单系列 六 表单数据绑定(编辑页面的数据绑定)
- postcss的安装与使用
- xgboost 参数调优指南
- [Solution] JZOJ-5806 简单的操作
- 协同过滤CF算法之入门
- Spring中的IoC(控制反转)具体是什么东西
- 【多视图几何】TUM 课程 第4章 同名点匹配
- bzoj 4242 水壶 (多源最短路+最小生成树+启发式合并)
- [Functional Programming] Build a Linear congruential generator
- pl/sql登录时,数据库下拉框没有任何内容
- [CF1042D] Petya and Array