您的位置:首页 > 其它

设计模式之反转控制(IOC)

2015-05-09 14:53 435 查看
反转 控制 (IOC:Inversion of Control)也称为依赖注入(DI:Dependency
Injection),是Spring的核心

可以把IOC模式看做是工厂模式的升华,可以把IOC看作是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的,然后利用Java的“反射”编程,根据XML中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的对象生成代码,改变为由XML文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。IOC中最基本的Java技术就是“反射”编程(有关反射可见Java笔记)。反射又是一个生涩的名词,通俗的说反射就是根据给出的类名(字符串)来生成对象。这种 编程 方式可以让对象在生成时才决定要生成哪一种对象。
当前比较知名的IOC容器有:Pico Container、Avalon 、Spring、JBoss、HiveMind、EJB等。 在上面的几个IOC容器中,轻量级的有Pico Container、Avalon、Spring、HiveMind等,超重量级的有EJB,而半轻半重的有容器有JBoss,Jdon等。

一般情况下的类耦合:

public 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);
}
}

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");
}
}

工厂方法来解耦合
Chinese类、American类及Human类与上面相同,额外添加一个工厂类,作为中介:

class HumanFactory {
public Human
getHuman(String type) {
if ("chinese".equals(type))
{
return new Chinese();
} else {
return new American();
}
}
}

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");
}
}

Main类与类Chinese和American不再耦合,仅仅和其接口Human耦合,当类和方法修改时,只有方法需要修改,因为如果类改变了可以通过修改HumanFactory来匹配。这一定程度上降低了Main类和Chinese、American类的耦合

依赖注入
注入有3种:构造器注入、 设置注入及 接口注入
1、简单例子:

package IOC;
public class UserBean
{
private String userName = "xxxx";
public String
getUserName() {
return userName;
}
public void setUserName(String
userName) {
this.userName =
userName;
}
}

package IOC;
public class BeanFactory
{
public Object
getBean(String beanName) throws Exception{
Class<?> cls = Class.forName(beanName);
Object bean = cls.newInstance();
return bean;
}
}

package IOC;
public class Main
{
public static void main(String[]
args) throws Exception {
BeanFactory beanFactory = new BeanFactory();
UserBean userBean = (UserBean)beanFactory.getBean("IOC.UserBean");
System.out.println(userBean.getUserName());
}
}

上面例子只是简单地通过类的"路径+类名"来创建该类的一个实例,用到的是UserBean的默认构造器,并没有设置UserBean里的任何变量。
2、构造器注入:

package IOC;
public class Book
{
public String
getBookName(){
return "XXX";
}
}

package IOC;
public class Student
{
private String name;
private int age;
private Book book;
public Student(String
name,int age,Book book){
this.name =
name;
this.age =
age;
this.book =
book;
}
public String
getStudentInfo(){
return "name:"+name+",age:"+age;
}
public String
getBookName(){
return book.getBookName();
}
}

package IOC;
import java.lang.reflect.Constructor;
public class Factory
{
public Object
getInstance(String className,Class[]param,Object[]initargs)throws Exception{
Class<?>cls = Class.forName(className);
//根据参数个数及类型确定返回哪个构造器
Constructor<?> constructor = cls.getConstructor(param);
//利用构造器实例化
Object object = constructor.newInstance(initargs);
return object;
}
}
package IOC;

public class Main
{
public static void main(String[]
args) throws Exception {
Factory factory = new Factory();
Class [] param = new Class[]{String.class,int.class,Book.class};
Book book = new Book();
Object [] initargs = new Object[]{"张三",25,book};
Student student = (Student)factory.getInstance("IOC.Student",
param, initargs);
System.out.println(student.getStudentInfo());
System.out.println(student.getBookName());
}
}

3、设置注入:

package IOC;
public class Book
{
public String
getBookName(){
return "XXX";
}
}

package IOC;
public class Student
{
private String name;
private int age;
private Book book;
public void setNameAndAge(String
name,int age){
this.name =
name;
this.age =
age;
}
public void setBook(Book
book){
this.book =
book;
}
public String
getStudentInfo(){
return "name:"+name+",age:"+age;
}
public String
getBookName(){
return book.getBookName();
}
}

