解析Spring IOC原理——工厂模式与反射机制的综合应用

(一)工厂模式

从一个例子开始讲起:

首先我们建立一个Chinese.java类,该类的sayHelloWorld(String name)方法,用中文对名为name的人问好,其内容如下:

     

public class Chinese {
    /**-- 用中文对某人问好. --*/
    publicvoid sayHelloWorld(String name) {
       String helloWorld = "你好," + name;
       System.out.println(helloWorld);
    }
}

 

下面我们接着建立一个American.java类,该类的sayHelloWorld(String name)方法,用英文对名为name的人问好,其内容如下:下面我们接着建立一个American.java类,该类的sayHelloWorld(String name)方法,用英文对名为name的人问好,其内容如下:

publicclass American {

    /*-- 用英文对某人问好 --*/
    publicvoid sayHelloWorld(String name) {
       String helloWorld = "Hello," + name;
       System.out.println(helloWorld);
    }
}


 最后我们编写一个测试类对这两个类的sayHelloWorld(String name)方法进行测试,下面是该类的内容:

publicclass HelloWorldTest {

    /*-- 测试Chinese和American的sayHelloWorld()方法 --*/
    publicstaticvoid main(String[] args) {
       Chinese chinese = new Chinese();
       chinese.sayHelloWorld("极度暴走");
       
       American american = new American();
       american.sayHelloWorld("jidubaozou");
    }
}

 

现实生活中我们总是把同一类产品放在一个工厂中生产,消费者只需要告诉工厂自己需要这一类产品中的哪一种产品,工厂就能够生产出对应的产品,从而完成交易。

这里我们把上面例子中的HelloWorldTest类想象为消费者,而Chinese和American类想象为产品,根据上面的例子,我们可以看到每当HelloWorldTest这个消费者需要一个产品时都要去自己去生产(new),这样消费者就不得不去了解各个产品的内部结构及生产过程。显然对于消费者来说这样是很麻烦的。

而在程序设计领域中这个问题就叫做强耦合问题,HelloWorldTest类与ChineseAmerican这两个类都存在强耦合关系。设想一下,如果程序中的很多类都需要用到ChineseAmerican类这两个类,那么每个类就必须和这两个类强耦合,而且现实的项目中类似于Chinese这种类往往不仅两个,这样会使得整个程序的耦合度很高,增大程序的复杂度。这时我们就想到,能否也使用一个工厂来生产这些类,这样如果某个类需要用到工厂中的哪个类,就只要通过和工厂生产就行了,从而降低了整个程序的耦合度。

下面看看工厂模式是如何实现的:

首先建立接口类Human.java,其内容如下:

public interface Human {

    /** *//**
     * 对某人问好.
     * @param name 姓名
     */
    public void sayHelloWorld(String name);
}

 

并将American.java类和Chinese.java类改为实现该接口,即类头分别改成:public class American implements Humanpublic class Chinese implements Human

接着编写HumanFactory.java工厂类,其内容为

 

public class HumanFactory {

       /** *//**
        * 通过类型字段获取人的相应实例
        * @param type 类型
        * @return 返回相应实例
        */
       public Human getHuman(String type) {
              if ("chinese".equals(type)) {
                     return new Chinese();
              } else {
                     return new American();
              }
       }
}

 

最后我们还需要修改测试类HelloWorld.java类,修改后的内容如下: 

 

public class HelloWorldTest {

       /** *//**
        * 测试sayHelloWorld()方法.
        * @param args
        */
       public static void main(String[] args) {
              HumanFactory factory = new HumanFactory();
              Human human1 = factory.getHuman("chinese");
              human1.sayHelloWorld("极度暴走");

              Human human2 = factory.getHuman("american");
              human2.sayHelloWorld("jidubaozou");
              }
}


通过上面的例子可以看到,使用工厂模式实现的方式, HelloWorldTest 不再与Chinese类和American类耦合,而只是和他们共同的接口耦合,从而很大程度的降低的程序的耦合性。到这里或许很多人会有一点疑虑,使用工厂模式的HelloWorldTest 类也是需要耦合两个类(即HumanFactory 类和Human 接口)那么为什么能说降低耦合度呢,这就要是何时要使用工厂模式的问题了。假如类似于我们这个例子,HumanFactory 这个工厂只生产两个类,那的确没有什么必要使用工厂模式,但是现实的项目中往往有更多和Chinese类类似的类,比如English,Japanese,这时候就能体现出工厂模式的优势了

 

 (二)Spring IOC的原理

