Simple IOC 容器实现-基于XML配置

前端之家收集整理的这篇文章主要介绍了Simple IOC 容器实现-基于XML配置前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

概述

IOC(Inversion of Control)“控制反转”,不过更流行的叫法是“依赖注入”(DI - Dependency Injection)。

什么是“控制反转”呢?其实就是将控制权(创建对象和对象之间的依赖关系的权利)交给Spring容器。以前我们写代码的需要某个对象的时候直接使用 new XXXImpl();,有了Spring IOC容器之后,它负责对象的创建和依赖注入,当我们需要某个对象时直接跟Spring IOC容器要就好了。

IOC 听起来很高大上,其实实现起来并不复杂。本文主要介绍基于XML配置的方式来实现一个IOC容器,后面会有单独的一章介绍如何通过注解的方式来实现IOC容器。

用法

具体用法与Spring IOC类似,如下:

1、beans.xml 配置文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
  3.  
  4. <bean id="userDao" class="com.ricky.ioc.sample.dao.UserDaoImpl" scope="singleton" init-method="init" ></bean>
  5.  
  6. <bean id="userService" class="com.ricky.ioc.sample.service.UserServiceImpl">
  7. <property name="userDao" ref="userDao"></property>
  8. </bean>
  9.  
  10. <bean id="userController" class="com.ricky.ioc.sample.controller.UserController">
  11. <property name="userService" ref="userService"></property>
  12. </bean>
  13. </beans>

2、添加maven依赖

  1. <dependency>
  2. <groupId>com.ricky.framework</groupId>
  3. <artifactId>ioc</artifactId>
  4. <version>1.0.0</version>
  5. </dependency>

3、加载bean配置文件

  1. ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
  2.  
  3. //通过id获取Bean
  4. UserController userController =(UserController)ctx.getBean("userController");
  5. userController.login("ricky","123");
  6.  
  7. //通过Class获取Bean
  8. UserService userService = ctx.getBean(UserService.class);
  9. System.out.println(userService);
  10. userService.login("ricky","abc");
  11.  
  12. ctx.close();

运行结果如下:

UserController login name->ricky,password->123
UserServiceImpl login name->ricky,password->123
UserDaoImpl find name->ricky
com.ricky.ioc.sample.service.UserServiceImpl@214c265e
UserServiceImpl login name->ricky,password->abc
UserDaoImpl find name->ricky
container close…

具体实现

思路:
解析beans.xml获取Bean列表以及相互之间的依赖关系,然后通过反射技术构造出Bean实例,并根据Bean之间的依赖关系进行Bean装配。

首先,看看ApplicationContext类,代码如下:

  1. package com.ricky.framework.ioc;
  2.  
  3. import java.beans.IntrospectionException;
  4. import java.beans.Introspector;
  5. import java.beans.PropertyDescriptor;
  6. import java.lang.reflect.InvocationTargetException;
  7. import java.lang.reflect.Method;
  8.  
  9. import org.apache.commons.lang3.StringUtils;
  10.  
  11. import com.ricky.framework.ioc.model.BeanDefinition;
  12. import com.ricky.framework.ioc.model.PropertyDefinition;
  13. import com.ricky.framework.ioc.util.ReflectionUtils;
  14.  
  15. public abstract class ApplicationContext {
  16.  
  17. public abstract Object getBean(String id);
  18.  
  19. public abstract <T> T getBean(Class<T> clazz);
  20.  
  21. public abstract void close();
  22.  
  23. protected abstract BeanDefinition getBeanDefinition(String id);
  24.  
  25. protected Object createBean(BeanDefinition bd) {
  26.  
  27. try {
  28. Object bean = ReflectionUtils.newInstance(bd.getClassName());
  29. if(StringUtils.isNotEmpty(bd.getInitMethodName())){
  30. ReflectionUtils.invokeMethod(bean,bd.getInitMethodName());
  31. }
  32.  
  33. return bean;
  34. } catch (ClassNotFoundException | InstantiationException
  35. | IllegalAccessException | IllegalArgumentException
  36. | InvocationTargetException | NoSuchMethodException e) {
  37. throw new RuntimeException("create bean error,class->"+bd.getClassName(),e);
  38. }
  39. }
  40.  
  41. protected void injectBeanProperties(Object bean,BeanDefinition beanDefinition){
  42.  
  43. try {
  44. PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
  45.  
  46. for(PropertyDefinition propertyDefinition : beanDefinition.getProperties()){
  47.  
  48. for(PropertyDescriptor propertyDescriptor:ps){
  49.  
  50. if(propertyDescriptor.getName().equals(propertyDefinition.getName())){
  51.  
  52. Method setter = propertyDescriptor.getWriteMethod();
  53. setter.setAccessible(true);
  54.  
  55. setter.invoke(bean,getBean(propertyDefinition.getRef()));
  56. }
  57. }
  58. }
  59. } catch (SecurityException | IllegalAccessException
  60. | IllegalArgumentException | InvocationTargetException
  61. | IntrospectionException e) {
  62. throw new RuntimeException("inject bean properties error",e);
  63. }
  64. }
  65. }

