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);
}
Spring源码解读(一)的更多相关文章
- Spring源码解读之BeanFactoryPostProcessor的处理
前言 前段时间旁听了某课堂两节Spring源码解析课,刚好最近自己又在重新学习中,便在这里记录一下学习所得.我之前写过一篇博文,是介绍BeanFactoryPostProcessor跟BeanPost ...
- Spring源码解读--(一)源码下载
走在Java程序员这条路上,网上Java各种工具满天飞,写个简单的CRUD,相信是个开发都能写出来,于是在思考如何可以在同行业中更有竞争力(其实就是如何赚更多钱).那么,老大给我推荐了Spring源码 ...
- 【Spring源码解读】bean标签中的属性
说明 今天在阅读Spring源码的时候,发现在加载xml中的bean时,解析了很多标签,其中有常用的如:scope.autowire.lazy-init.init-method.destroy-met ...
- Spring源码解读:核心类DefaultListableBeanFactory的继承体系
1 简介 我们常用的ClassPathXmlApplicationContext是AbstractRefreshableApplicationContext的子类,而DefaultListableBe ...
- Spring源码解读Spring IOC原理
一.什么是Ioc/DI? IoC 容器:最主要是完成了完成对象的创建和依赖的管理注入等等. 先从我们自己设计这样一个视角来考虑: 所谓控制反转,就是把原先我们代码里面需要实现的对象创建.依赖的代码,反 ...
- spring源码解读之 JdbcTemplate源码
原文:https://blog.csdn.net/songjinbin/article/details/19857567 在Spring中,JdbcTemplate是经常被使用的类来帮助用户程序操作数 ...
- 《spring源码解读》 - IoC 之解析 import 标签
在上一文中我们分析了注册 BeanDefinition 的过程,在其中我们了解到在解析跟节点和子节点时分两种情况,对于默认名称空间的标签我们通过 DefaultBeanDefinitionDocume ...
- Spring源码解读(一):Spring的背景起源及框架整体介绍
一.前言 Spring起源于2002年Rod Johnson写的一本书<Expert One-on-One J2EE>,书里介绍了Java企业应用程序开发情况,并指出Java EE和EJB ...
- Spring 源码解读 推荐流程
Spring源代码解析(一):IOC容器:http://www.javaeye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的启动:http://www.ja ...
- 【Spring源码解读】bean标签中的属性(二)你可能还不够了解的 abstract 属性和 parent 属性
abstract 属性说明 abstract 在java的语义里是代表抽象的意思,用来说明被修饰的类是抽象类.在Spring中bean标签里的 abstract 的含义其实也差不多,表示当前bean是 ...
随机推荐
- 计算1-9总共九个数字可以满足abc+def=hij这样的式子
计算1-9总共九个数字可以满足abc+def=hij这样的式子:其中abcdefghij九个数字各个都不相同,它们都属于1-9个数字中: 首先,第一种方法很简单很暴力,直接枚举,这样的话时间复杂度高: ...
- Java Netty和Android之WebSocket,Springboot和Vue项目网址
在Netty上使用Websocket和网页上写个简单的websocket https://www.cnblogs.com/amibandoufu/p/11442881.html Android上使用w ...
- 学习笔记:CentOS7学习之二十三: 跳出循环-shift参数左移-函数的使用
目录 学习笔记:CentOS7学习之二十三: 跳出循环-shift参数左移-函数的使用 23.1 跳出循环 23.1.1 break和continue 23.2 Shift参数左移指令 23.3 函数 ...
- HttpServletRequest对象(转)
HttpServletRequest介绍 HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供 ...
- 测试常用__linux命令
1.显示目录和文件的命令 Ls:用于查看所有文件夹的命令. Dir:用于显示指定文件夹和目录的命令 Tree: 以树状图列出目录内容 Du:显示目录或文件大小 2.修改目录,文件权限和属主及数组命令 ...
- 4.Linux系统命令及其使用详解
运维工程师必会的109个Linux命令 文件管理basename:从文件名中去掉路径和扩展名 cat:把档案串连接后传到基本输出(屏幕或加 > filename 到另一个档案)cd:切换目录 ...
- Java并发与多线程教程(2)
Java同步块 Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) ...
- codeblocks 使用汇总
codeblocks 使用汇总 http://www.cnblogs.com/-clq/archive/2012/01/31/2333247.html
- js组合继承
//组合继承指的是将原型链和借用构造函数(call.apply)的技术组合到一起,从而发挥二者之长的一种继承模式,//其背后的思路就是使用原型链实现对原型属性和方法的继承://而通过借用构造函数来实现 ...
- javaIO——AutoCloseable 小试
前面在 IO 概述篇提到过,AutoCloseable 接口类会自动调用 close() 方法,那究竟具体怎么写呢?以及发生异常情况下或者多个资源是不是都能自动调用呢?我们来写一个简单的类测试一下就知 ...