在spring的世界中, 我们通常会利用bean config file 或者 annotation注解方式来配置bean.
在第一种利用bean config file(spring xml)方式中, 还包括如下三小类
-
反射模式
-
工厂方法模式(本文重点)
-
Factory Bean模式
其中反射模式最常见, 我们需要在bean 配置中指明我们需要的bean object的全类名。
例如:
<bean id="car1" class="com.home.factoryMethod.Car"> <property name="id" value="1"></property> <property name="name" value="Honda"></property> <property name="price" value="300000"></property> </bean>
上面bean 里面的class属性就是全类名, Spring利用Java反射机制创建这个bean。
Factory方法模式
本文介绍的是另1种模式, 在工厂方法模式中, Spring不会直接利用反射机制创建bean对象, 而是会利用反射机制先找到Factory类,然后利用Factory再去生成bean对象。
而Factory Mothod方式也分两种, 分别是静态工厂方法 和 实例工厂方法。
静态工厂方法方式
所谓静态工厂方式就是指Factory类不本身不需要实例化, 这个Factory类中提供了1个静态方法来生成bean对象
bean类Car
首先我们定义1个bean类Car
package com.home.factoryMethod; public class Car { private int id; private String name; private int price; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } @Override public String toString() { return "Car [id=" + id + ", name=" + name + ", price=" + price + "]"; } public Car(){ } public Car(int id, String name, int price) { super(); this.id = id; this.name = name; this.price = price; } }
然后我们再定义1个工厂类CarStaticFactory
package com.home.factoryMethod; import java.util.HashMap; import java.util.Map; public class CarStaticFactory { private static Map<Integer, Car> map = new HashMap<Integer,Car>(); static{ map.put(1, new Car(1,"Honda",300000)); map.put(2, new Car(2,"Audi",440000)); map.put(3, new Car(3,"BMW",540000)); } public static Car getCar(int id){ return map.get(id); } }
里面定义了1个静态的bean 容器map. 然后提供1个静态方法根据Car 的id 来获取容器里的car对象。
xml配置
<bean id="bmwCar" class="com.home.factoryMethod.CarStaticFactory" factory-method="getCar"> <constructor-arg value="3"></constructor-arg> </bean> <bean id="audiCar" class="com.home.factoryMethod.CarStaticFactory" factory-method="getCar"> <constructor-arg value="2"></constructor-arg> </bean>
可以见到, 利用静态工厂方法定义的bean item种, class属性不在是bean的全类名, 而是静态工厂的全类名, 而且还需要指定工厂里的
getBean 静态方法名字和参数
客户端代码
public static void h(){ ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-factoryMethod.xml"); Car car1 = (Car) ctx.getBean("bmwCar"); System.out.println(car1); car1 = (Car) ctx.getBean("audiCar"); System.out.println(car1); }
小结
由上面的例子, 静态工厂方法方式是非常适用于作为1个bean容器(集合的), 只不过吧bean集合定义在工厂类里面而不是bean config file里面。
缺点也比较明显, 把数据写在class里面而不是配置文件中违反了我们程序猿的常识和spring的初衷。当然优点就是令到令人恶心的bean config file更加简洁啦。
实例工厂方法方式
所谓实例工厂方式也很容易看懂, 就是里面的getBean 方法不是静态的, 也就是代表要先实例1个工厂对象, 才能依靠这个工厂对象去获得bean 对象。
用回上面的例子。
而这次我们写1个实例工厂类
CarInstanceFactroy
package com.home.factoryMethod; import java.util.HashMap; import java.util.Map; public class CarInstanceFactory { private Map<Integer, Car> map = new HashMap<Integer,Car>(); public void setMap(Map<Integer, Car> map) { this.map = map; } public CarInstanceFactory(){ } public Car getCar(int id){ return map.get(id); } }
bean xml写法
<bean id="carFactory" class="com.home.factoryMethod.CarInstanceFactory"> <property name="map"> <map> <entry key="4"> <bean class="com.home.factoryMethod.Car"> <property name="id" value="4"></property> <property name="name" value="Honda"></property> <property name="price" value="300000"></property> </bean> </entry> <entry key="6"> <bean class="com.home.factoryMethod.Car"> <property name="id" value="6"></property> <property name="name" value="ford"></property> <property name="price" value="500000"></property> </bean> </entry> </map> </property> </bean> <bean id="car4" factory-bean="carFactory" factory-method="getCar"> <constructor-arg value="4"></constructor-arg> </bean> <bean id="car6" factory-bean="carFactory" factory-method="getCar"> <constructor-arg value="6"></constructor-arg> </bean
因为实例工厂本身要实例化, 所以我们可以在xml中 指定它里面容器的data, 解决了上面提到的静态工厂方法的缺点啦
client代码
public static void h2(){ ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-factoryMethod.xml"); Car car1 = (Car) ctx.getBean("car4"); System.out.println(car1); car1 = (Car) ctx.getBean("car6"); System.out.println(car1); }
小结
实例工厂方式使用起来更加灵活, FactoryBean比起工厂方法方式更加常见。