注:这是一个“重复造轮子”的过程,本篇介绍如何通过XML配置文件实现IOC。

需求:

以往在我们写服务器的时候,业务逻辑层(Biz,有些地方叫Service)通常会有个获取Dao的需求,通常情况是从DaoFactory中调用Get方法,获取所需的Dao。而现在我想改变一下想法,按照IOC这种思路,主动地给Biz注入对应的Dao。

Xml文件读取

Xml配置

Hibernate的配置文件似乎太多了,先写一个更加简单的Xml文件,取名为Student.xml,这个Student.xml没什么意义,先测试一下,将一个Xml文件解析一下,在控制台原原本本地输出,主要就通过Document对象的方法进行解析 。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans>
  3. <Student id="001">
  4. <name>Tom</name>
  5. <age>31</age>
  6. </Student>
  7. <Student id="002">
  8. <name>Jom</name>
  9. <age>15</age>
  10. </Student>
  11. </beans>

解析Xml文件

Java代码,运行结果就不贴出来了,就是完完整整地把上面的Xml配置内容在控制台输出来,大家可以注意一下我们可能用到的方法。

  1. public class XmlLoad {
  2. public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
  3. InputStream inputStream = XmlLoad.class.getResourceAsStream("/Student.xml");
  4. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  5. DocumentBuilder builder = factory.newDocumentBuilder();
  6. Document document = builder.parse(inputStream);
  7. Element root = document.getDocumentElement();
  8. printNode(root);
  9. }
  10. private static void printNode(Node node) {
  11. short nodeType = node.getNodeType();
  12. switch (nodeType) {
  13. case Node.TEXT_NODE:
  14. System.out.print(node.getNodeValue());
  15. break;
  16. case Node.ELEMENT_NODE:
  17. Element element = (Element) node;
  18. String tagName = element.getTagName();
  19. System.out.print("<");
  20. System.out.print(tagName);
  21. NamedNodeMap attrs = element.getAttributes();
  22. for (int i = 0; i < attrs.getLength(); i++) {
  23. Attr attr = (Attr) attrs.item(i);
  24. printNode(attr);
  25. }
  26. System.out.print(">");
  27. NodeList childNodeList = element.getChildNodes();
  28. for (int i = 0; i < childNodeList.getLength(); i++) {
  29. Node childNode = childNodeList.item(i);
  30. printNode(childNode);
  31. }
  32. System.out.print("</" + tagName + ">");
  33. break;
  34. case Node.ATTRIBUTE_NODE:
  35. Attr attr = (Attr) node;
  36. String name = attr.getName();
  37. String value = attr.getValue();
  38. System.out.print(" " + name + "=" + value);
  39. break;
  40. default:
  41. break;
  42. }
  43. }
  44. }

Xml配置文件转JavaBean(Set方式注入)

Xml配置

注:这里使用的是Set方法注入,其实也可以通过构造函数注入,直接通过字段强行注入也可以实现。

下面开始真正的实战,首先,写一个Xml文件,取名为beans.xml:

  • beans是最外层的标签;
  • 其中bean标签,class属性值“com.using.bean.Student“,指的是Student的全类名;
  • 然后property标签指明了各个属性值,比如:Student类的Id属性的值是12,这些都是和Javabean一一对应的。
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans>
  3. <bean class="com.using.bean.Student">
  4. <property name="id" value="12a"></property>
  5. <property name="name" value="2a"></property>
  6. <property name="age" value="31a"></property>
  7. </bean>
  8. <bean class="com.using.bean.Student">
  9. <property name="id" value="12b"></property>
  10. <property name="name" value="2b"></property>
  11. <property name="age" value="31b"></property>
  12. </bean>
  13. </beans>

然后是对应的Javabean,尤其是Set方法,一定要补齐,因为下面的Demo就是通过Set方法注入值的。

  1. public class Student {
  2. private String id;
  3. private String name;
  4. private String age;
  5. //方法补齐
  6. `

解析思路:

其实思路就是解析Xml布局文件,然后读取属性值,最后通过Java反射机制,将配置的信息反射成一个对象,至于说效率问题,那肯定会比较低,想想就知道,代码都多写了好多行,还要加文件流的读取,能不慢么?不过无反射,不框架。

其中全类名”com.using.bean.Student“,干什么用?相信你们肯定用过,在写数据库连接的时候这样写:Class.forName(“com.mysql.jdbc.Driver”) ,它的作用是装载数据库驱动,而我们要通过它实现这样一个功能,通过字符串来创建实例,具体的使用方法如下:
Object obj=Class.forName(fullClassName).newInstance();

Set方式注入的关键代码如下,先找到Set方法的代码,然后通过invoke()方法注入:

  1. if (("set" + name.substring(0, 1).toUpperCase() + name.substring(1)).equals(methodName)) {
  2. try {
  3. //参数注入
  4. method.invoke(instance, value);
  5. } catch (Exception e) {
  6. e.printStackTrace();
  7. }
  8. }

解析Xml文件

  1. public class ClassFormat {
  2. public static void main(String[] args) {
  3. parseBeanFile("/beans.xml");
  4. }
  5. public static void parseBeanFile(String beanPath) {
  6. try {
  7. InputStream inputStream = ClassFormat.class.getResourceAsStream(beanPath);
  8. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  9. DocumentBuilder builder = factory.newDocumentBuilder();
  10. Document document = builder.parse(inputStream);
  11. NodeList stuNodeList = document.getElementsByTagName("bean");
  12. for (int i = 0; i < stuNodeList.getLength(); i++) {
  13. Element stuElement = (Element) stuNodeList.item(i);
  14. String className = stuElement.getAttribute("class");
  15. Object instance = createInstance(className);
  16. parseBeanNodes(instance, stuElement);
  17. System.out.println(instance.toString());
  18. }
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. /**
  24. * 根据全类名反射出对象
  25. */
  26. public static Object createInstance(String fullClassName) {
  27. try {
  28. return Class.forName(fullClassName).newInstance();
  29. } catch (Exception e) {
  30. e.printStackTrace();
  31. }
  32. return null;
  33. }
  34. /**
  35. * 解析子节点
  36. */
  37. public static void parseBeanNodes(Object instance, Element stuElement) {
  38. NodeList propertyNodeList = stuElement.getChildNodes();
  39. for (int j = 0; j < propertyNodeList.getLength(); j++) {
  40. Node propertyNode = propertyNodeList.item(j);
  41. if (propertyNode.getNodeType() == Node.ELEMENT_NODE && "property".equals(propertyNode.getNodeName())) {
  42. Element propertyElement = (Element) propertyNode;
  43. String name = propertyElement.getAttribute("name");
  44. String value = propertyElement.getAttribute("value");
  45. //可以补充type属性,然后反射成Javabean的字段类型,逻辑会多一些
  46. setProperty(instance, name, value);
  47. }
  48. }
  49. }
  50. /**
  51. * Set方法注入
  52. */
  53. public static void setProperty(Object instance, String name, String value) {
  54. Class<?> clazz = instance.getClass();
  55. Method[] methods = clazz.getMethods();
  56. for (Method method : methods) {
  57. String methodName = method.getName();
  58. if (("set" + name.substring(0, 1).toUpperCase() + name.substring(1)).equals(methodName)) {
  59. try {
  60. method.invoke(instance, value);
  61. } catch (Exception e) {
  62. e.printStackTrace();
  63. }
  64. }
  65. }
  66. }
  67. }

控制台输出:
Student [id=12a, name=2a, age=31a]
Student [id=12b, name=2b, age=31b]

最终封装,往Biz中注入Dao

思路分析

  1. 通过DaoFactory这个类实现了Dao和Biz的初始化,这和上面实例化Student是一样的,初始化完成就放到beanMap中;
  2. 然后通过解析Xml文件,了解到Biz要引用哪个Dao;
  3. 配置文件中的name属性指名是哪个Set方法,ref属性指明是哪一个Dao;
  4. 最后通过反射机制,从Set方法注入Dao。

使用场景

这里只给出思路,具体使用,在之后的文章会用到。

在我们写后台的时候,比如说你有一张User表,你可能就会去写UserDao,而这个UserDao只需要一个实例就够了,没必要创建特别多个,你可能会把它写成单例,而且是很希望程序启动的时候就创建好。

这样就可以设计一个优先级别高的Servlet,然后在这个Servlet中去调用我们的DaoFactory,DaoFactory就开始解析Xml配置文件,实例化Dao和Biz,然后往Biz中注入Dao。这样Tomcat启动的时候,我们就有了Dao和Biz对象,而且后面就不要再重复创建这些对象了。

当然,Servlet调用Biz这个耦合我们还没解决,所以Servlet调用Biz的时候你还是得通过DaoFactory来获取Biz。

Dao类:

  1. public class Dao {
  2. private String name="this is dao";
  3. public Dao() {
  4. }
  5. @Override
  6. public String toString() {
  7. return "Dao [ name :"+name + "]";
  8. }
  9. public String getName() {
  10. return name;
  11. }
  12. public void setName(String name) {
  13. this.name = name;
  14. }
  15. }

Biz类:

  1. public class Biz {
  2. private Dao dao;
  3. public Biz() {
  4. }
  5. @Override
  6. public String toString() {
  7. return "Biz [dao=" + dao.toString() + "]";
  8. }
  9. public Dao getDao() {
  10. return dao;
  11. }
  12. public void setDao(Dao dao) {
  13. this.dao = dao;
  14. }
  15. }

设计Xml配置文件


  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans>
  3. <bean id="Dao" class="com.using.bean.Dao"/>
  4. <bean id="Biz" class="com.using.bean.Biz">
  5. <property name="Dao" ref="Dao" />
  6. </bean>
  7. </beans>

DaoFactory类设计

真正实现IOC注入的Java类,虽然取名叫DaoFactory,但是它也实现了Biz的初始化,命名有些不规范o(∩_∩)o

  1. public class DaoFatory {
  2. private static Map<String, Object> beanMap = new HashMap<>();
  3. public static void main(String[] args) {
  4. System.out.println(beanMap.toString());
  5. }
  6. static {
  7. parseBeanFile("/beans.xml");
  8. }
  9. /**
  10. * 根据路径解析
  11. */
  12. public static void parseBeanFile(String beanPath) {
  13. try {
  14. InputStream inputStream = DaoFatory.class.getResourceAsStream(beanPath);
  15. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  16. DocumentBuilder builder = factory.newDocumentBuilder();
  17. Document document = builder.parse(inputStream);
  18. //第一次遍历,初始化所有的对象,包括Dao和Biz
  19. NodeList beanNodeList = document.getElementsByTagName("bean");
  20. for (int i = 0; i < beanNodeList.getLength(); i++) {
  21. Node bean = beanNodeList.item(i);
  22. parseBeanNodes(bean);
  23. }
  24. //第一次遍历,往初始化好的Biz注入Dao
  25. for (int i = 0; i < beanNodeList.getLength(); i++) {
  26. Node bean = beanNodeList.item(i);
  27. parsePropertyNodes(bean);
  28. }
  29. } catch (Exception e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. /**
  34. * 解析property
  35. */
  36. public static void parsePropertyNodes(Node property) {
  37. if(property!=null&&property.getNodeType()==Node.ELEMENT_NODE){
  38. Element element=(Element) property;
  39. String id = element.getAttribute("id");
  40. Object bean=beanMap.get(id);
  41. NodeList childNodeList=property.getChildNodes();
  42. for (int i = 0; i < childNodeList.getLength(); i++) {
  43. Node childNode=childNodeList.item(i);
  44. if(childNode!=null&&childNode.getNodeType()==Node.ELEMENT_NODE
  45. &&"property".equals(childNode.getNodeName())){
  46. Element childElement=(Element) childNode;
  47. String childName = childElement.getAttribute("name");
  48. String childRef = childElement.getAttribute("ref");
  49. Object refBean=beanMap.get(childRef);
  50. setProperty(bean, refBean, childName);
  51. }
  52. }
  53. }
  54. }
  55. /**
  56. * Set参数注入
  57. */
  58. public static void setProperty(Object bean,Object refBean,String childName) {
  59. Class<?> clazz = bean.getClass();
  60. Method[] methods = clazz.getMethods();
  61. for (Method method : methods) {
  62. String methodName = method.getName();
  63. if (("set" + childName.substring(0, 1).toUpperCase() + childName.substring(1)).equals(methodName)) {
  64. try {
  65. method.invoke(bean, refBean);
  66. } catch (Exception e) {
  67. e.printStackTrace();
  68. }
  69. }
  70. }
  71. }
  72. /**
  73. * 解析Node
  74. */
  75. public static void parseBeanNodes(Node node) {
  76. Element element = (Element) node;
  77. String idName = element.getAttribute("id");
  78. String className = element.getAttribute("class");
  79. beanMap.put(idName, createInstance(className));
  80. }
  81. /**
  82. * 根据全类名反射成对象
  83. */
  84. public static Object createInstance(String fullClassName) {
  85. try {
  86. return Class.forName(fullClassName).newInstance();
  87. } catch (Exception e) {
  88. e.printStackTrace();
  89. }
  90. return null;
  91. }
  92. }

控制台输出:

{Biz=Biz [dao=Dao [ name :this is dao]], Dao=Dao [ name :this is dao]}

文章写到这,就已经打到了我想要的效果了,不过单独使用IOC思想,确实看着好奇怪,直接new一个Dao,再往Biz里面Set不是很简单嘛?现在看也确实如此,不过也不着急,之后的文章会不停地使用这里的代码

JavaEE中的MVC(二)Xml配置实现IOC控制反转的更多相关文章

  1. JavaEE中的MVC(五)定制Struts——Action跳转JSP

    在JavaEE中的MVC(三)中,我在Servlet中引入了命令模式的使用,采用Xml配置的方式,实现了一个Servlet调用多个不同的Action类,但是还不能实现页面地跳转,这一篇博客从之前的代码 ...

  2. 在过滤器中获取在web.xml配置的初始化参数

    在过滤器中获取在web.xml配置的初始化参数   例如 <filter> <filter-name>cross-origin</filter-name> < ...

  3. Spring详解(二)------IOC控制反转

    我相信提到 Spring,很多人会脱口而出IOC(控制反转).DI(依赖注入).AOP等等概念,这些概念也是面试官经常问到的知识点.那么这篇博客我们就来详细的讲解 IOC控制反转. ps:本篇博客源码 ...

  4. ASP.NET Core中的依赖注入(1):控制反转(IoC)

    ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了"标准化&qu ...

  5. 精通android体系架构、mvc、常见的设计模式、控制反转(ioc)

    1.请看某个著名的it公司一则招聘信息的其中一条要求:“熟悉android系统架构及相关技术,1年以上实际android平台开发经验:”,里面非常明确的说道要求熟练android系统架构,这从某种程度 ...

  6. Spring 01: Spring配置 + IOC控制反转 + Setter注入

    简介 Spring框架是一个容器,是整合其他框架的框架 他的核心是IOC(控制反转)和AOP(面向切面编程),由20多个模块构成,在很多领域都提供了优秀的问题解决方案 特点 轻量级:由20多个模块构成 ...

  7. Spring框架中IoC(控制反转)的原理(转)

    原文链接:Spring框架中IoC(控制反转)的原理 一.IoC的基础知识以及原理: 1.IoC理论的背景:在采用面向对象方法设计的软件系统中,底层实现都是由N个对象组成的,所有的对象通过彼此的合作, ...

  8. Spring MVC 的 XML 配置方式

    索引: 开源Spring解决方案--lm.solution 参看代码 GitHub: solution/pom.xml solution/webapi/pom.xml solution/mapper/ ...

  9. Spring MVC Web.xml配置

    Web.xml spring&spring mvc 在web.xml中定义contextConfigLocation参数,Spring会使用这个参数去加载所有逗号分隔的xml文件,如果没有这个 ...

随机推荐

  1. OpenWRT (RT5350) 使能两个串口

    OpenWRT(RT5350) 默认使能一个串口(uartlite) ,当做console口了,另外一个串口(uartf)与gpio复用. 在查找资料的过程中,发现在新的内核中使用到了设备树(devi ...

  2. nodejs项目管理之supervisor||pm2||forever

    supervisor 是开发环境用. forever 管理多个站点,每个站访问量不大,不需要监控. pm2 网站访问量比较大,需要完整的监控界面. supervisor 特点: 代码修改,实时重启 安 ...

  3. Xuan.UWP.Framework

    开篇博客,以前总是懒,不喜欢写博客什么,其实都是给自己找理由,从今天开始有空就写写博客.新手博客,写得不好轻喷,哈哈! 开始正题,微软移动平台,从WP7开始,经历了WP8,然后WP8.1,到目前得Wi ...

  4. Codeforces Round #439 (Div. 2) C DP(图论)

    C. The Intriguing Obsession time limit per test 1 second memory limit per test 256 megabytes input s ...

  5. Linux命令用法

    1.cut http://www.cnblogs.com/dong008259/archive/2011/12/09/2282679.html 2.sed http://www.cnblogs.com ...

  6. LeetCode 111. Minimum Depth of Binary Tree (二叉树最小的深度)

    Given a binary tree, find its minimum depth. The minimum depth is the number of nodes along the shor ...

  7. switchhost -- 切换host的工具

    https://github.com/oldj/SwitchHosts/downloads 下载链接: 1,290 downloads SwitchHosts! _v0.2.2.1790.dmg - ...

  8. Python Fabric远程自动部署简介

    Fabric是一个Python(2.5-2.7)库,用于简化使用SSH的应用程序部署或系统管理任务. 它提供的操作包括:执行本地或远程shell命令,上传/下载文件,以及其他辅助功能,如提示用户输入. ...

  9. linux C 文件操作之fgets()

    1. fgets(...)从标准设备读数据.      原型:fgets(s,n,stdin);      假设在控制台下,我们可以用fgets(...)替代gets(),读入键盘输入的信息,fget ...

  10. Sequence one

    Problem Description Search is important in the acm algorithm. When you want to solve a problem by us ...