什么是IoC
控制反转(Inversion of Control,缩写为IoC),是一种设计模式,用来解耦组件之间的耦合度。
IoC容器系列的设计与实现
在Spring IoC容器的设计中,有两种主要的容器系列:BeanFactory与ApplicationContext。
BeanFactory,IoC容器的接口定义,该系列容器提供的是最基本的IoC容器的功能。ApplicationContext,容器的高级形态,它通过继承 MessageSource,ResourceLoader,ApplicationEventPublisher接口,在简单容器的基础上,增加了许多对高级容器的支持。
在Spring提供的最基本的IoC容器的接口定义和实现的基础上,Spring通过定义BeanDefinition来管理基于Spring的应用中的各种对象以及他们直接的相互依赖关系。BeanDefinition 抽象了我们对 Bean的定义,是让容器起作用的主要数据类型。对 IOC 容器来说,BeanDefinition 就是对依赖反转模式中管理的对象依赖关系的数据抽象。也是容器实现依赖反转功能的核心数据结构。
BeanFactory接口定义的方法:
BeanFactory容器的设计原理
BeanFactory接口提供了使用IoC容器的规范,我们以XmlBeanFactory的实现为例来说明简单IoC容器的设计原理。XmlBeanFactory类继承关系图:
XmlBeanFactory继承自DefaultListableBeanFactory类,DefaultListableBeanFactory包含了基本IoC容器所具有的重要功能,XmlBeanFactory在继承了DefaultListableBeanFactory容器的功能的同时,增加了新的功能,它是一个可以读取以XML文件定义的BeanDefinition的IoC容器,来看下具体实现:
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
}
复制代码
在XmlBeanFactory中,初始化了一个XmlBeanDefinitionReader对象,该对象用于对XML文件定义信息的处理。XmlBeanFactory构造函数需要传入一个Resource参数,该参数封装了BeanDefinition的来源信息。在构造方法中,XmlBeanDefinitionReader会调用loadBeanDefinitions方法来完成BeanDefinition的加载。
ApplicationContext容器的设计原理
我们以常用的FileSystemXmlApplicationContext为例,debug源码来看下ApplicationContext容器的设计原理。
测试代码如下:
@Test
public void testApplicationContext() {
ApplicationContext context = new FileSystemXmlApplicationContext("src/main/resources/bean.xml");
context.getBean("helloWorld",HelloWorld.class).sayHello();
}
复制代码
进入 FileSystemXmlApplicationContext 的构造方法:
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
复制代码
该方法调用重载方法,传入三个参数:数组类型的configLocation,默认属性为true,父容器为null:
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
复制代码
该方法首先会调用父类的构造方法,参数为null,在跟进去之前,我们先看下FileSystemXmlApplicationContext类的继承结构:
super(parent)方法会一直往上调用其父类构造方法,直到AbstractApplicationContext:
public AbstractApplicationContext(ApplicationContext parent) {
this();
setParent(parent);
}
复制代码
this方法调用默认构造函数,为属性resourcePatternResolver赋值:
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
复制代码
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
复制代码
回到setConfigLocations(configLocations)方法,看下该方法的具体实现:
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);
}
复制代码
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
复制代码
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
复制代码
getEnvironment方法返回的是StandardEnvironment对象,该对象是标准环境,会自动注册System.getProperties() 和 System.getenv()到环境。
接下来,回到resolvePath方法,该方法会调用AbstractEnvironment类的resolveRequiredPlaceholders方法:
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
return this.propertyResolver.resolveRequiredPlaceholders(text);
}
复制代码
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
复制代码
此时propertyResolver属性已经有值了,紧接着会调用propertyResolver属性的resolveRequiredPlaceholders方法,我们先看下ConfigurablePropertyResolver继承结构:
进入resolveRequiredPlaceholders方法看下,该方法在AbstractPropertyResolver中:
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
if (this.strictHelper == null) {
this.strictHelper = createPlaceholderHelper(false);
}
return doResolvePlaceholders(text, this.strictHelper);
}
复制代码
该方法做了两件事:1.调用createPlaceholderHelper方法为属性strictHelper赋值;2.调用doResolvePlaceholders方法,该方法是具体解析方法。
其中属性strictHelper,类型为PropertyPlaceholderHelper,持有要解析的前缀后缀,我们看下该类属性
继续跟进,会调用doResolvePlaceholders方法:
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() {
@Override
public String resolvePlaceholder(String placeholderName) {
return getPropertyAsRawString(placeholderName);
}
});
}
复制代码
具体不在深入,讲了半天,我们来总结下setConfigLocations方法干了什么事情:解析FileSystemXmlApplicationContext构造函数中参数存在的占位符,并替换为真实值。如:
@Test
public void testApplicationContext() {
Properties properties = System.getProperties();
properties.setProperty("aaaa", "bbbb");
properties.setProperty("bbbb", "src/main/resources/bean.xml");
ApplicationContext context = new FileSystemXmlApplicationContext("${${aaaa}}");
context.getBean("helloWorld",HelloWorld.class).sayHello();
}
复制代码
setConfigLocations方法会将${${aaaa}}
,递归解析为bbbb,然后查找对应的系统属性存在aaaa为key的value bbbb,然后再根据bbbb查找系统属性,替换为applicationContext.xml。
如果读完觉得有收获的话,欢迎点赞、关注、加公众号【Java在线】,查阅更多精彩历史!!!