分了六步来完成一个简易的IOC功能的搭建:

  1. 最基本的容器
  2. 将 bean 创建放入工厂
  3. 为 bean 注入属性
  4. 读取 xml 配置来初始化 bean
  5. 为 bean 注入 bean
  6. 引入 ApplicationContext

首先,我们知道 bean 实例化的几种方式分别是:

  1. 普通构造函数
  2. 静态工厂
  3. 实例工厂

IoC即 控制反转,在对象层级嵌套的情况下,IoC帮助我们在创建对象时,自动创建依赖的对象并注入,IoC容器创建对象是自底向上的:

Spring IoC 的简易实现 - 图1

这篇博客很好地解释了 IoC 和 DI:https://www.iteye.com/blog/jinnianshilongnian-1413846

下面,我们逐步实现一个简易的 IoC 容器


Step 1 最基本的容器

  1. 首先创建了一个 BeanDefinition 类来封装 bean 对象和元信息
  1. public class BeanDefinition {
  2. private Object bean;
  3. public BeanDefinition(Object bean) {
  4. this.bean = bean;
  5. }
  6. public Object getBean() {
  7. return bean;
  8. }
  9. }
  1. 创建 BeanFactory 负责保存 bean 对象到容器中(这里用到的是Hashmap保存),并且可以通过 name 来获取保存的 BeanDefinition
  1. public class BeanFactory {
  2. private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
  3. public Object getBean(String name) {
  4. return beanDefinitionMap.get(name).getBean();
  5. }
  6. public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
  7. beanDefinitionMap.put(name, beanDefinition);
  8. }
  9. }
  1. 测试代码
  1. // 1.初始化beanfactory
  2. BeanFactory beanFactory = new BeanFactory();
  3. // 2.注入bean
  4. BeanDefinition beanDefinition = new BeanDefinition(new HelloWorldService());
  5. beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);
  6. // 3.获取bean
  7. HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
  8. helloWorldService.helloWorld();

Step 2 将 bean 创建放入工厂