在这涉及到两个关键类:BeanDefinition 和 PropertyDefinition,它们分别是用来描述 javabean的定义和javabean 属性的定义,一个BeanDefinition 可以有1个或多个PropertyDefinition,它们之间是1:N的关系。代码如下:

BeanDefinition.java

  1. package com.ricky.framework.ioc.model;
  2.  
  3. import java.util.List;
  4.  
  5. public class BeanDefinition {
  6. private String id;
  7. private String className;
  8. private String scope; //singleton|prototype
  9. private String initMethodName;
  10. private List<PropertyDefinition> properties;
  11.  
  12. public BeanDefinition(String id,String className) {
  13. this.id = id;
  14. this.className = className;
  15. }
  16.  
  17. public String getId() {
  18. return id;
  19. }
  20. public void setId(String id) {
  21. this.id = id;
  22. }
  23. public String getClassName() {
  24. return className;
  25. }
  26. public void setClassName(String className) {
  27. this.className = className;
  28. }
  29. public String getScope() {
  30. return scope;
  31. }
  32. public void setScope(String scope) {
  33. this.scope = scope;
  34. }
  35. public String getInitMethodName() {
  36. return initMethodName;
  37. }
  38. public void setInitMethodName(String initMethodName) {
  39. this.initMethodName = initMethodName;
  40. }
  41. public List<PropertyDefinition> getProperties() {
  42. return properties;
  43. }
  44. public void setProperties(List<PropertyDefinition> properties) {
  45. this.properties = properties;
  46. }
  47.  
  48. @Override
  49. public String toString() {
  50. return "BeanDefinition [id=" + id + ",className=" + className
  51. + ",scope=" + scope + ",initMethodName=" + initMethodName
  52. + ",properties=" + properties + "]";
  53. }
  54.  
  55. }

