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

需求:

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

Xml文件读取

Xml配置

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

<?xml version="1.0" encoding="UTF-8"?>
<beans>
<Student id="001">
<name>Tom</name>
<age>31</age>
</Student>
<Student id="002">
<name>Jom</name>
<age>15</age>
</Student>
</beans>

解析Xml文件

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

public class XmlLoad {
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
InputStream inputStream = XmlLoad.class.getResourceAsStream("/Student.xml");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(inputStream); Element root = document.getDocumentElement();
printNode(root);
} private static void printNode(Node node) {
short nodeType = node.getNodeType();
switch (nodeType) {
case Node.TEXT_NODE:
System.out.print(node.getNodeValue());
break; case Node.ELEMENT_NODE:
Element element = (Element) node;
String tagName = element.getTagName();
System.out.print("<");
System.out.print(tagName); NamedNodeMap attrs = element.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
Attr attr = (Attr) attrs.item(i);
printNode(attr);
}
System.out.print(">"); NodeList childNodeList = element.getChildNodes();
for (int i = 0; i < childNodeList.getLength(); i++) {
Node childNode = childNodeList.item(i);
printNode(childNode);
} System.out.print("</" + tagName + ">");
break; case Node.ATTRIBUTE_NODE:
Attr attr = (Attr) node;
String name = attr.getName();
String value = attr.getValue();
System.out.print(" " + name + "=" + value);
break; default:
break;
}
}
}

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

Xml配置

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

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

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

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

