分了六步来完成一个简易的IOC功能的搭建:
- 最基本的容器
- 将 bean 创建放入工厂
- 为 bean 注入属性
- 读取 xml 配置来初始化 bean
- 为 bean 注入 bean
- 引入 ApplicationContext
首先,我们知道 bean 实例化的几种方式分别是:
- 普通构造函数
- 静态工厂
- 实例工厂
IoC即 控制反转,在对象层级嵌套的情况下,IoC帮助我们在创建对象时,自动创建依赖的对象并注入,IoC容器创建对象是自底向上的:

这篇博客很好地解释了 IoC 和 DI:https://www.iteye.com/blog/jinnianshilongnian-1413846
下面,我们逐步实现一个简易的 IoC 容器
Step 1 最基本的容器
- 首先创建了一个
BeanDefinition类来封装 bean 对象和元信息
public class BeanDefinition {private Object bean;public BeanDefinition(Object bean) {this.bean = bean;}public Object getBean() {return bean;}}
- 创建
BeanFactory负责保存 bean 对象到容器中(这里用到的是Hashmap保存),并且可以通过 name 来获取保存的BeanDefinition
public class BeanFactory {private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();public Object getBean(String name) {return beanDefinitionMap.get(name).getBean();}public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {beanDefinitionMap.put(name, beanDefinition);}}
- 测试代码
// 1.初始化beanfactoryBeanFactory beanFactory = new BeanFactory();// 2.注入beanBeanDefinition beanDefinition = new BeanDefinition(new HelloWorldService());beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);// 3.获取beanHelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");helloWorldService.helloWorld();
Step 2 将 bean 创建放入工厂
在上一步中,工厂类的功能只是保存和获取 BeanDefinition,这一步中,将 bean 的创建也放到工厂中完成,在这一步中,把简单工厂模式改造成了提取了接口的抽象工厂模式
- 改造 BeanDefinition
public class BeanDefinition {@Getter@Setterprivate Object bean;@Getter@Setterprivate Class beanClass;@Getterprivate String beanClassName;public void setBeanClassName(String beanClassName) {this.beanClassName = beanClassName;try {this.beanClass = Class.forName(beanClassName);} catch (ClassNotFoundException e) {e.printStackTrace();}}}
- 首先,定义工厂接口
public interface BeanFactory {Object getBean(String name);void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception;}
- 创建抽象工厂,并在工厂类中创建 bean(doCreateBean)
public abstract class AbstractBeanFactory implements BeanFactory {private Map<String, BeanDefinition> beanMap = new ConcurrentHashMap<>();@Overridepublic Object getBean(String name) {return beanMap.get(name).getBean();}@Overridepublic void registerBeanDefinition(String name, BeanDefinition beanDefinition) {Object bean = doCreateBean(beanDefinition);beanDefinition.setBean(bean);beanMap.put(name, beanDefinition);}/*** 初始化 Bean** @param beanDefinition* @return*/protected abstract Object doCreateBean(BeanDefinition beanDefinition);}
- 具体的工厂类实现,利用反射创建 bean
public class AutowireCapableBeanFactory extends AbstractBeanFactory {@Overrideprotected Object doCreateBean(BeanDefinition beanDefinition) {try {return beanDefinition.getBeanClass().newInstance();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return null;}}
- 测试代码
// 1.初始化BeanFactoryBeanFactory beanFactory = new AutowireCapableBeanFactory();// 2.注入BeanBeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setBeanClassName("wu.easyioc.HelloWorldService");beanFactory.registerBeanDefinition("helloworld", beanDefinition);// 3.获取BeanHelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloworld");helloWorldService.helloworld();
Step 3 为 bean 注入属性
上面的步骤中,无法为 bean 注入属性,下面是在创建 bean 的时候,利用反射来为 bean 注入属性
- 创建
PropertyValue来保存需要注入的字段信息
public class PropertyValue {@Getterprivate final String name;@Getterprivate final Object value;public PropertyValue(String name, Object value) {this.name = name;this.value = value;}}
- 创建
PropertyValues来保存多个PropertyValue,实际上就是一个 List
public class PropertyValues {private final List<PropertyValue> propertyValueList = new ArrayList<>();public PropertyValues() {}public void addPropertyValue(PropertyValue propertyValue) {this.propertyValueList.add(propertyValue);}public List<PropertyValue> getPropertyValues() {return this.propertyValueList;}}
- 改造后的
AutowireCapableBeanFactory,根据 Class 对象创建对象实例后,再把PropertyValues中的属性名 - 属性值 循环注入到对象中
public class AutowireCapableBeanFactory extends AbstractBeanFactory {@Overrideprotected 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 beanDefinition) throws Exception {for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) {Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());declaredField.setAccessible(true);declaredField.set(bean, propertyValue.getValue());}}}
HelloService
public class HelloWorldService {String text; // 需要注入值的属性public void helloworld() {System.out.println(text);}}
- 测试代码
// 1.初始化BeanFactoryBeanFactory beanFactory = new AutowireCapableBeanFactory();// 2.注入BeanBeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setBeanClassName("wu.easyioc.HelloWorldService");// 3.设置属性PropertyValues propertyValues = new PropertyValues();propertyValues.addPropertyValue(new PropertyValue("text","helloworld from bot..."));beanDefinition.setPropertyValues(propertyValues);// 4.生成BeanbeanFactory.registerBeanDefinition("helloworld", beanDefinition);// 5.获取BeanHelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloworld");helloWorldService.helloworld();
Step 4 读取 xml 配置来初始化 bean
在步骤3中需要手动初始化 bean,下面我们通过xml配置文件的方式来自动注入 bean 信息
- 创建
Resource接口,负责获取内部资源,以输入流的形式返回
public interface Resource {InputStream getInputStream() throws IOException;}
- 创建
UrlResource类,实现Resource接口,传入一个URL来获取输入流
public class UrlResource implements Resource {private final URL url;public UrlResource(URL url) {this.url = url;}@Overridepublic InputStream getInputStream() throws IOException{URLConnection urlConnection = url.openConnection();urlConnection.connect();return urlConnection.getInputStream();}}
ResourceLoader根据 Class对象 来获取指定路径的URL对象,并注入UrlResource中
public class ResourceLoader {public Resource getResource(String location) {// 获取Class对象 -> 获取类装载器 -> 根据路径获取URL对象URL resource = this.getClass().getClassLoader().getResource(location);return new UrlResource(resource);}}
- 创建 Bean 读取接口
BeanDefinitionReader,负责从xml配置文件中读取 bean 信息
public interface BeanDefinitionReader {void loadBeanDefinitions(String location) throws Exception;}
- 实现接口
BeanDefinitionReader的抽象类AbstractBeanDefinitionReader,内部的 registry 集合保存了需要注册的 bean 信息
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {// 保存需要注册的Bean的容器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;}}
- 自动初始化 bean 信息的实现类
XmlBeanDefinitionReader,功能是将 xml 配置文件中配置的 bean 初始化信息存入到内部的 registy 集合中
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {public XmlBeanDefinitionReader(ResourceLoader resourceLoader) {super(resourceLoader);}@Overridepublic void loadBeanDefinitions(String location) throws Exception {// 获取输入流,输入流是为了输入xml资源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);// 解析beanregisterBeanDefinitions(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);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));}}}}
- 测试代码
// 1.读取配置XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");// 2.初始化BeanFactory并注册beanBeanFactory beanFactory = new AutowireCapableBeanFactory();for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());}// 3.获取beanHelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");helloWorldService.helloWorld();
- 大致流程图如下:

Step 5 为 bean 注入 bean
前面的步骤3中,利用反射完成了属性的注入,但是无法为 bean 注入 bean,下面完成为 bean 注入 bean 的实现
- 创建
BeanReference类,表示这个属性是对另外一个bean的引用
@Data@AllArgsConstructorpublic class BeanReference {private String name;private Object bean;}
- 在处理xml配置文件的时候,判断xml中需要注入的是
ref还是value,对不同的属性进行处理,所以需要改造处理的方法,对ref封装成BeanRefrence
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));}}}}
- 在
AutowireCapableBeanFactory中的注入属性方法中,判断注入属性是否为BeanReference类,如果是则调用getBean(...)方法获取 bean 对象并注入
protected void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception {for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) {// getDeclaredField 获取一个类的指定成员变量Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());// 需要对 private 的成员变量进行set操作,必须setAccessible(true)declaredField.setAccessible(true);Object value = propertyValue.getValue();if (value instanceof BeanReference) {BeanReference beanReference = (BeanReference) value;value = getBean(beanReference.getName());}declaredField.set(bean, value);}}
基于以上修改,已经可以对普通的bean依赖进行处理,但是遇到循环依赖的情况,会造成死锁。解决的方案是改造为单例的注入,先将Bean对象实例化后在设置对象属性,而不是在实例化的时候就注入属性。
- 在注册bean的方法
registerBeanDefinition中先不 createBean ,而是新建一个容器存放需要实例的 beanName
// 旧方法:public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {Object bean = doCreateBean(beanDefinition);beanDefinition.setBean(bean);beanMap.put(name, beanDefinition);}// 新的方法:public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {beanDefinitionMap.put(name, beanDefinition);beanDefinitionNames.add(name); // List<String> beanDefinitionNames}
- 创建
preInstantiateSingletons方法,在调用 getBean 前,先将 beanDefinitionNames 容器中的实例初始化
public void preInstantiateSingletons() throws Exception {for (Iterator it = this.beanDefinitionNames.iterator(); it.hasNext();) {String beanName = (String) it.next();getBean(beanName); // 初始化bean,单例形式}}
- 对
getBean方法的重写,对 bean 进行非空判断,为空则调用doCreateBean方法,如果不为空则返回 bean(单例)
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;}
- 引入两个Service,并循环依赖,测试代码
// 1.读取配置XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");// 2.初始化BeanFactory并注册beanAbstractBeanFactory beanFactory = new AutowireCapableBeanFactory();for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());}// 3.初始化bean,当然不需要这一步也是可以的,因为在下面的getBean(...)方法中,也有对null的Bean判断并创建,相当于 Lazy-initbeanFactory.preInstantiateSingletons();// 4.获取beanHelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");helloWorldService.helloWorld();
Step 6 引入 ApplicationContext
上面已经完成了简单的 IoC 功能,但是使用起来还是比较繁琐,所以引入了 ApplicationContext 来对过程进行进一步地封装
- 创建
ApplicationContext接口
public interface ApplicationContext {}
- 抽象实现
ApplicationContext接口的抽象类AbstractApplicationContext,这里主要有两个方法:refresh(...)加载xml配置方法 和getBean(...)获取bean方法
public class AbstractApplicationContext implements ApplicationContext {protected AbstractBeanFactory beanFactory;public AbstractApplicationContext(AbstractBeanFactory beanFactory) {this.beanFactory = beanFactory;}public void refresh() throws Exception {}@Overridepublic Object getBean(String name) throws Exception {return beanFactory.getBean(name);}}
- 具体的实现类
ClassPathXmlApplicationContext,主要作用是实现了refresh(...)方法,该类主要用于加载指定路径的 xml 配置文件并保存
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();}@Overridepublic 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());}}}
- 测试代码
// 1. 载入 xml 配置ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");// 2. 获取 beanHelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService");// 3. 执行方法helloWorldService.helloWorld();
总结
以上实现了一个简易的 IoC 功能,在以上设计中,大量用到了 模板方法模式 和 抽象工厂模式 ,后续可以基于此实现:1. 基于注解的 IoC 功能 2. 简易的 AOP 功能