PropertyDefinition.java

  1. package com.ricky.framework.ioc.model;
  2.  
  3. public class PropertyDefinition {
  4. private String name;
  5. private String ref;
  6.  
  7. public PropertyDefinition(String name,String ref) {
  8. this.name = name;
  9. this.ref = ref;
  10. }
  11. public String getName() {
  12. return name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public String getRef() {
  18. return ref;
  19. }
  20. public void setRef(String ref) {
  21. this.ref = ref;
  22. }
  23.  
  24. @Override
  25. public String toString() {
  26. return "PropertyDefinition [name=" + name + ",ref=" + ref + "]";
  27. }
  28.  
  29. }

2、接下来是ClassPathXmlApplicationContext类,代码如下:

  1. package com.ricky.framework.ioc;
  2.  
  3. import java.io.FileNotFoundException;
  4. import java.util.HashMap;
  5. import java.util.List;
  6. import java.util.Map;
  7.  
  8. import org.apache.commons.lang3.StringUtils;
  9. import org.dom4j.DocumentException;
  10.  
  11. import com.ricky.framework.ioc.model.BeanDefinition;
  12. import com.ricky.framework.ioc.parser.BeanXmlConfigParser;
  13. import com.ricky.framework.ioc.util.BeanScope;
  14. import com.ricky.framework.ioc.util.ReflectionUtils;
  15.  
  16. public class ClassPathXmlApplicationContext extends ApplicationContext {
  17.  
  18. private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<String,BeanDefinition>();
  19. protected Map<String,Object> beanInstanceMap = new HashMap<String,Object>();
  20.  
  21. public ClassPathXmlApplicationContext(String xmlFilePath) {
  22.  
  23. System.out.println("****************container init begin****************");
  24.  
  25. readXml(xmlFilePath);
  26. initBeans();
  27. injectBeans();
  28.  
  29. System.out.println("****************container init end****************");
  30. }
  31.  
  32. private void readXml(String xmlFilePath) {
  33.  
  34. BeanXmlConfigParser beanXmlConfigParser = new BeanXmlConfigParser();
  35.  
  36. List<BeanDefinition> bean_def_list = null;
  37. try {
  38. bean_def_list = beanXmlConfigParser.parse(xmlFilePath);
  39. } catch (FileNotFoundException e) {
  40. throw new RuntimeException("not found bean xml,file->"+xmlFilePath,e);
  41. } catch (DocumentException e) {
  42. throw new RuntimeException("bean xml format error,e);
  43. }
  44. for (BeanDefinition beanDefinition : bean_def_list) {
  45. if(StringUtils.isEmpty(beanDefinition.getId()) || StringUtils.isEmpty(beanDefinition.getClassName())){
  46. throw new IllegalArgumentException("bean definition is empty!");
  47. }
  48. if (beanDefinitionMap.containsKey(beanDefinition.getId())) {
  49. throw new IllegalArgumentException(
  50. "duplicated bean id,id->"
  51. + beanDefinition.getId());
  52. }
  53. beanDefinitionMap.put(beanDefinition.getId(),beanDefinition);
  54. }
  55. }
  56. private void initBeans() {
  57. for (Map.Entry<String,BeanDefinition> me : beanDefinitionMap.entrySet()) {
  58. BeanDefinition bd = me.getValue();
  59. if(StringUtils.isEmpty(bd.getScope()) || bd.getScope().equals(BeanScope.SINGLETON)){
  60. try {
  61. Object bean = createBean(bd);
  62. beanInstanceMap.put(bd.getId(),bean);
  63. } catch (Exception e) {
  64. throw new IllegalArgumentException("create bean error,class->"+bd.getClassName(),e);
  65. }
  66. }
  67. }
  68. }
  69. private void injectBeans() {
  70. for (Map.Entry<String,BeanDefinition> me : beanDefinitionMap.entrySet()) {
  71. BeanDefinition beanDefinition = me.getValue();
  72. //判断有没有注入属性
  73. if (beanDefinition.getProperties() != null && beanDefinition.getProperties().size()>0) {
  74. Object bean = beanInstanceMap.get(beanDefinition.getId());
  75. try {
  76. injectBeanProperties(bean,beanDefinition);
  77. } catch (Exception e) {
  78. e.printStackTrace();
  79. }
  80. }
  81. }
  82. }
  83. @Override
  84. public Object getBean(String id) {
  85. // System.out.println("get bean by id:"+id);
  86. if (StringUtils.isEmpty(id)) {
  87. return null;
  88. }
  89. if (beanDefinitionMap.containsKey(id)) {
  90. BeanDefinition bd = beanDefinitionMap.get(id);
  91. if(StringUtils.isEmpty(bd.getScope()) || bd.getScope().equals(BeanScope.SINGLETON)){
  92. return beanInstanceMap.get(id);
  93. }
  94. Object bean = null;
  95. try {
  96. bean = createBean(bd);
  97. injectBeanProperties(bean,bd);
  98. beanInstanceMap.put(bd.getId(),bean);
  99. } catch (Exception e) {
  100. e.printStackTrace();
  101. }
  102. return bean;
  103. }
  104. throw new IllegalArgumentException("unknown bean,id->" + id);
  105. }
  106. @SuppressWarnings("unchecked")
  107. @Override
  108. public <T> T getBean(Class<T> clazz) {
  109. // System.out.println("get bean by type:"+clazz.getName());
  110. for(Map.Entry<String,BeanDefinition> me : beanDefinitionMap.entrySet()){
  111. BeanDefinition bd = me.getValue();
  112. Class<?> beanClass = null;
  113. try {
  114. beanClass = ReflectionUtils.loadClass(bd.getClassName());
  115. } catch (ClassNotFoundException e) {
  116. e.printStackTrace();
  117. }
  118. if(beanClass!=null && clazz.isAssignableFrom(beanClass)){
  119. // System.out.println("find bean by type,class->"+clazz.getName());
  120. return (T) getBean(bd.getId());
  121. }
  122. }
  123. return null;
  124. }
  125. @Override
  126. protected BeanDefinition getBeanDefinition(String id) {
  127. return beanDefinitionMap.get(id);
  128. }
  129. @Override
  130. public void close() {
  131. System.out.println("container close...");
  132. // release resource
  133. beanDefinitionMap.clear();
  134. beanDefinitionMap = null;
  135. beanInstanceMap.clear();
  136. beanInstanceMap = null;
  137. }
  138. }

在ClassPathXmlApplicationContext类中,主要负责三大功能:解析XML配置文件、通过反射构建Bean实例以及对Bean进行装配。


小结

以上所有代码均已上传GitHub上,欢迎大家fork。另外由于时间比较仓促,代码设计上有不合理的地方还请包涵,后面会抽时间对代码进行重构。

猜你在找的XML相关文章