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

反转 控制 (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类之间的强耦合 ***********/

         // ChineseAmerican,当类和方法修改时,此处的类和方法也需要修改

         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类与类ChineseAmerican不再耦合,仅仅和其接口Human耦合 ***********/

         // 修改时还需要修改在Main类中修改这些字符串

         // ChineseAmerican,当类和方法修改时,只有方法需要修改

         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的属性idclass

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

             // 获取其属性描述(所有setXXXgetXXX方法)

             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());

             }

             // 遍历该beanproperty属性

             for (Iterator ite = foo.elementIterator("property"); ite.hasNext();) {

                 Element foo2 = (Element) ite.next();

                 // 获取该propertyname属性

                 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中,其中keyid值,value为对象

             beanMap.put(id.getText(), obj);

        }

    }

    // 通过beanid获取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():

获取setXXXgetXXX的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()等于agexxxpd[k].getWriteMethod()则得到setAgexxx()方法(如果有的话), pd[k].getReadMethod()则可得到getAgexxx()方法(如果有的话),比如,设置age的方法为setAgeA1(),获

age的方法为getAgeA2(),那么ageA1ageA2对应两个不同的pd[m]pd[n]:

pd[m].getName()返回ageA1,pd[m].getWriteMethod()返回方法setAgeA1(),但pd[m].getReadMethod()返回null(因为没有方法getAgeA1()),同理,pd[n].getName()返回ageA2,pd[n].getReadMethod()返回方法getAgeA2(),但pd[n].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的属性idclass

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

             // 获取其属性描述(所有setXXXgetXXX方法)

             java.beans.MethodDescriptor md[] = info.getMethodDescriptors();

             // 设置值的方法

             Method mSet = null;

             // 创建一个对象

             Object obj = bean.newInstance();

             // 遍历该beanproperty属性

             for (Iterator ite = foo.elementIterator("property"); ite.hasNext();) {

                 Element foo2 = (Element) ite.next();

                 // 获取该propertyname属性

                 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中,其中keyid值,value为对象

             beanMap.put(id.getText(), obj);

        }

    }

    // 通过beanid获取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的属性idclass

             Attribute id = foo.attribute("id");

             Attribute cls = foo.attribute("class");

             // 利用Java反射机制,通过class的名称获取Class对象

             Class bean = Class.forName(cls.getText());

             Object obj = null;

             // 遍历该beanproperty属性

             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中,其中keyid值,value为对象

             beanMap.put(id.getText(), obj);

        }

    }

    // 通过beanid获取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属性


















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值