在上一步中,工厂类的功能只是保存和获取 BeanDefinition,这一步中,将 bean 的创建也放到工厂中完成,在这一步中,把简单工厂模式改造成了提取了接口的抽象工厂模式

  1. 改造 BeanDefinition
  1. public class BeanDefinition {
  2. @Getter
  3. @Setter
  4. private Object bean;
  5. @Getter
  6. @Setter
  7. private Class beanClass;
  8. @Getter
  9. private String beanClassName;
  10. public void setBeanClassName(String beanClassName) {
  11. this.beanClassName = beanClassName;
  12. try {
  13. this.beanClass = Class.forName(beanClassName);
  14. } catch (ClassNotFoundException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. }
  1. 首先,定义工厂接口
  1. public interface BeanFactory {
  2. Object getBean(String name);
  3. void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception;
  4. }
  1. 创建抽象工厂,并在工厂类中创建 bean(doCreateBean)
  1. public abstract class AbstractBeanFactory implements BeanFactory {
  2. private Map<String, BeanDefinition> beanMap = new ConcurrentHashMap<>();
  3. @Override
  4. public Object getBean(String name) {
  5. return beanMap.get(name).getBean();
  6. }
  7. @Override
  8. public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
  9. Object bean = doCreateBean(beanDefinition);
  10. beanDefinition.setBean(bean);
  11. beanMap.put(name, beanDefinition);
  12. }
  13. /**
  14. * 初始化 Bean
  15. *
  16. * @param beanDefinition
  17. * @return
  18. */
  19. protected abstract Object doCreateBean(BeanDefinition beanDefinition);
  20. }
  1. 具体的工厂类实现,利用反射创建 bean
  1. public class AutowireCapableBeanFactory extends AbstractBeanFactory {
  2. @Override
  3. protected Object doCreateBean(BeanDefinition beanDefinition) {
  4. try {
  5. return beanDefinition.getBeanClass().newInstance();
  6. } catch (InstantiationException e) {
  7. e.printStackTrace();
  8. } catch (IllegalAccessException e) {
  9. e.printStackTrace();
  10. }
  11. return null;
  12. }
  13. }
  1. 测试代码
  1. // 1.初始化BeanFactory
  2. BeanFactory beanFactory = new AutowireCapableBeanFactory();
  3. // 2.注入Bean
  4. BeanDefinition beanDefinition = new BeanDefinition();
  5. beanDefinition.setBeanClassName("wu.easyioc.HelloWorldService");
  6. beanFactory.registerBeanDefinition("helloworld", beanDefinition);
  7. // 3.获取Bean
  8. HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloworld");
  9. helloWorldService.helloworld();

Step 3 为 bean 注入属性

上面的步骤中,无法为 bean 注入属性,下面是在创建 bean 的时候,利用反射来为 bean 注入属性

  1. 创建 PropertyValue 来保存需要注入的字段信息
  1. public class PropertyValue {
  2. @Getter
  3. private final String name;
  4. @Getter
  5. private final Object value;
  6. public PropertyValue(String name, Object value) {
  7. this.name = name;
  8. this.value = value;
  9. }
  10. }
  1. 创建 PropertyValues 来保存多个 PropertyValue ,实际上就是一个 List
  1. public class PropertyValues {
  2. private final List<PropertyValue> propertyValueList = new ArrayList<>();
  3. public PropertyValues() {
  4. }
  5. public void addPropertyValue(PropertyValue propertyValue) {
  6. this.propertyValueList.add(propertyValue);
  7. }
  8. public List<PropertyValue> getPropertyValues() {
  9. return this.propertyValueList;
  10. }
  11. }
  1. 改造后的 AutowireCapableBeanFactory ,根据 Class 对象创建对象实例后,再把 PropertyValues 中的属性名 - 属性值 循环注入到对象中
  1. public class AutowireCapableBeanFactory extends AbstractBeanFactory {
  2. @Override
  3. protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception {
  4. Object bean = createBeanInstance(beanDefinition);
  5. applyPropertyValues(bean, beanDefinition);
  6. return bean;
  7. }
  8. protected Object createBeanInstance(BeanDefinition beanDefinition) throws Exception {
  9. return beanDefinition.getBeanClass().newInstance();
  10. }
  11. protected void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception {
  12. for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) {
  13. Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
  14. declaredField.setAccessible(true);
  15. declaredField.set(bean, propertyValue.getValue());
  16. }
  17. }
  18. }
  1. HelloService
  1. public class HelloWorldService {
  2. String text; // 需要注入值的属性
  3. public void helloworld() {
  4. System.out.println(text);
  5. }
  6. }
  1. 测试代码
  1. // 1.初始化BeanFactory
  2. BeanFactory beanFactory = new AutowireCapableBeanFactory();
  3. // 2.注入Bean
  4. BeanDefinition beanDefinition = new BeanDefinition();
  5. beanDefinition.setBeanClassName("wu.easyioc.HelloWorldService");
  6. // 3.设置属性
  7. PropertyValues propertyValues = new PropertyValues();
  8. propertyValues.addPropertyValue(new PropertyValue("text","helloworld from bot..."));
  9. beanDefinition.setPropertyValues(propertyValues);
  10. // 4.生成Bean
  11. beanFactory.registerBeanDefinition("helloworld", beanDefinition);
  12. // 5.获取Bean
  13. HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloworld");
  14. helloWorldService.helloworld();

Step 4 读取 xml 配置来初始化 bean

