Spring源码阅读目录
第一部分——IOC篇
第一章 Spring之最熟悉的陌生人——IOC
第二章 Spring之假如让你来写IOC容器——加载资源篇
第三章 Spring之假如让你来写IOC容器——解析配置文件篇
第四章 Spring之假如让你来写IOC容器——XML配置文件篇
第五章 Spring之假如让你来写IOC容器——BeanFactory和FactoryBean
第六章 Spring之假如让你来写IOC容器——Scope和属性填充
第七章 Spring之假如让你来写IOC容器——属性填充特别篇:SpEL表达式
第八章 Spring之假如让你来写IOC容器——拓展篇
第九章 Spring之源码阅读——环境搭建篇
第十章 Spring之源码阅读——IOC篇
第二部分——AOP篇
第十一章 Spring之不太熟的熟人——AOP
第十二章 Spring之不得不了解的内容——概念篇
第十三章 Spring之假如让你来写AOP——AOP联盟篇
第十四章 Spring之假如让你来写AOP——雏形篇
第十五章 Spring之假如让你来写AOP——Joinpoint(连接点)篇
第十六章 Spring之假如让你来写AOP——Pointcut(切点)篇
第十七章 Spring之假如让你来写AOP——Advice(通知)上篇
第十八章 Spring之假如让你来写AOP——Advice(通知)下篇
第十九章 Spring之假如让你来写AOP——番外篇:Spring早期设计
第二十章 Spring之假如让你来写AOP——Aspect(切面)篇
第二十一章 Spring之假如让你来写AOP——Weaver(织入器)篇
第二十二章 Spring之假如让你来写AOP——Target Object(目标对象)篇
第二十三章 Spring之假如让你来写AOP——融入IOC容器篇
第二十四章 Spring之源码阅读——AOP篇
第三部分——事务篇
第二十五章 Spring之曾经的老朋友——事务
第二十六章 Spring之假如让你来写事务——初稿篇
第二十七章 Spring之假如让你来写事务——铁三角篇
第二十八章 Spring之假如让你来写事务——属性篇
第二十九章 Spring之假如让你来写事务——状态篇
第三十章 Spring之假如让你来写事务——管理篇
第三十一章 Spring之假如让你来写事务——融入IOC容器篇
第三十二章 Spring之源码阅读——事务篇
第四部分——MVC篇
第三十三章 Spring之梦开始的地方——MVC
第三十四章 Spring之假如让你来写MVC——草图篇
第三十五章 Spring之假如让你来写MVC——映射器篇
第三十六章 Spring之假如让你来写MVC——拦截器篇
第三十七章 Spring之假如让你来写MVC——控制器篇
第三十八章 Spring之假如让你来写MVC——适配器篇
第三十九章 Spring之假如让你来写MVC——番外篇:类型转换
第四十章 Spring之假如让你来写MVC——ModelAndView篇
第四十一章 Spring之假如让你来写MVC——番外篇:数据绑定
第四十二章 Spring之假如让你来写MVC——视图篇
第四十三章 Spring之假如让你来写MVC——上传文件篇
第四十四章 Spring之假如让你来写MVC——异常处理器篇
第四十五章 Spring之假如让你来写MVC——国际化篇
第四十六章 Spring之假如让你来写MVC——主题解析器篇
第四十七章 Spring之假如让你来写MVC——闪存管理器篇
第四十八章 Spring之假如让你来写MVC——请求映射视图篇
第四十九章 Spring之假如让你来写MVC——番外篇:属性操作
第五十章 Spring之假如让你来写MVC——融入IOC容器篇
第五十一章 Spring之源码阅读——MVC篇
第五部分——Boot篇
第五十二章 Spring之再进一步——Boot
第五十三章 Spring之假如让你来写Boot——环境篇
第五十四章 Spring之假如让你来写Boot——注解篇(上)
第五十五章 Spring之假如让你来写Boot——注解篇(下)
第五十六章 Spring之假如让你来写Boot——SPI篇
第五十七章 Spring之假如让你来写Boot——配置文件篇(上)
第五十八章 Spring之假如让你来写Boot——配置文件篇(下)
第五十九章 Spring之假如让你来写Boot——番外篇:再谈Bean定义
第六十章 Spring之假如让你来写Boot——自动装配篇
第六十一章 Spring之假如让你来写Boot——番外篇:杂谈Starter
第六十二章 Spring之假如让你来写Boot——番外篇:重构BeanFactory
第六十三章 Spring之假如让你来写Boot——番外篇:再谈ApplicationContext
第六十四章 Spring之假如让你来写Boot——内嵌Web容器篇
第六十五章 Spring之假如让你来写Boot——Main方法启动篇
第六十六章 Spring之最终章——结语篇
文章目录
前言
对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》
书接上回,在上篇 第二章 Spring源码阅读之假如让我来写IOC容器——加载资源篇 已经说到了如何通过协议加载不同路径下的资源,接下来继续看下如何解析配置文件吧
尝试动手写IOC容器
出场人物:A君(苦逼的开发)、老大(项目经理)
背景:老大要求A君在一周内开发个简单的IOC容器
前情提要:在A君提交代码后,老大提出配置文件可是有多种多样的,目前这个只支持XML,如果是Properties、YML、JSON的呢?
第四版 抽取配置文件接口
A君骂骂咧咧的回到了工位:“XML不香吗?还要支持别的”,独自生了会儿闷气。A君又开始思考了,混口饭吃,不容易啊
既然是想要多种实现,老样子,还是定一个接口BeanDefinitionReader,用来读取不同配置文件,接口代码如下:
import com.hqd.ch03.v4.io.Resource;
/**
* 读取配置文件
*/
public interface BeanDefinitionReader {
void loadBeanDefinition(Resource resource);
}
接口定义完成之后,就开始定义具体实现了。先是解析XML的配置文件,代码没什么变动,只不过换了个地方。代码如下:
import com.hqd.ch03.v4.io.Resource;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.util.List;
import java.util.Map;
/**
* 解析xml配置
*/
public class XmlBeanDefinitionReader implements BeanDefinitionReader {
private Map<String, Object> bdMap;
public XmlBeanDefinitionReader(Map<String, Object> bdMap) {
this.bdMap = bdMap;
}
private void initBean(Object bean, List<Element> properties) {
try {
properties.forEach(property -> {
String name = property.attributeValue("name");
String value = property.attributeValue("value");
try {
BeanUtils.setProperty(bean, name, value);
} catch (Exception e) {
e.printStackTrace();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private Object createBean(String className) {
try {
return Class.forName(className).getConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public void loadBeanDefinition(Resource resource) {
SAXReader reader = new SAXReader();
try {
Document document = reader.read(resource.getInputStream());
Element rootElement = document.getRootElement();
List<Element> beanEles = rootElement.elements("bean");
beanEles.forEach(beanEle -> {
String className = beanEle.attributeValue("class");
Object bean = createBean(className);
List<Element> properties = beanEle.elements("property");
if (CollectionUtils.isNotEmpty(properties)) {
initBean(bean, properties);
}
bdMap.put(beanEle.attributeValue("id"), bean);
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
关于Properties的,这里偷个懒,先不实现了,如下:
import com.hqd.ch03.v4.io.Resource;
/**
* 解析Properties配置
*/
public class PropertiesBeanDefinitionReader implements BeanDefinitionReader {
@Override
public void loadBeanDefinition(Resource resource) {
//TODO
}
}
由于配置文件有了多种实现,所以SpringImitation类也需要多种实现,先抽取公共实现为父类,如下:
import com.hqd.ch03.v4.io.DefaultResourceLoader;
import com.hqd.ch03.v4.io.ResourceLoader;
import java.util.HashMap;
import java.util.Map;
public abstract class SpringImitationV4 {
protected Map<String, Object> bdMap = new HashMap<>();
/**
* 资源加载器
*/
protected ResourceLoader resourceLoader = new DefaultResourceLoader();
public <T> T getBean(String name, Class<T> clazz) {
return (T) bdMap.get(name);
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
提取完父类,接下来是XML配置的实现,如下:
import com.hqd.ch03.v4.reader.XmlBeanDefinitionReader;
public class SpringImitationV4Xml extends SpringImitationV4 {
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(bdMap);
public SpringImitationV4Xml(String location) {
this.reader.loadBeanDefinition(resourceLoader.getResource(location));
}
}
再来个Properties的实现,如下:
import com.hqd.ch03.v4.reader.PropertiesBeanDefinitionReader;
public class SpringImitationV4Property extends SpringImitationV4 {
private PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader();
public SpringImitationV4Property(String location) {
this.reader.loadBeanDefinition(resourceLoader.getResource(location));
}
}
接着做个测试,测试代码如下:
/**
* 添加配置文件读取接口
*/
@Test
public void v4() {
System.out.println("############# 第四版:简化资源接口使用 #############");
SpringImitationV4 bean = new SpringImitationV4Xml("classpath:v4/bean.xml");
User user = bean.getBean("user", User.class);
System.out.println("基于类路径加载:" + user);
SpringImitationV4 bean1 = new SpringImitationV4Property("file:E:/代码/java/JavaEE/spring-imitation/src/main/resources/v4/bean.xml");
User user1 = bean1.getBean("user", User.class);
System.out.println("基于文件系统路径加载:" + user1);
}
输出如下:
捣鼓了半天,A君多少松了一口气,总算可以交差了。这回A君已经做好心里准备了,颤抖着双手把代码提交上去,坐等老大批评(开摆)
老大又看了遍代码,语重心长的说:A君,你没有将bean定义提取出来,假如用户想在运行时修改bean定义,这时候怎么办?拿回去优化下吧
第五版 提取Bean配置信息
得!这个老大不是个好糊弄的主,继续优化吧
A君看着代码,琢磨了一会儿,心想:既然运行时可能修改bean定义,那就先把Bean定义缓存缓存起来,等到用的时候在缓存里边取就行了
于是,A君定义了BeanDefinition类用来存储Bean定义,代码如下:
import lombok.Data;
/**
* Bean配置信息类
*/
@Data
public class BeanDefinition {
private String id;
private String beanClass;
private String scope;
private Boolean lazyInit;
private String initMethodName;
private String destroyMethodName;
private String description;
private MutablePropertyValues properties;
}
Bean配置信息的实体类有了,还需要个地方缓存这个Bean信息才行。考虑到缓存可能有多种实现,A君又定义了BeanDefinitionRegistry接口,代码如下:
import com.hqd.ch03.v5.config.BeanDefinition;
/**
* 缓存Bean定义信息
*/
public interface BeanDefinitionRegistry {
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
void removeBeanDefinition(String beanName);
BeanDefinition getBeanDefinition(String beanName);
boolean containsBeanDefinition(String beanName);
}
有了BeanDefinitionRegistry接口之后还需要提供实现,默认用map实现即可,代码如下:
import com.hqd.ch03.v5.config.BeanDefinition;
import java.util.HashMap;
import java.util.Map;
/**
* Bean信息缓存,map实现
*/
public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry {
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitionMap.put(beanName, beanDefinition);
}
@Override
public void removeBeanDefinition(String beanName) {
beanDefinitionMap.remove(beanName);
}
@Override
public BeanDefinition getBeanDefinition(String beanName) {
return beanDefinitionMap.get(beanName);
}
@Override
public boolean containsBeanDefinition(String beanName) {
return beanDefinitionMap.containsKey(beanName);
}
}
XmlBeanDefinitionReader需要新增构造器,用来接收传入的BeanDefinitionRegistry ,改造如下:
完整代码:
import com.hqd.ch03.v5.config.BeanDefinition;
import com.hqd.ch03.v5.config.MutablePropertyValues;
import com.hqd.ch03.v5.io.Resource;
import com.hqd.ch03.v5.registry.BeanDefinitionRegistry;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.util.List;
/**
* XML方式读取Bean定义
*/
public class XmlBeanDefinitionReader implements BeanDefinitionReader {
private BeanDefinitionRegistry beanDefinitionRegistry;
public XmlBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {
this.beanDefinitionRegistry = beanDefinitionRegistry;
}
@Override
public void loadBeanDefinition(Resource resource) {
SAXReader reader = new SAXReader();
try {
Document document = reader.read(resource.getInputStream());
Element rootElement = document.getRootElement();
List<Element> beanEles = rootElement.elements("bean");
beanEles.forEach(beanEle -> {
BeanDefinition bd = parseBeanEle(beanEle);
beanDefinitionRegistry.registerBeanDefinition(bd.getId(), bd);
});
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 转换bean节点
*
* @param element
* @return
*/
private BeanDefinition parseBeanEle(Element element) {
BeanDefinition bd = new BeanDefinition();
bd.setScope(element.attributeValue("scope"));
bd.setId(element.attributeValue("id"));
String lazyInit = element.attributeValue("lazy-init");
bd.setLazyInit(StringUtils.isBlank(lazyInit) ? true : Boolean.valueOf(lazyInit));
bd.setBeanClass(element.attributeValue("class"));
bd.setProperties(parsePropertyEles(element.elements("property")));
return bd;
}
/**
* 转换bean下的property节点
*
* @param elements
* @return
*/
private MutablePropertyValues parsePropertyEles(List<Element> elements) {
if (CollectionUtils.isNotEmpty(elements)) {
MutablePropertyValues propertyValues = new MutablePropertyValues();
elements.forEach(element -> {
propertyValues.addProperty(element.attributeValue("name"), element.attributeValue("value"));
});
return propertyValues;
}
return null;
}
}
SpringImitation需要提供BeanDefinitionRegistry属性,创建bean时从缓存获取获取Bean配置信息。改造如下:
完整代码:
import com.hqd.ch03.v5.config.BeanDefinition;
import com.hqd.ch03.v5.config.MutablePropertyValues;
import com.hqd.ch03.v5.io.DefaultResourceLoader;
import com.hqd.ch03.v5.io.ResourceLoader;
import com.hqd.ch03.v5.registry.BeanDefinitionRegistry;
import com.hqd.ch03.v5.registry.SimpleBeanDefinitionRegistry;
import org.apache.commons.beanutils.BeanUtils;
public abstract class SpringImitationV5 {
protected BeanDefinitionRegistry beanDefinitionRegistry = new SimpleBeanDefinitionRegistry();
/**
* 资源加载器
*/
protected ResourceLoader resourceLoader = new DefaultResourceLoader();
private void initBean(Object bean, BeanDefinition bd) {
try {
MutablePropertyValues properties = bd.getProperties();
properties.getProperties().keySet().forEach(name -> {
String value = properties.getValue(name);
try {
BeanUtils.setProperty(bean, name, value);
} catch (Exception e) {
e.printStackTrace();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private Object createBean(BeanDefinition bd) {
try {
String beanClass = bd.getBeanClass();
Object o = Class.forName(beanClass).getConstructor().newInstance();
initBean(o, bd);
return o;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public <T> T getBean(String beanName, Class<T> clazz) {
BeanDefinition bd = beanDefinitionRegistry.getBeanDefinition(beanName);
return (T) createBean(bd);
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
SpringImitationXml也需要改造下,创建XmlBeanDefinitionReader时候,需要传入Bean配置注册器,改造如下:
完整代码:
import com.hqd.ch03.v5.reader.XmlBeanDefinitionReader;
public class SpringImitationV5Xml extends SpringImitationV5 {
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanDefinitionRegistry);
public SpringImitationV5Xml(String location) {
this.reader.loadBeanDefinition(resourceLoader.getResource(location));
}
}
做个简单的测试,测试代码如下:
/**
* 提取Bean配置信息
*/
@Test
public void v5() {
System.out.println("############# 第五版:提取Bean配置信息 #############");
SpringImitationV5 bean = new SpringImitationV5Xml("classpath:v5/bean.xml");
User user = bean.getBean("user", User.class);
System.out.println("基于类路径加载:" + user);
}
输出如下:
做完这些,A君又看几遍,觉得没什么问题后,再次把代码提交上去。他不太相信老大还能提出问题
老大这次没有马上提出问题,过了许久,才缓缓说道:XmlBeanDefinitionReader这个类做的事太多了,不论是Document的加载,解析,还是注册BeanDefinition都在里边做,类太过臃肿了。而且把XML文件加载成DOM可以有多种方式,你又把他写死了
总结
正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)