javaDAY5
来源:互联网 发布:杨闻萍审计 知乎 编辑:程序博客网 时间:2024/06/04 00:21
Java之控制反转和依赖注入
(转自http://www.cnblogs.com/devinzhang/p/3862942.html)
1.简介
控制反转:创建被调用者的工作不再由调用者来完成,因此称为控制反转。结合Java说,当某个Java实例需要其他Java实例时,系统自动提供一个所需要的实例,无须程序显示的new一个。所以,控制反转是,关于一个对象如何获取他所依赖的对象的引用,这个责任的反转。在有控制反转之前,是由调用者负责创建他所依赖的对象;之后,由系统负责创建。
依赖注入:纵观所有的Java应用,它们都是由一些互相协作的对象构成的。我们称这种互相协作的关系为依赖关系。假如A组件调用了B组件的方法,我们可称A组件依赖于B组件。系统创建的实例供调用者调用,也可以看作是系统将创建的实例注入调用者。
优点:因为把对象生成放在了XML里定义,所以当我们需要换一个实现子类将会变成很简单(一般这样的对象都是实现于某种接口的),只要修改XML就可以了,这样我们甚至可以实现对象的热插拨(有点象USB接口和SCSI硬盘了)。
缺点:
(1)生成一个对象的步骤变复杂了(事实上操作上还是挺简单的),对于不习惯这种方式的人,会觉得有些别扭和不直观;
(2)对象生成因为是使用反射编程,在效率上有些损耗。但相对于IoC提高的维护性和灵活性来说,这点损耗是微不足道的,除非某对象的生成对效率要求特别高;
(3)缺少IDE重构操作的支持,如果在Eclipse要对类改名,那么你还需要去XML文件里手工去改了,这似乎是所有XML方式的缺憾所在。
目的:依赖注入和控制反转,目的是为了使类与类之间解耦合,提高系统的可扩展性和可维护性,下面通过一个例子来引入这一概念。
2.案例
1)一般情况下的类耦合
Main.java
public class Main { public static void main(String[] args) { /******** 一般写法,Main类与Chinese类和American类之间的强耦合 ***********/ // Chinese和American,当类和方法修改时,此处的类和方法也需要修改 Chinese chinese = new Chinese(); chinese.sayHelloWorld("张三"); American american = new American(); american.sayHelloWorld("Jack"); }}
/********** 一般方法 *****************/
interface Human { public void sayHelloWorld(String name);}class Chinese implements Human { public void sayHelloWorld(String name) { String helloWorld = "你好," + name; System.out.println(helloWorld); }}class American implements Human { public void sayHelloWorld(String name) { String helloWorld = "Hello," + name; System.out.println(helloWorld); }}
通过上面代码可以看出:Main类与Chinese类和American类之间存在着强耦合 , Chinese和American类和方法修改时,此处的类和方法也需要修改。不容易扩展和维护。
2)工厂方法来解耦合
public class Main { public static void main(String[] args) { /******** 工厂方法, Main类与类Chinese和American不再耦合,仅仅和其接口Human耦合 ***********/ // 修改时还需要修改在Main类中修改这些字符串 // Chinese和American,当类和方法修改时,只有方法需要修改 HumanFactory humanFactory = new HumanFactory(); Human human1 = humanFactory.getHuman("chinese"); human1.sayHelloWorld("张三"); Human human2 = humanFactory.getHuman("american"); human2.sayHelloWorld("Jack"); }}
/******************** 工厂方法 ***************************/interface Human { public void sayHelloWorld(String name);}class HumanFactory { public Human getHuman(String type) { if ("chinese".equals(type)) { return new Chinese(); } else { return new American(); } }}
通过上面代码可以看出:Main类与类Chinese和American不再耦合,仅仅和其接口Human耦合,修改时还需要修改在Main类中修改这些字符串,当类和方法修改时,只有方法需要修改。这一定程度上降低了Main类和Chinese、American类的耦合
3)依赖注入和控制反转
public class Main { public static void main(String[] args) { /******************** IOC控制反转和依赖注入 ***************************/ // 利用容器,通过xml文件直接注入属性值,在Main类中只添加需要的 // Chinese和American,当类和方法修改时,代码完全不用修改,只需要修改xml文件即可,彻底实现了解耦 BeanFactory beanFactory = new BeanFactory(); beanFactory.init("/config.xml"); UserBean userBean = (UserBean) beanFactory.getBean("userBean"); System.out.println("userName=" + userBean.getUserName()); System.out.println("password=" + userBean.getPassword()); }}
/******************** IOC控制反转和依赖注入 ***************************/// 下面是Spring的IOC实现:Bean工厂class BeanFactory { private Map<String, Object> beanMap = new HashMap<String, Object>(); public void init(String fileName) { try { // 读取指定的配置文件 SAXReader reader = new SAXReader(); // System.out.println(xmlpath); String realPathString = new File("").getCanonicalPath(); Document document = reader.read(new File(realPathString + "/src/com/devin/") + fileName); Element root = document.getRootElement(); Element foo; // 遍历bean for (Iterator i = root.elementIterator("bean"); i.hasNext();) { foo = (Element) i.next(); // 获取bean的属性id和class Attribute id = foo.attribute("id"); Attribute cls = foo.attribute("class"); // 利用Java反射机制,通过class的名称获取Class对象 Class bean = Class.forName(cls.getText()); // 获取对应class的信息 java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean); // 获取其属性描述 java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors(); // 设置值的方法 Method mSet = null; // 创建一个对象 Object obj = bean.newInstance(); // 遍历该bean的property属性 for (Iterator ite = foo.elementIterator("property"); ite.hasNext();) { Element foo2 = (Element) ite.next(); // 获取该property的name属性 Attribute name = foo2.attribute("name"); String value = null; // 获取该property的子元素value的值 for (Iterator ite1 = foo2.elementIterator("value"); ite1.hasNext();) { Element node = (Element) ite1.next(); value = node.getText(); break; } for (int k = 0; k < pd.length; k++) { if (pd[k].getName().equalsIgnoreCase(name.getText())) { mSet = pd[k].getWriteMethod(); mSet.invoke(obj, value); } } } // 将对象放入beanMap中,其中key为id值,value为对象 beanMap.put(id.getText(), obj); } } catch (Exception e) { System.out.println(e.toString()); } } // 通过bean的id获取bean的对象. public Object getBean(String beanName) { Object obj = beanMap.get(beanName); return obj; }}
UserBean.javapublic class UserBean { private String userName; private String password; public String getPassword() { return password; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public void setPassword(String password) { this.password = password; }}
config.xml<?xml version="1.0" encoding="UTF-8"?><beans> <bean id="userBean" class="com.devin.UserBean"> <property name="userName"> <value>张三</value> </property> <property name="password"> <value>Jack</value> </property> </bean></beans>
说明:模拟了Spring中IOC的实现,虽然只是完成了Spring中依赖注入的一小部分工作,但是很好的展现了Java反射机制在Spring中的应用,能使我们能更好的从原理上了解IOC的实现。
- javaDAY5
- 个人对设计模式的理解-->建造者模式
- HDU 1076 An Easy Task(数学题)
- 链表接口的封装
- jstl标签
- 微信公众平台最实用的工具和技巧大集合
- javaDAY5
- PAT (Advanced Level) Practise 1114 Family Property (25) 并查集orDFS
- 母函数最终模板(备忘录)
- glog学习
- android之AsyncTask原理分析
- Rust 1.7.0 语法基础 标识符(ident)和分隔符的约束
- 人工智能的新纪元——深度学习
- Spring MVC 中 HandlerInterceptorAdapter的使用
- android服务器Bmob的使用