在步骤3中需要手动初始化 bean,下面我们通过xml配置文件的方式来自动注入 bean 信息

  1. 创建 Resource 接口,负责获取内部资源,以输入流的形式返回
  1. public interface Resource {
  2. InputStream getInputStream() throws IOException;
  3. }
  1. 创建 UrlResource 类,实现 Resource 接口,传入一个 URL 来获取输入流
  1. public class UrlResource implements Resource {
  2. private final URL url;
  3. public UrlResource(URL url) {
  4. this.url = url;
  5. }
  6. @Override
  7. public InputStream getInputStream() throws IOException{
  8. URLConnection urlConnection = url.openConnection();
  9. urlConnection.connect();
  10. return urlConnection.getInputStream();
  11. }
  12. }
  1. ResourceLoader 根据 Class对象 来获取指定路径的 URL 对象,并注入 UrlResource
  1. public class ResourceLoader {
  2. public Resource getResource(String location) {
  3. // 获取Class对象 -> 获取类装载器 -> 根据路径获取URL对象
  4. URL resource = this.getClass().getClassLoader().getResource(location);
  5. return new UrlResource(resource);
  6. }
  7. }
  1. 创建 Bean 读取接口 BeanDefinitionReader ,负责从xml配置文件中读取 bean 信息
  1. public interface BeanDefinitionReader {
  2. void loadBeanDefinitions(String location) throws Exception;
  3. }
  1. 实现接口 BeanDefinitionReader 的抽象类 AbstractBeanDefinitionReader ,内部的 registry 集合保存了需要注册的 bean 信息
  1. public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {
  2. // 保存需要注册的Bean的容器
  3. private Map<String,BeanDefinition> registry;
  4. private ResourceLoader resourceLoader;
  5. protected AbstractBeanDefinitionReader(ResourceLoader resourceLoader) {
  6. this.registry = new HashMap<String, BeanDefinition>();
  7. this.resourceLoader = resourceLoader;
  8. }
  9. public Map<String, BeanDefinition> getRegistry() {
  10. return registry;
  11. }
  12. public ResourceLoader getResourceLoader() {
  13. return resourceLoader;
  14. }
  15. }
  1. 自动初始化 bean 信息的实现类 XmlBeanDefinitionReader ,功能是将 xml 配置文件中配置的 bean 初始化信息存入到内部的 registy 集合中
  1. public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
  2. public XmlBeanDefinitionReader(ResourceLoader resourceLoader) {
  3. super(resourceLoader);
  4. }
  5. @Override
  6. public void loadBeanDefinitions(String location) throws Exception {
  7. // 获取输入流,输入流是为了输入xml资源
  8. InputStream inputStream = getResourceLoader().getResource(location).getInputStream();
  9. doLoadBeanDefinitions(inputStream);
  10. }
  11. protected void doLoadBeanDefinitions(InputStream inputStream) throws Exception {
  12. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  13. DocumentBuilder docBuilder = factory.newDocumentBuilder();
  14. Document doc = docBuilder.parse(inputStream);
  15. // 解析bean
  16. registerBeanDefinitions(doc);
  17. inputStream.close();
  18. }
  19. public void registerBeanDefinitions(Document doc) {
  20. Element root = doc.getDocumentElement();
  21. parseBeanDefinitions(root);
  22. }
  23. protected void parseBeanDefinitions(Element root) {
  24. NodeList nl = root.getChildNodes();
  25. for (int i = 0; i < nl.getLength(); i++) {
  26. Node node = nl.item(i);
  27. if (node instanceof Element) {
  28. Element ele = (Element) node;
  29. processBeanDefinition(ele);
  30. }
  31. }
  32. }
  33. protected void processBeanDefinition(Element ele) {
  34. String name = ele.getAttribute("name");
  35. String className = ele.getAttribute("class");
  36. BeanDefinition beanDefinition = new BeanDefinition();
  37. processProperty(ele,beanDefinition);
  38. beanDefinition.setBeanClassName(className);
  39. getRegistry().put(name, beanDefinition);
  40. }
  41. private void processProperty(Element ele,BeanDefinition beanDefinition) {
  42. NodeList propertyNode = ele.getElementsByTagName("property");
  43. for (int i = 0; i < propertyNode.getLength(); i++) {
  44. Node node = propertyNode.item(i);
  45. if (node instanceof Element) {
  46. Element propertyEle = (Element) node;
  47. String name = propertyEle.getAttribute("name");
  48. String value = propertyEle.getAttribute("value");
  49. beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name,value));
  50. }
  51. }
  52. }
  53. }
  1. 测试代码
  1. // 1.读取配置
  2. XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
  3. xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
  4. // 2.初始化BeanFactory并注册bean
  5. BeanFactory beanFactory = new AutowireCapableBeanFactory();
  6. for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
  7. beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
  8. }
  9. // 3.获取bean
  10. HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
  11. helloWorldService.helloWorld();
  1. 大致流程图如下:

