目标明确
简单叙述一下本文想要解决的问题:如何在Spring中不再使用Spring创建Bean实例,而是把Bean创建过程转移到开发者手中。
思路清晰
创建Bean实例的方式:
1) 通过构造器(有参或无参)
方式: <bean id="" class=""/>
2) 通过静态工厂方法
方式: <bean id="" class="工厂类" factory-method="静态工厂方法"/>
注: 工厂类实例没有创建
3) 通过实例工厂方法(非静态方法)
方式:
<bean id="factory" class="工厂类"/>
<bean id="" factory-bean="factory" factory-method="实例工厂方法"/>
注: 工厂类实例被创建
方法实用
示例1:
需求:
1 不想再bean.xml加载的时候实例化bean,而是想把加载bean.xml与实例化对象分离。
2 实现单例的bean
以上的情况,都可以通过工厂方法factory-method来创建bean。
这样再加载bean.xml时,不会直接实例化bean,而是当调用factory-method所指的方法时,才开始真正的实例化。
实现:通过spring的factory-method来创建单例的bean
首先通过静态内部类创建一个单例对象
package com.spring.test.factorymethod; public class Stage { public void perform(){ System.out.println("演出开始..."); } private Stage(){ } private static class StageSingletonHolder{ static Stage instance = new Stage(); } public static Stage getInstance(){ return StageSingletonHolder.instance; } }
在spring配置文件中指定加载的方法getInstance
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="theStage" class="com.spring.test.factorymethod.Stage" factory-method="getInstance"></bean> </beans>
通过应用上下文调用bean获取实例
package com.spring.test.factorymethod; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class test { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml"); Stage stage = ((Stage)ctx.getBean("theStage"));//.getInstance(); stage.perform(); } }
执行结果
一月 24, 2015 6:38:18 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@512dbd1a: startup date [Sat Jan 24 18:38:18 CST 2015]; root of context hierarchy 一月 24, 2015 6:38:19 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [bean.xml] 一月 24, 2015 6:38:19 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons 信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2d1879ea: defining beans [duke,sonnet29,poeticDuke,theStage]; root of factory hierarchy 演出开始...
工厂方法创建bean介绍
1. 使用静态工厂方法创建Bean
使用静态工厂方法创建Bean实例时,class属性也必须指定,但此时class属性并不是指定Bean实例的实现类,而是静态工厂类。因为Spring需要知道是用哪个工厂来创建Bean实例。另外,还需要使用factory-method来指定静态工厂方法名,Spring将调用静态工厂方法(可能包含一组参数),来返回一个Bean实例,一旦获得了指定Bean实例,Spring后面的处理步骤与采用普通方法创建Bean实例则完全一样。需要注意的是,当使用静态工厂方法来创建Bean时,这个factory-method必须要是静态的。这段阐述听上去有点晕,话不多说,上代码:
先定义一个接口,静态方法产生的将是该接口的实例:
public interface Animal { public void sayHello(); }
下面是接口的两个实现类:
public class Cat implements Animal { private String msg; //依赖注入时必须的setter方法 public void setMsg(String msg){ this.msg = msg; } @Override public void sayHello(){ System.out.println(msg + ",喵~喵~"); } } public class Dog implements Animal { private String msg; //依赖注入时必须的setter方法 public void setMsg(String msg){ this.msg = msg; } @Override public void sayHello(){ System.out.println(msg + ",旺~旺~"); } }
下面的AnimalFactory工厂中包含了一个getAnimal的静态方法,该方法将根据传入的参数决定创建哪个对象。这是典型的静态工厂设计模式。
public clas AnimalFactory { public static Animal getAnimal(String type){ if ("cat".equalsIgnoreCase(type)){ return new Cat(); } else { return new Dog(); } } }
如果需要指定Spring使用AnimalFactory来产生Animal对象,则可在Spring配置文件中作如下配置:
<!-- 配置AnimalFactory的getAnimal方法,使之产生Cat --> <bean id="cat" class="com.abc.AnimalFactory" factory-method="getAnimal"> <!-- 配置静态工厂方法的参数,getAnimal方法将产生Cat类型的对象 --> <constructor-arg value="cat" /> <!-- 通过setter注入的普通属性 --> <property name="msg" value="猫猫" /> </bean> <!-- 配置AnimalFactory的getAnimal方法,使之产生Dog --> <bean id="dog" class="com.abc.AnimalFactory" factory-method="getAnimal"> <!-- 配置静态工厂方法的参数,getAnimal方法将产生Dog类型的对象 --> <constructor-arg value="dog" /> <!-- 通过setter注入的普通属性 --> <property name="msg" value="狗狗" /> </bean>
从上面的配置可以看出:cat和dog两个Bean配置的class和factory-method完全相同,这是因为两个实例都使用同一个静态工厂类、同一个静态工厂方法产生得到的。只是为这个静态工厂方法指定的参数不同,使用<constructor-arg />元素来为静态工厂方法指定参数。
主程序获取cat和dog两个Bean实例的方法不变,同样只需要调用Spring容器的getBean()即可:
public class Test { public static void main(String args[]){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Animal a1 = context.getBean("cat", Animal.class); a1.sayHello(); Animal a2 = context.getBean("dog", Animal.class); a2.sayHello(); } }
输出结果:
<code class="hljs">猫猫,喵~喵~ 狗狗,旺~旺~</code>
使用静态工厂方法创建实例时必须提供工厂类和产生实例的静态工厂方法。通过静态工厂方法创建实例时需要对Spring配置文件做如下改变;
class属性不在是Bean实例的实现类,而是生成Bean实例的静态工厂类
使用factory-method指定生产Bean实例的静态工厂方法
如果静态工厂方法需要参数,使用<constructor-arg />元素为其配置
当我们指定Spring使用静态工厂方法来创建Bean实例时,Spring将先解析配置文件,并根据配置文件指定的信息,通过反射调用静态工厂类的静态工厂方法,并将该静态工厂方法的返回值作为Bean实例,在这个过程中,Spring不再负责创建Bean实例,Bean实例是由用户提供的静态工厂方法提供的。
2. 使用实例工厂方法创建Bean
实例工厂方法与静态工厂方法只有一点不同:调用静态工厂方法只需要使用工厂类即可,调用实例工厂方法则必须使用工厂实例。所以在Spring配置上也只有一点区别:配置静态工厂方法指定静态工厂类,配置实例工厂方法则指定工厂实例。同样是上面的例子将AnimalFactory修改为:
public clas AnimalFactory { public Animal getAnimal(String type){ //这里仅仅是去掉了static关键字 if ("cat".equalsIgnoreCase(type)){ return new Cat(); } else { return new Dog(); } } }
Spring文件修改为:
<!-- 先配置工厂类 --> <bean id="animalFactory" class="com.abc.AnimalFactory" /> <!-- 这里使用factory-bean指定实例工厂类对象 --> <bean id="cat" factory-bean="animalFactory" factory-method="getAnimal"> <!-- 同样指定factory-method的参数 --> <constructor-arg value="cat" /> <property name="msg" value="猫猫" /> </bean> <bean id="dog" factory-bean="animalFactory" factory-method="getAnimal"> <constructor-arg value="dog" /> <property name="msg" value="狗狗" /> </bean>
测试类不用修改,输出结果和上面相同。
很多情况下使用<bean id=”bean1” class=”…” />定义一个bean,这种定义方式Spring将会调用默认的无参数构造方法创建Bean实例。除此之外还可以使用工厂方式创建Bean实例,实现Bean创建与使用的分离,将Bean创建工作交由工厂来完成。
配置工厂Bean的三种方式。
抽象接口:
public interface IMusicBox { public void play(); }
1、静态工厂方法取得Bean实例
工厂类:
public class MusicBoxFactory { public static IMusicBox createMusicBox(){ return new IMusicBox(){ public void play(){ System.out.println("Play piano..."); } }; } }
配置文件:
<bean id="musicBox" class="test.spring.MusicBoxFactory" factory-method="createMusicBox" />
测试类:
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-config.xml"); IMusicBox musicbox = (IMusicBox)ctx.getBean("musicBox"); musicbox.play(); }
2、工厂实例的方法取得Bean实例
工厂类:
public class MusicBoxFactory { public IMusicBox createMusicBox(){//没有static修饰 return new IMusicBox(){ public void play(){ System.out.println("Play piano..."); } }; } }
配置文件:
<bean id="factoryBean" class="test.spring.MusicBoxFactory" /> <bean id="musicBox" factory-bean="factoryBean" factory-method="createMusicBox" />
“factory-bean”属性指定工厂Bean,”factory-method”属性指定工厂方法来取得Bean实例。
测试类:
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-config.xml"); IMusicBox musicbox = (IMusicBox)ctx.getBean("musicBox"); musicbox.play(); }
3、工厂类实现org.springframework.beans.factory.FacotryBean接口
工厂类:
import org.springframework.beans.factory.FactoryBean; public class MusicBoxFactory2 implements FactoryBean { public Object getObject() throws Exception { return new IMusicBox(){ public void play(){ System.out.println("Play piano..."); } }; } public Class getObjectType() { return IMusicBox.class; } public boolean isSingleton() { return false; } }
配置文件:
<bean id="musicBox" class="test.spring.MusicBoxFactory2"/>
测试类:
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-config.xml"); //不加 & 返回工厂的“产品” IMusicBox musicbox = (IMusicBox)ctx.getBean("musicBox"); musicbox.play(); //加 & 返回工厂类实例 Object obj = ctx.getBean("&musicBox"); System.out.println(obj.getClass().getName()); }
实现FactoryBean接口的类不会被视为普通的Bean,Spring会自动检测,调用getObject方法获取Bean实例
总结
Spring工厂方法实例化bean实例的介绍就到这里,有什么不足之处,大家可以留言指出。感谢朋友们对本站的支持!