概述
IoC(Inversion of Controller),即控制反转。它是一种设计思想,简单说就是创建Java对象的过程从之前new出来,变成了由Spring工厂创建出来(由Spring来负责控制对象的生命周期和对象之间的关系)。控制反转,转移的就是创建对象的主动权。
tiny-spring实现了基本的 IoC 容器,支持singleton类型的bean,包括初始化、属性注入、以及依赖 Bean 注入,可从 XML 中读取配置。
第一步-最基本的容器
IoC最基本的角色有两个:容器(BeanFactory
)和Bean
本身。这里使用BeanDefinition
来封装了bean对象,这样可以保存一些额外的元信息
BeanDefinition类
定义BeanDefinition类来封装一个spring bean对象
/**
* 核心bean的定义,其对象被注册到BeanFactory中
*/
public class BeanDefinition {
private Object bean;
public BeanDefinition(Object bean) {
this.bean = bean;
}
public Object getBean() {
return bean;
}
}
复制代码
BeanFactory类
定义一个Bean工厂类,即容器,用一个Map实现Bean的注册和获取。注册机制很简单,key为bean名称,value为BeanDefinition类对象。
/**
* bean的工厂类,用于保存注册的bean
*/
public class BeanFactory {
/**
* 存放Factory里的所有Bean的详细信息,可以看做是Bean的注册表
*/
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
public Object getBean(String name) {
return beanDefinitionMap.get(name).getBean();
}
/**
* 将新加的BeanDefinition对象加到注册表中
* @param name 注册表Map的key
* @param beanDefinition BeanDefinition对象,存储了Bean的详细信息
*/
public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
beanDefinitionMap.put(name, beanDefinition);
}
}
复制代码
测试
用于测试的bean对象,其只拥有一个方法,没有成员变量
/**
* 用于测试的bean
*/
public class HelloWorldService {
public void helloWorld(){
System.out.println("Hello World!");
}
}
复制代码
bean注册与获取实现的测试类
public class BeanFactoryTest {
@Test
public void test() {
// 1.初始化beanfactory
BeanFactory beanFactory = new BeanFactory();
// 2.注入bean
BeanDefinition beanDefinition = new BeanDefinition(new HelloWorldService());
beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);
// 3.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
}
}
------------output------------
Hello world!
复制代码
第二步-将bean创建放入工厂
step1中的bean是初始化好之后再作为BeanDefinition
的入参。实际使用中,我们希望容器来管理bean的创建。于是我们将bean的初始化放入BeanFactory
中。为了保证扩展性,我们使用接口提取的方法,将BeanFactory
替换成接口,而使用AbstractBeanFactory
和AutowireCapableBeanFactory
作为其实现。"AutowireCapable"的意思是“可自动装配的”,为我们后面注入属性做准备。
BeanDefinition类
为BeanDefinition
类增加一个Class类成员变量用来记录当前bean的Class类型,以及记录bean的类全限定名称。
public class BeanDefinition {
/**
* bean对象
*/
private Object bean;
/**
* 记录bean对象的Class信息
*/
private Class beanClass;
/**
* 记录bean对象的ClassName
*/
private String beanClassName;
/**
* 通过bean对象的className加载该对象,并初始化
* @param beanClassName 类名
*/
public void setBeanClassName(String beanClassName) {
this.beanClassName = beanClassName;
try {
this.beanClass = Class.forName(beanClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 省略其他的getter和setter方法
}
复制代码
BeanFactory容器接口
public interface BeanFactory {
Object getBean(String name);
void registerBeanDefinition(String name, BeanDefinition beanDefinition);
}
复制代码
实现容器的虚拟类
public abstract class AbstractBeanFactory implements BeanFactory {
/**
* 实际存储bean的容器
*/
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
@Override
public Object getBean(String name) {
return beanDefinitionMap.get(name).getBean();
}
@Override
public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
Object bean = doCreateBean(beanDefinition);
beanDefinition.setBean(bean);
beanDefinitionMap.put(name, beanDefinition);
}
/**
* 初始化bean,实际建立方式交给子类
* @param beanDefinition
* @return
*/
protected abstract Object doCreateBean(BeanDefinition beanDefinition);
}
复制代码
可自动装配的实现类
public class AutowireCapableBeanFactory extends AbstractBeanFactory {
/**
* 根据类限定名称创建类实例
* @param beanDefinition
* @return
*/
@Override
protected Object doCreateBean(BeanDefinition beanDefinition) {
try {
Object bean = beanDefinition.getBeanClass().newInstance();
return bean;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
复制代码
测试
public class BeanFactoryTest {
@Test
public void test() {
// 1.初始化beanfactory
BeanFactory beanFactory = new AutowireCapableBeanFactory();
// 2.注入bean
BeanDefinition beanDefinition = new BeanDefinition();
String className = new HelloWorldService().getClass().getName();
beanDefinition.setBeanClassName(className);
beanFactory.registerBeanDefinition(className, beanDefinition);
// 3.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean(className);
helloWorldService.helloWorld();
}
}
------------------output-----------------
Hello World!
复制代码
第三步-为bean注入属性
这一步,我们想要为bean注入属性。我们选择将属性注入信息保存成PropertyValue
对象,并且保存到BeanDefinition
中。这样在初始化bean的时候,我们就可以根据ropertyValue
来进行bean属性的注入。Spring本身使用了setter来进行注入,这里为了代码简洁,我们使用Field的形式来注入。
PropertyValue类,用于单个属性注入
public class PropertyValue {
// 属性
private final String name;
// 属性值
private final Object value;
public PropertyValue(String name, Object value) {
this.name = name;
this.value = value;
}
// 省略getter和setter方法
}
复制代码
PropertyValues类,包装一个对象所有的PropertyValue
public class PropertyValues {
private final List<PropertyValue> propertyValueList = new ArrayList<PropertyValue>();
public PropertyValues() {
}
public void addPropertyValue(PropertyValue pv) {
//TODO:这里可以对于重复propertyName进行判断,直接用list没法做到
this.propertyValueList.add(pv);
}
public List<PropertyValue> getPropertyValues() {
return this.propertyValueList;
}
}
复制代码
为BeanDefinition添加一个属性成员
public class BeanDefinition {
private Object bean;
private Class beanClass;
private String beanClassName;
/**
* 保存bean的属性
*/
private PropertyValues propertyValues;
// 省略其他的getter和setter方法
}
复制代码
AutowireCapableBeanFactory虚拟类
增加对注入属性的绑定
public class AutowireCapableBeanFactory extends AbstractBeanFactory {
@Override
protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception {
Object bean = createBeanInstance(beanDefinition);
applyPropertyValues(bean, beanDefinition);
return bean;
}
protected Object createBeanInstance(BeanDefinition beanDefinition) throws Exception {
return beanDefinition.getBeanClass().newInstance();
}
// 使用反射绑定属性列表
protected void applyPropertyValues(Object bean, BeanDefinition mbd) throws Exception {
for (PropertyValue propertyValue : mbd.getPropertyValues().getPropertyValues()) {
Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
declaredField.setAccessible(true);
declaredField.set(bean, propertyValue.getValue());
}
}
}
复制代码
测试
用于测试的HelloWorldService
不再是只含有一个方法了,添加了一个String类型的成员变量。
public class HelloWorldService {
private String text;
public void helloWorld(){
System.out.println(text);
}
public void setText(String text) {
this.text = text;
}
}
复制代码
相应的测试方法实现了属性绑定结果的验证。
public class BeanFactoryTest {
@Test
public void test() throws Exception {
// 1.初始化beanfactory
BeanFactory beanFactory = new AutowireCapableBeanFactory();
// 2.bean定义
BeanDefinition beanDefinition = new BeanDefinition();
String className = new HelloWorldService().getClass().getName();
beanDefinition.setBeanClassName(className);
// 3.设置属性
PropertyValues propertyValues = new PropertyValues();
propertyValues.addPropertyValue(new PropertyValue("text", "Hello World!"));
beanDefinition.setPropertyValues(propertyValues);
// 4.生成bean
beanFactory.registerBeanDefinition(className, beanDefinition);
// 5.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean(className);
helloWorldService.helloWorld();
}
}
-----------output---------
Hello World!
复制代码
第四步-读取xml配置来初始化bean
这么大一坨初始化代码让人心烦。这里的BeanDefinition
只是一些配置,我们还是用xml来初始化吧。我们定义了BeanDefinitionReader
初始化bean,它有一个实现是XmlBeanDefinitionReader
。这一步其实就是从xml中读取数然后再用反射实现bean的转化,原理和手工装配并没有很大的区别。先看看用于测试的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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean name="helloWorldService" class="us.codecraft.tinyioc.HelloWorldService">
<property name="text" value="Hello World!"></property>
</bean>
</beans>
复制代码
xml文件中包含一个名为helloWorldService
的bean以及其一个text属性。
BeanDefinitionReader接口
这个记录用来从配置中读取BeanDefinition。
public interface BeanDefinitionReader {
void loadBeanDefinitions(String location) throws Exception;
}
复制代码
AbstractBeanDefinitionReader虚类
其实现了BeanDefinitionReader接口,但没有真正去定义加载bean的实现方式。主要功能是配置一个BeanDefinition的容器,并定义一个加载器。
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {
private Map<String,BeanDefinition> registry;
private ResourceLoader resourceLoader;
protected AbstractBeanDefinitionReader(ResourceLoader resourceLoader) {
this.registry = new HashMap<String, BeanDefinition>();
this.resourceLoader = resourceLoader;
}
public Map<String, BeanDefinition> getRegistry() {
return registry;
}
public ResourceLoader getResourceLoader() {
return resourceLoader;
}
}
复制代码
XmlBeanDefinitionReader类
XmlBeanDefinitionReader
类继承AbstractBeanDefinitionReader
,实现了从xml文件中解析并绑定spring bean及其属性。
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public XmlBeanDefinitionReader(ResourceLoader resourceLoader) {
super(resourceLoader);
}
@Override
public void loadBeanDefinitions(String location) throws Exception {
InputStream inputStream = getResourceLoader().getResource(location).getInputStream();
doLoadBeanDefinitions(inputStream);
}
protected void doLoadBeanDefinitions(InputStream inputStream) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = factory.newDocumentBuilder();
Document doc = docBuilder.parse(inputStream);
// 解析bean
registerBeanDefinitions(doc);
inputStream.close();
}
public void registerBeanDefinitions(Document doc) {
Element root = doc.getDocumentElement();
parseBeanDefinitions(root);
}
protected void parseBeanDefinitions(Element root) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
processBeanDefinition(ele);
}
}
}
// 解析
protected void processBeanDefinition(Element ele) {
String name = ele.getAttribute("name");
String className = ele.getAttribute("class");
BeanDefinition beanDefinition = new BeanDefinition();
processProperty(ele,beanDefinition);
// 绑定bean
beanDefinition.setBeanClassName(className);
getRegistry().put(name, beanDefinition);
}
// 绑定属性
private void processProperty(Element ele,BeanDefinition beanDefinition) {
NodeList propertyNode = ele.getElementsByTagName("property");
for (int i = 0; i < propertyNode.getLength(); i++) {
Node node = propertyNode.item(i);
if (node instanceof Element) {
Element propertyEle = (Element) node;
String name = propertyEle.getAttribute("name");
String value = propertyEle.getAttribute("value");
beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name,value));
}
}
}
}
复制代码
测试
public class BeanFactoryTest {
@Test
public void test() throws Exception {
// 1.读取配置
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
// 2.初始化BeanFactory并注册bean
BeanFactory beanFactory = new AutowireCapableBeanFactory();
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
// 3.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
}
}
复制代码
第五步-为bean注入bean
使用xml配置之后,似乎里我们熟知的Spring更近了一步!但是现在有一个大问题没有解决:我们无法处理bean之间的依赖,无法将bean注入到bean中,所以它无法称之为完整的IoC容器!如何实现呢?我们定义一个BeanReference,来表示这个属性是对另一个bean的引用。这个在读取xml的时候初始化,并在初始化bean的时候,进行解析和真实bean的注入。先看下测试用的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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean name="outputService" class="us.codecraft.tinyioc.OutputService">
<property name="helloWorldService" ref="helloWorldService"></property>
</bean>
<bean name="helloWorldService" class="us.codecraft.tinyioc.HelloWorldService">
<property name="text" value="Hello World!"></property>
<property name="outputService" ref="outputService"></property>
</bean>
</beans>
复制代码
可以发现相比上一步,bean对了一个引用类型的成员变量。
BeanReference类
该类表示这个属性是对另一个spring bean的引用。
public class BeanReference {
private String name;
private Object bean;
public BeanReference(String name) {
this.name = name;
}
// 省略getter/setter方法
}
复制代码
BeanDefinition类
public class BeanDefinition {
private Object bean;
private Class beanClass;
private String beanClassName;
private PropertyValues propertyValues = new PropertyValues();
// 省略getter/setter方法
复制代码
AbstractBeanFactory类
同时为了解决循环依赖的问题,我们使用lazy-init的方式,将createBean的事情放到getBean的时候才执行,是不是一下子方便很多?这样在注入bean的时候,如果该属性对应的bean找不到,那么就先创建!因为总是先创建后注入,所以不会存在两个循环依赖的bean创建死锁的问题。
public abstract class AbstractBeanFactory implements BeanFactory {
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
private final List<String> beanDefinitionNames = new ArrayList<String>();
// 实现懒加载
@Override
public Object getBean(String name) throws Exception {
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
if (beanDefinition == null) {
throw new IllegalArgumentException("No bean named " + name + " is defined");
}
Object bean = beanDefinition.getBean();
if (bean == null) {
bean = doCreateBean(beanDefinition);
}
return bean;
}
@Override
public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {
beanDefinitionMap.put(name, beanDefinition);
beanDefinitionNames.add(name);
}
public void preInstantiateSingletons() throws Exception {
for (Iterator it = this.beanDefinitionNames.iterator(); it.hasNext();) {
String beanName = (String) it.next();
getBean(beanName);
}
}
/**
* 初始化bean
*
* @param beanDefinition
* @return
*/
protected abstract Object doCreateBean(BeanDefinition beanDefinition) throws Exception;
}
复制代码
AutowireCapableBeanFactory类
由于引入了bean之间的依赖,所以需要当前spring bean的成员变量不是基本数据类型时,需要添加而外的处理方法。
public class AutowireCapableBeanFactory extends AbstractBeanFactory {
@Override
protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception {
Object bean = createBeanInstance(beanDefinition);
beanDefinition.setBean(bean);
applyPropertyValues(bean, beanDefinition);
return bean;
}
protected Object createBeanInstance(BeanDefinition beanDefinition) throws Exception {
return beanDefinition.getBeanClass().newInstance();
}
protected void applyPropertyValues(Object bean, BeanDefinition mbd) throws Exception {
for (PropertyValue propertyValue : mbd.getPropertyValues().getPropertyValues()) {
Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
declaredField.setAccessible(true);
Object value = propertyValue.getValue();
// 处理成员变量是引用类型的情况
if (value instanceof BeanReference) {
BeanReference beanReference = (BeanReference) value;
value = getBean(beanReference.getName());
}
declaredField.set(bean, value);
}
}
复制代码
XmlBeanDefinitionReader类
同样的,需要单独处理xml文件中property元素为引用类型的情况。
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
...
private void processProperty(Element ele, BeanDefinition beanDefinition) {
NodeList propertyNode = ele.getElementsByTagName("property");
for (int i = 0; i < propertyNode.getLength(); i++) {
Node node = propertyNode.item(i);
if (node instanceof Element) {
Element propertyEle = (Element) node;
String name = propertyEle.getAttribute("name");
String value = propertyEle.getAttribute("value");
if (value != null && value.length() > 0) {
beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value));
} else {
// 处理引用类型
String ref = propertyEle.getAttribute("ref");
if (ref == null || ref.length() == 0) {
throw new IllegalArgumentException("Configuration problem: <property> element for property '"
+ name + "' must specify a ref or value");
}
BeanReference beanReference = new BeanReference(ref);
beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, beanReference));
}
}
}
}
...
}
复制代码
测试
相应的需要更改测试所用的模型类
public class HelloWorldService {
private String text;
// 增加一个类成员变量
private OutputService outputService;
public void helloWorld(){
outputService.output(text);
}
// 省略setter方法
}
public class OutputService {
private HelloWorldService helloWorldService;
public void output(String text){
Assert.assertNotNull(helloWorldService);
System.out.println(text);
}
public void setHelloWorldService(HelloWorldService helloWorldService) {
this.helloWorldService = helloWorldService;
}
}
复制代码
测试方法:
@Test
public void testPreInstantiate() throws Exception {
// 1.读取配置
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
// 2.初始化BeanFactory并注册bean
AbstractBeanFactory beanFactory = new AutowireCapableBeanFactory();
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
// 3.初始化bean
beanFactory.preInstantiateSingletons();
// 4.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
}
复制代码
第六步-ApplicationContext登场
现在BeanFactory
的功能齐全了,但是使用起来有点麻烦。于是我们引入熟悉的ApplicationContext
接口,并在AbstractApplicationContext
的refresh()
方法中进行bean的初始化工作。
ApplicationContext接口
ApplicationContext
接口继承BeanFactory
。BeanFactory
是对内的容器,而ApplicationContext
即对外提供的接口。
public interface ApplicationContext extends BeanFactory {
}
复制代码
AbstractApplicationContext类
public abstract class AbstractApplicationContext implements ApplicationContext {
protected AbstractBeanFactory beanFactory;
public AbstractApplicationContext(AbstractBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void refresh() throws Exception{
}
// 实际提供bean的还是BeanFactory
@Override
public Object getBean(String name) throws Exception {
return beanFactory.getBean(name);
}
}
复制代码
ClassPathXmlApplicationContext类
ClassPathXmlApplicationContext
类定义refresh
方法,实现类的自动加载和初始化工作。
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {
private String configLocation;
public ClassPathXmlApplicationContext(String configLocation) throws Exception {
this(configLocation, new AutowireCapableBeanFactory());
}
public ClassPathXmlApplicationContext(String configLocation, AbstractBeanFactory beanFactory) throws Exception {
super(beanFactory);
this.configLocation = configLocation;
refresh();
}
@Override
public void refresh() throws Exception {
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions(configLocation);
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
}
}
复制代码
测试
public class ApplicationContextTest {
@Test
public void test() throws Exception {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService");
helloWorldService.helloWorld();
}
}
复制代码
顿时感觉清爽了很多。