package IOC;
import java.lang.reflect.Method;
public class Factory
{
public Object
getInstance(String className,String[]methodNames,Class[][]params,Object[][]initargs)throws Exception{
Class<?>cls = Class.forName(className);
Object object = cls.newInstance();
if(methodNames==null)
return object;
int size
= methodNames.length;
for(int i
= 0;i<size;i++){
Method method = cls.getMethod(methodNames[i],params[i]);
method.invoke(object, initargs[i]);
}
return object;
}
}

package IOC;
public class Main
{
public static void main(String[]
args) throws Exception {
Factory factory = new Factory();
String[]methodNames = new String[]{"setNameAndAge","setBook"};
Class[][]params = new Class[][]{new Class[]{String.class,int.class},new Class[]{Book.class}};
Book book = new Book();
Object[][]initargs = new Object[][]{new Object
[]{"李四",30},new Object[]{book}};
Student student = (Student)factory.getInstance("IOC.Student",
methodNames,params, initargs);
System.out.println(student.getStudentInfo());
System.out.println(student.getBookName());
}
}

4、结合配置文件注入(结合构造器注入或设置注入)
结合设置注入:

package IOC;
public class Student
{
private String name;
private Integer agexxx;
public String getName()
{
return "student:"+name;
}
public void setName(String
name) {
this.name =
name;
}
public Integer
getAgebb() {
return agexxx;
}
public void setAgeaa(Integer
age) {
this.agexxx =
age;
}
public void setAA(String
aa){}
}

package IOC;
public class Teacher
{
private String name;
private Integer age;
public String
getName() {
return "teacher:"+name;
}
public void setName(String
name) {
this.name =
name;
}
public Integer
getAge() {
return age;
}
public void setAge(Integer
age) {
this.age =
age;
}
}

package IOC;
import java.io.File;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class Factory
{
private Map<String,
Object> beanMap = new HashMap<String,
Object>();
public void init(String
fileName) throws Exception {
// 读取指定的配置文件
SAXReader reader = new SAXReader();
String realPathString = new File("").getCanonicalPath();
Document document = reader.read(new File(realPathString
+ "/src/") + 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);
// 获取其属性描述(所有setXXX与getXXX方法)
java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors();
// 设置值的方法
Method mSet = null;
// 创建一个对象
Object obj = bean.newInstance();
System.out.println(pd.length);
for(int k
= 0;k<pd.length;k++){
System.out.println(pd[k].getName());
}
// 遍历该bean的property属性
for (Iterator ite
= foo.elementIterator("property"); ite.hasNext();) {
Element foo2 = (Element) ite.next();
// 获取该property的name属性
Attribute name = foo2.attribute("name");
Object value = null;
// 获取该property的子元素value的值,注意,这里的value只能是一个,因为后面是根据属性名而不是方法名来设置属性
for (Iterator ite1
= foo2.elementIterator("value"); ite1.hasNext();) {
Element node = (Element) ite1.next();
value = (Object)node.getText();
break;
}
for (int k
= 0; k < pd.length; k++) {
if (pd[k].getName().equalsIgnoreCase(name.getText()))
{
mSet = pd[k].getWriteMethod();
if(pd[k].getPropertyType()==Integer.class){
mSet.invoke(obj,Integer.parseInt(value.toString()));
}
else{
mSet.invoke(obj,value);
}
}
}
}
// 将对象放入beanMap中,其中key为id值,value为对象
beanMap.put(id.getText(),
obj);
}
}
// 通过bean的id获取bean的对象.
public Object
getBean(String beanName) {
Object obj = beanMap.get(beanName);
return obj;
}
}

package IOC;
public class Main
{
public static void main(String[]
args) throws Exception {
//当类和方法修改时,代码完全不用修改,只需要修改xml文件即可,彻底实现了解耦
Factory factory = new Factory();
factory.init("/myConfig.xml");
Student student = (Student) factory.getBean("student");
System.out.println(student.getName()+":"+student.getAgebb());
Teacher teacher = (Teacher) factory.getBean("teacher");
System.out.println(teacher.getName()+":"+teacher.getAge());
}
}