Spring IoC 的简易实现 - 图2

Step 5 为 bean 注入 bean

前面的步骤3中,利用反射完成了属性的注入,但是无法为 bean 注入 bean,下面完成为 bean 注入 bean 的实现

  1. 创建 BeanReference 类,表示这个属性是对另外一个bean的引用
  1. @Data
  2. @AllArgsConstructor
  3. public class BeanReference {
  4. private String name;
  5. private Object bean;
  6. }
  1. 在处理xml配置文件的时候,判断xml中需要注入的是 ref 还是 value ,对不同的属性进行处理,所以需要改造处理的方法,对 ref 封装成 BeanRefrence
  1. private void processProperty(Element ele, BeanDefinition beanDefinition) {
  2. NodeList propertyNode = ele.getElementsByTagName("property");
  3. for (int i = 0; i < propertyNode.getLength(); i++) {
  4. Node node = propertyNode.item(i);
  5. if (node instanceof Element) {
  6. Element propertyEle = (Element) node;
  7. String name = propertyEle.getAttribute("name");
  8. String value = propertyEle.getAttribute("value");
  9. if (value != null && value.length() > 0) {
  10. beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value));
  11. } else {
  12. String ref = propertyEle.getAttribute("ref");
  13. if (ref == null || ref.length() == 0) {
  14. throw new IllegalArgumentException("Configuration problem: <property> element for property '"
  15. + name + "' must specify a ref or value");
  16. }
  17. BeanReference beanReference = new BeanReference(ref);
  18. beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, beanReference));
  19. }
  20. }
  21. }
  22. }
  1. AutowireCapableBeanFactory 中的注入属性方法中,判断注入属性是否为 BeanReference 类,如果是则调用 getBean(...) 方法获取 bean 对象并注入
  1. protected void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception {
  2. for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) {
  3. // getDeclaredField 获取一个类的指定成员变量
  4. Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
  5. // 需要对 private 的成员变量进行set操作,必须setAccessible(true)
  6. declaredField.setAccessible(true);
  7. Object value = propertyValue.getValue();
  8. if (value instanceof BeanReference) {
  9. BeanReference beanReference = (BeanReference) value;
  10. value = getBean(beanReference.getName());
  11. }
  12. declaredField.set(bean, value);
  13. }
  14. }