首先我们来讲下为什么要引入IOC:

假设上面举例的那个程序已经部署到服务器中,并已经上线运行,这时项目来了这样一个新需求:增加一个Japanese类,并且在HelloWorldTest 类中调用Japanese的sayHelloWorld方法。在没有引入IOC之前,很显然我们为了这个新需求,必须把项目停止下来,然后从新编译HumanFactory 和HelloWorldTest这两个类,最后再重新上线运行。

而使用IOC,则能够在不重新编译部署的情况下实现上面的新需求!

那么我们来看一下IOC是去怎么实现这个新需求的: 

 首先我们在com.human包内创建一个Japanese类:

public class Japanese implements Human{
     private String name;
     private String age;
     
     public void sayHelloWorld(String string){
    	 System.out.println("你好,"+string);
     }
     public String getAge() {
	 return age;
     }
     public void setAge(String age) {
	 this.age = age;
     }
     public String getName() {
	 return name;
     }
     public void setName(String name) {
         this.name = name;
     }
}


 

然后对HelloWorldTest 类做如下修改

 

public class HelloWorldTest {

       public static void main(String[] args) {
             ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
             BeanFactory beanFactory=ctx.getBeanFactory();
             Human human=beanFactory.getBean("human");
             human.sayHelloWorld("极度暴走");
             }
}

 

然后我们在beans.xml中做如下配置:

<?xml version="1.0" encoding="UTF-8"?>

<beans>
    <bean id="human" class="com.human.Japanese">
       <property name="name">
           <value>漩涡鸣人</value>
       </property>
       <property name="password">
           <value>22</value>
       </property>
</bean>
</beans>


这样,HelloWorldTest 便会去调用Japanese类的sayHelloWorld()方法;用这种形式,我们只要修改beans.xml的配置便可以改变HelloWorldTest 类中human对象,并且不需要重新部署项目,这样就实现了human对象的热拔插。

从上面的代码可以看到spring IOC的这种实现方式主要是靠BeanFactory这个类,那么这个BeanFactory到底有什么神奇之处呢?

下面让我们模拟实现一个BeanFactory:


 

import java.io.InputStream;
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;

/** *//**
 * bean工厂类.    
 */
public class BeanFactory {
       private Map<String, Object> beanMap = new HashMap<String, Object>();

       /** *//**
        * bean工厂的初始化.
        * @param xml xml配置文件
        */
       public void init(String xml) {
              try {
                     //读取指定的配置文件
                     SAXReader reader = new SAXReader();
                     ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
                     //从class目录下获取指定的xml文件
                     InputStream ins = classLoader.getResourceAsStream(xml);
                     Document doc = reader.read(ins);
                     Element root = doc.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();
                                   //利用Java的反射机制调用对象的某个set方法,并将值设进去
                                   mSet.invoke(obj, value);
}
}
}

//将对象放入beanMap中,其中key为id值,value为对象
beanMap.put(id.getText(), obj);
}
} catch (Exception e) {
    System.out.println(e.toString());
}
}

       /** *//**
        * 通过bean的id获取bean的对象.
        * @param beanName bean的id
        * @return 返回对应对象
        */
       public Object getBean(String beanName) {
              Object obj = beanMap.get(beanName);
              return obj;
       }

       /** *//**
        * 测试方法.
        * @param args
        */
       public static void main(String[] args) {
             BeanFactory factory = new BeanFactory();
              factory.init("bean.xml");
              Human human= (Human) factory.getBean("human");
              human.sayHelloWorld("极度暴走");
              System.out.println("Name=" + human.getName());
              System.out.println("age=" + human.getAge());
       }
}


测试方法的执行结果:你好,极度暴走

漩涡鸣人

22

 

总结:从上面的代码可以看出Spring的bean工厂主要实现了以下几个步骤

1.解析配置文件(bean.xml)

2.使用反射机制动态加载每个class节点中配置的类

3.为每个class节点中配置的类实例化一个对象

4.使用反射机制调用各个对象的seter方法,将配置文件中的属性值设置进对应的对象

5.将这些对象放在一个存储空间(beanMap)中

6.使用getBean方法从存储空间(beanMap)中取出指定的JavaBean

 

PS:IOC的标准定义:

(Inversion of Control)

  中文译为: 控制反转

  IOC的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。容器负责将这些联系在一起。

 

 












  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值