myConfig.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="student" class="IOC.Student">
<property name="name">
<value>张三</value>
</property>
<property name="ageaa">
<value>25</value>
</property>
</bean>
<bean id="teacher" class="IOC.Teacher">
<property name="name">
<value>李老师</value>
</property>
<property name="age">
<value>45</value>
</property>
</bean>
</beans>

输出:
5
AA
ageaa
agebb
class
name
3
age
class
name
student:张三:25
teacher:李老师:45

说明:

pd[k].getName():

获取setXXX或getXXX的set或get后面的名字,如:setMyAge()则返回myAge,然后再通过与配置文件比较,确定pd[k]对应的方法是否合适用来设置属性值,比如,Student类中的setAA()方法对应的pd[i].getName()返回aA,而配置文件没有aA,所以该方法不会被使用,而setAgeaa()方法对应的pd[j].getName()返回ageaa,配置文件有这么一项,所以该方法被选中使用来设置ageaa的value25。
pd[k].getWriteMethod():


比如pd[k].getName()等于agexxx,pd[k].getWriteMethod()则得到setAgexxx()方法(如果有的话), pd[k].getReadMethod()则可得到getAgexxx()方法(如果有的话),比如,设置age的方法为setAgeA1(),获
取age的方法为getAgeA2(),那么ageA1与ageA2对应两个不同的pd[m]和pd

pd[m].getName()返回ageA1,pd[m].getWriteMethod()返回方法setAgeA1(),但pd[m].getReadMethod()返回null(因为没有方法getAgeA1()),同理,pd
.getName()返回ageA2,pd
.getReadMethod()返回方法getAgeA2(),但pd
.getWriteMethod()返回null(因为没有方法setAgeA2())。
pd[k].getPropertyType():
返回setXXX()的参数类型,也即getXXX()的返回值类型

上面例子只能用setXXX()来设置属性,并且一个setXXX()方法只能设置一个属性,下面换一种做法,可以自己定义含任意个数的设置方法:

package IOC;
public class Student
{
private String name;
private String addr;
private Integer age;
public void setStudent(String
name,String addr,Integer age){
this.name =
name;
this.addr =
addr;
this.age =
age;
}
public String getStudent(){
return "Student
name:"+name+",address:"+addr+",age:"+age;
}
}

package IOC;
public class Teacher
{
private String name;
private String addr;
private Integer age;
public void setTeacher(String
name,String addr,Integer age){
this.name =
name;
this.addr =
addr;
this.age =
age;
}
public String
getTeacher(){
return "Teacher
name:"+name+",address:"+addr+",age:"+age;
}
}
package IOC;

import java.io.File;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class Factory
{
private Map<String,
Object> beanMap = new HashMap<String,
Object>();
public void init(String
fileName) throws Exception {
// 读取指定的配置文件
SAXReader reader = new SAXReader();
String realPathString = new File("").getCanonicalPath();
Document document = reader.read(new File(realPathString
+ "/src/") + 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);
// 获取其属性描述(所有setXXX与getXXX方法)
java.beans.MethodDescriptor md[] = info.getMethodDescriptors();
// 设置值的方法
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");
Vector<String>vector = new Vector<String>();
// 获取该property的子元素value的值,注意,这里的value只能是一个,因为后面是根据属性名而不是方法名来设置属性
for (Iterator ite1
= foo2.elementIterator("value"); ite1.hasNext();) {
Element node = (Element) ite1.next();
vector.add(node.getText());
}
for (int k
= 0; k < md.length; k++) {
if (md[k].getName().equalsIgnoreCase(name.getText()))
{
mSet = md[k].getMethod();
Class<?>[] pClasses = mSet.getParameterTypes();
Object[] value = new Object[vector.size()];
for(int pi
= 0;pi<vector.size();pi++){
if(pClasses[pi]==Integer.class){
value[pi] = Integer.parseInt(vector.elementAt(pi));
}else{
value[pi] = vector.elementAt(pi);
}
}
mSet.invoke(obj,value);
}
}
}
// 将对象放入beanMap中,其中key为id值,value为对象
beanMap.put(id.getText(),
obj);
}
}
// 通过bean的id获取bean的对象.
public Object
getBean(String beanName) {
Object obj = beanMap.get(beanName);
return obj;
}
}