基于以上修改,已经可以对普通的bean依赖进行处理,但是遇到循环依赖的情况,会造成死锁。解决的方案是改造为单例的注入,先将Bean对象实例化后在设置对象属性,而不是在实例化的时候就注入属性。

  1. 在注册bean的方法 registerBeanDefinition 中先不 createBean ,而是新建一个容器存放需要实例的 beanName
  1. // 旧方法:
  2. public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {
  3. Object bean = doCreateBean(beanDefinition);
  4. beanDefinition.setBean(bean);
  5. beanMap.put(name, beanDefinition);
  6. }
  7. // 新的方法:
  8. public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {
  9. beanDefinitionMap.put(name, beanDefinition);
  10. beanDefinitionNames.add(name); // List<String> beanDefinitionNames
  11. }
  1. 创建 preInstantiateSingletons 方法,在调用 getBean 前,先将 beanDefinitionNames 容器中的实例初始化
  1. public void preInstantiateSingletons() throws Exception {
  2. for (Iterator it = this.beanDefinitionNames.iterator(); it.hasNext();) {
  3. String beanName = (String) it.next();
  4. getBean(beanName); // 初始化bean,单例形式
  5. }
  6. }
  1. getBean 方法的重写,对 bean 进行非空判断,为空则调用 doCreateBean 方法,如果不为空则返回 bean(单例)
  1. public Object getBean(String name) throws Exception {
  2. BeanDefinition beanDefinition = beanDefinitionMap.get(name);
  3. if (beanDefinition == null) {
  4. throw new IllegalArgumentException("No bean named " + name + " is defined");
  5. }
  6. Object bean = beanDefinition.getBean();
  7. if (bean == null) {
  8. bean = doCreateBean(beanDefinition);
  9. }
  10. return bean;
  11. }
  1. 引入两个Service,并循环依赖,测试代码
  1. // 1.读取配置
  2. XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
  3. xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
  4. // 2.初始化BeanFactory并注册bean
  5. AbstractBeanFactory beanFactory = new AutowireCapableBeanFactory();
  6. for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
  7. beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
  8. }
  9. // 3.初始化bean,当然不需要这一步也是可以的,因为在下面的getBean(...)方法中,也有对null的Bean判断并创建,相当于 Lazy-init
  10. beanFactory.preInstantiateSingletons();
  11. // 4.获取bean
  12. HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
  13. helloWorldService.helloWorld();

Step 6 引入 ApplicationContext

上面已经完成了简单的 IoC 功能,但是使用起来还是比较繁琐,所以引入了 ApplicationContext 来对过程进行进一步地封装

  1. 创建 ApplicationContext 接口
  1. public interface ApplicationContext {}
  1. 抽象实现 ApplicationContext 接口的抽象类 AbstractApplicationContext ,这里主要有两个方法:refresh(...) 加载xml配置方法 和 getBean(...) 获取bean方法
  1. public class AbstractApplicationContext implements ApplicationContext {
  2. protected AbstractBeanFactory beanFactory;
  3. public AbstractApplicationContext(AbstractBeanFactory beanFactory) {
  4. this.beanFactory = beanFactory;
  5. }
  6. public void refresh() throws Exception {}
  7. @Override
  8. public Object getBean(String name) throws Exception {
  9. return beanFactory.getBean(name);
  10. }
  11. }
  1. 具体的实现类 ClassPathXmlApplicationContext ,主要作用是实现了 refresh(...) 方法,该类主要用于加载指定路径的 xml 配置文件并保存
  1. public class ClassPathXmlApplicationContext extends AbstractApplicationContext {
  2. private String configLocation;
  3. public ClassPathXmlApplicationContext(String configLocation) throws Exception {
  4. this(configLocation, new AutowireCapableBeanFactory());
  5. }
  6. public ClassPathXmlApplicationContext(String configLocation, AbstractBeanFactory beanFactory) throws Exception {
  7. super(beanFactory);
  8. this.configLocation = configLocation;
  9. refresh();
  10. }
  11. @Override
  12. public void refresh() throws Exception {
  13. XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
  14. xmlBeanDefinitionReader.loadBeanDefinitions(configLocation);
  15. for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
  16. beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
  17. }
  18. }
  19. }
  1. 测试代码
  1. // 1. 载入 xml 配置
  2. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
  3. // 2. 获取 bean
  4. HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService");
  5. // 3. 执行方法
  6. helloWorldService.helloWorld();

总结

以上实现了一个简易的 IoC 功能,在以上设计中,大量用到了 模板方法模式抽象工厂模式 ,后续可以基于此实现:1. 基于注解的 IoC 功能 2. 简易的 AOP 功能

GitHub

https://github.com/iamwumaixing/easy-spring