public class Student {
private String id;
private String name;
private String age;
//方法补齐
}`

解析思路:

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

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

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

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

解析Xml文件

public class ClassFormat {
public static void main(String[] args) {
parseBeanFile("/beans.xml");
} public static void parseBeanFile(String beanPath) {
try {
InputStream inputStream = ClassFormat.class.getResourceAsStream(beanPath);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(inputStream); NodeList stuNodeList = document.getElementsByTagName("bean");
for (int i = 0; i < stuNodeList.getLength(); i++) {
Element stuElement = (Element) stuNodeList.item(i); String className = stuElement.getAttribute("class");
Object instance = createInstance(className);
parseBeanNodes(instance, stuElement); System.out.println(instance.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 根据全类名反射出对象
*/
public static Object createInstance(String fullClassName) {
try {
return Class.forName(fullClassName).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
} /**
* 解析子节点
*/
public static void parseBeanNodes(Object instance, Element stuElement) {
NodeList propertyNodeList = stuElement.getChildNodes();
for (int j = 0; j < propertyNodeList.getLength(); j++) {
Node propertyNode = propertyNodeList.item(j);
if (propertyNode.getNodeType() == Node.ELEMENT_NODE && "property".equals(propertyNode.getNodeName())) {
Element propertyElement = (Element) propertyNode; String name = propertyElement.getAttribute("name");
String value = propertyElement.getAttribute("value");
//可以补充type属性,然后反射成Javabean的字段类型,逻辑会多一些 setProperty(instance, name, value);
}
}
} /**
* Set方法注入
*/
public static void setProperty(Object instance, String name, String value) {
Class<?> clazz = instance.getClass();
Method[] methods = clazz.getMethods();
for (Method method : methods) {
String methodName = method.getName();
if (("set" + name.substring(0, 1).toUpperCase() + name.substring(1)).equals(methodName)) {
try {
method.invoke(instance, value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}

控制台输出:
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类:

public class Dao {
private String name="this is dao"; public Dao() {
} @Override
public String toString() {
return "Dao [ name :"+name + "]";
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
}

Biz类:

public class Biz {
private Dao dao; public Biz() {
} @Override
public String toString() {
return "Biz [dao=" + dao.toString() + "]";
} public Dao getDao() {
return dao;
} public void setDao(Dao dao) {
this.dao = dao;
}
}

设计Xml配置文件


<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="Dao" class="com.using.bean.Dao"/>
<bean id="Biz" class="com.using.bean.Biz">
<property name="Dao" ref="Dao" />
</bean>
</beans>

DaoFactory类设计

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

public class DaoFatory {
private static Map<String, Object> beanMap = new HashMap<>(); public static void main(String[] args) {
System.out.println(beanMap.toString());
} static {
parseBeanFile("/beans.xml");
} /**
* 根据路径解析
*/
public static void parseBeanFile(String beanPath) {
try {
InputStream inputStream = DaoFatory.class.getResourceAsStream(beanPath);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(inputStream); //第一次遍历,初始化所有的对象,包括Dao和Biz
NodeList beanNodeList = document.getElementsByTagName("bean");
for (int i = 0; i < beanNodeList.getLength(); i++) {
Node bean = beanNodeList.item(i);
parseBeanNodes(bean);
} //第一次遍历,往初始化好的Biz注入Dao
for (int i = 0; i < beanNodeList.getLength(); i++) {
Node bean = beanNodeList.item(i);
parsePropertyNodes(bean);
}
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 解析property
*/
public static void parsePropertyNodes(Node property) {
if(property!=null&&property.getNodeType()==Node.ELEMENT_NODE){
Element element=(Element) property;
String id = element.getAttribute("id");
Object bean=beanMap.get(id); NodeList childNodeList=property.getChildNodes();
for (int i = 0; i < childNodeList.getLength(); i++) {
Node childNode=childNodeList.item(i);
if(childNode!=null&&childNode.getNodeType()==Node.ELEMENT_NODE
&&"property".equals(childNode.getNodeName())){ Element childElement=(Element) childNode;
String childName = childElement.getAttribute("name");
String childRef = childElement.getAttribute("ref"); Object refBean=beanMap.get(childRef); setProperty(bean, refBean, childName); }
}
}
} /**
* Set参数注入
*/
public static void setProperty(Object bean,Object refBean,String childName) {
Class<?> clazz = bean.getClass();
Method[] methods = clazz.getMethods();
for (Method method : methods) {
String methodName = method.getName();
if (("set" + childName.substring(0, 1).toUpperCase() + childName.substring(1)).equals(methodName)) {
try {
method.invoke(bean, refBean);
} catch (Exception e) {
e.printStackTrace();
}
}
}
} /**
* 解析Node
*/
public static void parseBeanNodes(Node node) {
Element element = (Element) node;
String idName = element.getAttribute("id");
String className = element.getAttribute("class");
beanMap.put(idName, createInstance(className));
} /**
* 根据全类名反射成对象
*/
public static Object createInstance(String fullClassName) {
try {
return Class.forName(fullClassName).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

控制台输出:

{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. win10 uwp 打电话

    UWP可以使用打电话功能,在PC是用Skype,在手机是直接使用电话功能. UWP可以通过Skype打电话,那么如何通过应用间通讯,很简单使用Launcher. Skype电话使用Skype:(电话号 ...

  2. 关于JavaScript日期类型处理的总结

    在任何一门开发语言中,对日期时间类型的处理,必不可少也非常重要,长期以来对于JS的日期类型处理较为苍白.在这里做一个浅显的总结. Date 对象用于处理日期和时间.Date 对象用于处理日期和时间.D ...

  3. 使用bitset实现毫秒级查询(二)

    在上一篇中我们了解了bitset索引的基本用法,本篇开始学习bitset索引更新及一些复杂查询. 1.bitset索引更新   因为我们的数据是在系统启动时全部加载进内存,所以当数据库数据发生变化时要 ...

  4. 【NOIP2015资源+题解】

    数据下载(含cena配置文件+一套自己写的代码) 试题下载(pdf版) Day1 T1 Day1 T2 Day1 T3 Day2 T1 Day2 T2 Day3 T3

  5. java分页算法,传入当前pageIndex,pageSise,dataTotal可计算出页面上显示的页码,和是否启动上一页下一页

    public class CalculationPage { private Boolean showStartPagerDot; private Boolean showEndPagerDot; p ...

  6. C#设计模式之十组合模式(Composite)【结构型】

    一.引言   今天我们要讲[结构型]设计模式的第四个模式,该模式是[组合模式],英文名称是:Composite Pattern.当我们谈到这个模式的时候,有一个物件和这个模式很像,也符合这个模式要表达 ...

  7. SpringMVC 异常的处理

    Spring MVC处理异常有3种方式: (1)使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver: (2)实现Spring的异常处理接口Hand ...

  8. 网页授权——扫二维码获取openid

    最近做微信公众平台开发项目时遇到这样一个功能需求:生成一个特定url的二维码,用户扫描二维码后跳转到这个url指定的页面,并在这个页面获得用户的openid.这个功能主要涉及到两方面的技术:生成二维码 ...

  9. Establish the LAMP (Linux+Apache+MySQL+PHP) Development Environment on Ubuntu 14.04 Operating System

    ######################################################## Step One: Update the software package in yo ...

  10. [译]ASP.NET Core 2.0 部分视图

    问题 如何在ASP.NET Core 2.0中使用部分视图来重用页面的公共部分? 答案 新建一个空项目,在Startup中添加MVC服务和中间件: public void ConfigureServi ...