package IOC;
public class Main
{
public static void main(String[]
args) throws Exception {
//当类和方法修改时,代码完全不用修改,只需要修改xml文件即可,彻底实现了解耦
Factory factory = new Factory();
factory.init("/myConfig.xml");
Student student = (Student) factory.getBean("student");
System.out.println(student.getStudent());
Teacher teacher = (Teacher) factory.getBean("teacher");
System.out.println(teacher.getTeacher());
}
}

myConfig.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="student" class="IOC.Student">
<property name="setStudent">
<value>张三</value>
<value>广东</value>
<value>25</value>
</property>
</bean>
<bean id="teacher" class="IOC.Teacher">
<property name="setTeacher">
<value>李老师</value>
<value>北京</value>
<value>45</value>
</property>
</bean>
</beans>

输出:
Student name:张三,address:广东,age:25
Teacher name:李老师,address:北京,age:45
结合构造器注入:

package IOC;
public class Student
{
private String name;
private String addr;
private Integer age;
public Student(String
name,String addr,Integer age){
this.name =
name;
this.addr =
addr;
this.age =
age;
}
public String
getStudent(){
return "Student
name:"+name+",address:"+addr+",age:"+age;
}
}

package IOC;
public class Teacher
{
private String name;
private String addr;
private Integer age;
public Teacher(String
name,String addr,Integer age){
this.name =
name;
this.addr =
addr;
this.age =
age;
}
public String
getTeacher(){
return "Teacher
name:"+name+",address:"+addr+",age:"+age;
}
}

public class Factory
{
private Map<String,
Object> beanMap = new HashMap<String,
Object>();
public void init(String
fileName) throws Exception {
// 读取指定的配置文件
SAXReader reader = new SAXReader();
String realPathString = new File("").getCanonicalPath();
Document document = reader.read(new File(realPathString
+ "/src/") + 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());
Object obj = null;
// 遍历该bean的property属性
for (Iterator ite =
foo.elementIterator("property"); ite.hasNext();) {
Element foo2 = (Element) ite.next();
Vector<String>valueVector = new Vector<String>();
Vector<Class<?>>paramVector = new Vector<Class<?>>();
for (Iterator ite1
= foo2.elementIterator("value"); ite1.hasNext();) {
Element node = (Element) ite1.next();
valueVector.add(node.getText());
paramVector.add(Class.forName(node.attribute("type").getText()));
}
int size
= valueVector.size();
Class<?>[]param = new Class<?>[size];
Object[] value = new Object[size];
for(int vi
= 0;vi<size;vi++){
param[vi] = paramVector.elementAt(vi);
if(paramVector.elementAt(vi)==Integer.class)
value[vi] = Integer.parseInt(valueVector.elementAt(vi));
else
value[vi] = valueVector.elementAt(vi);
}
Constructor constructor = bean.getConstructor(param);
// 创建一个对象
obj = constructor.newInstance(value);
}
// 将对象放入beanMap中,其中key为id值,value为对象
beanMap.put(id.getText(),
obj);
}
}
// 通过bean的id获取bean的对象.
public Object
getBean(String beanName) {
Object obj = beanMap.get(beanName);
return obj;
}
}

Main类不变
myConfig.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="student" class="IOC.Student">
<property>
<value type="java.lang.String">张三</value>
<value type="java.lang.String">天津</value>
<value type="java.lang.Integer">18</value>
</property>
</bean>
<bean id="teacher" class="IOC.Teacher">
<property>
<value type="java.lang.String">李老师</value>
<value type="java.lang.String">上海</value>
<value type="java.lang.Integer">35</value>
</property>
</bean>
</beans>

如果默认都是String类型,则无需指定type属性
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: