工厂模式-理解Spring的Bean工厂
接面向对象里面 “老张开车去东北”的场景。链接名称
封装“老张开车去东北”里面的交通工具,封装交通工具Car
只给司机一辆车(单例、多例)
顺带讲解单例
要求只能有一辆车,别人不能new Car,只有Car自己能控制newCar的逻辑。私有化构造方法,别人就不能new了。
/**
* 交通工具Car
*
*/
public class Car {
//private static Car car = new Car();
private Car(){}
public static Car getInstance(){
return new Car();
}
public void run(){
System.out.println("冒着烟奔跑中...");
}
}
工厂就是自主生产自己的产品,不再依赖于new。比如你想new我家的一个抽屉,你想拿钱就拿钱, 肯定不行。
但是我要给你提供一个方法:getChouTi(); 我就能在get方法里面做各种各样的限制了。
比如返回Car的getInstance方法,可以做逻辑判断。
/**
* 交通工具Car
*/
public class Car {
private Car(){}
public static Car getInstance(){
if(有驾照){
return new Car();
}
return null;
}
public void run(){
System.out.println("冒着烟奔跑中...");
}
}
再回到上面的要求,只有一辆车,这么做:自己new一个Car,调用getInstance时候,返回这个Car 。
public class Car {
private static Car car = new Car();
private Car(){}
public static Car getInstance(){
return car;
}
public void run(){
System.out.println("冒着烟奔跑中...");
}
}
测试
getInstance两次看是不是一辆车:
public static void main(String[] args) {
Car car1 = Car.getInstance();
Car car2 = Car.getInstance();
System.err.println(car1 == car2);
}
打印:true
打印true,说明是一辆车
这个模式叫 单例,又有人叫这个getInstance方法叫静态工厂方法。
任何方法,里面控制了产生对象的逻辑,都可以叫工厂方法。
多例
如果Car类里面返回的不是一个Car,里面有一个List装了一堆的Car,getInstance的时候随机返回一个,这个又有人起了个名字 叫---多例
public class Car {
//private static Car car = new Car();
private static List<Car> cars = new ArrayList<>();
static{
//静态初始化cars
cars.add(new Car());
cars.add(new Car());
}
private Car(){}
public static Car getInstance(){
//return car;
//随机返回一个Car,这里就不随机了
return cars.get(1);
}
public void run(){
System.out.println("冒着烟奔跑中...");
}
}
JDBC连接池,里面装的Connection,就是多例。
任意定制交通工具的类型和生产过程
自然就想起了多态,抽取一个接口:Moveable,然后让Car实现Moveable接口:
public interface Moveable {
void run();
}
Car的实现:
public class Car implements Moveable{
@Override
public void run() {
System.out.println("冒着烟奔跑中...");
}
}
飞机的实现:
public class Plane implements Moveable{
@Override
public void run() {
System.out.println("扇着翅膀飞呀飞...");
}
}
测试类:
调用的时候,父类引用指向子类对象,多态,我new谁,就调用的是谁,很随意就换了交通工具:
Moveable m = new Car();
m.run();
m = new Plane();
m.run();
打印结果:
冒着烟奔跑中...
扇着翅膀飞呀飞...
还有其他任何交通工具 比如交通工具是哈利波特的扫帚,就直接实现Moveable接口,你就可以直接new Broom(); 了 。
现在存在的问题就是,可以任意的new 交通工具,构造方法是公开的。现在想对任意交通工具的生产过程也能够定制的话。有了上面单例的思路,现在第一个想到的还是,在交通工具类里面写一个静态的方法控制new 的过程。这里比如飞机,把产生飞机的过程单独一个类拿出来,比如叫飞机工厂PlaneFactory:
//飞机工厂类
public class PlaneFactory {
public Plane createPlane(){
//单例、多例、条件检查自己控制
return new Plane();
}
}
测试 :
//飞机工厂
PlaneFactory factory = new PlaneFactory();
Moveable m = factory.createPlane();
m.run();
打印结果:
扇着翅膀飞呀飞...
如果现在想有一个Car工厂,那么很简单,就是这样:
//Car工厂类
public class CarFactory {
public Car createPlane(){
//单例、多例、条件检查自己控制
return new Car();
}
}
测试代码:
以前是开着飞机,现在想换成开Car,需要把飞机工厂换成了Car工厂,调用他的createCar方法。这样太别扭了。
//飞机工厂
//PlaneFactory factory = new PlaneFactory();
//换成Car工厂,整个工厂方法都得换
CarFactory factory = new CarFactory();
Moveable m = factory.createCar();
m.run();
有没有什么办法,从飞机换成Car的时候,只换工厂的实现就行呢?自然就想起了多态,有多态就得有父类、子类。所以工厂类需要有一个父类。Factory本来是产生交通工具的,抽象出一个产生交通工具的工厂:
//交通工具工厂
public abstract class VehicleFactory {
//具体生成什么交通工具由子类决定,这里是抽象的。
public abstract Moveable create();
}
这时候让CarFactory和PlaneFactory去继承VehicleFactory :
//Car工厂类
public class CarFactory extends VehicleFactory{
@Override
public Moveable create() {
//单例、多例、条件检查自己控制
return new Car();
}
}
//飞机工厂类
public class PlaneFactory extends VehicleFactory {
@Override
public Moveable create() {
//单例、多例、条件检查自己控制
return new Plane();
}
}
换了工厂的实现,就可以换交通工具了,比如你加了一个哈利波特的魔法扫帚,需要加一个Broom类实现Moveable接口,和一个BroomFactory工厂类继承VehicleFactory就可以了。
//扫帚
public class Broom implements Moveable{
@Override
public void run() {
System.out.println("扫帚摇着尾巴呼呼呼...");
}
}
//扫帚工厂类
public class BroomFactory extends VehicleFactory {
@Override
public Moveable create() {
//单例、多例、条件检查自己控制
return new Broom();
}
}
此时测试代码就成了这样子:
//机车工厂,new飞机工厂实例
VehicleFactory factory = new PlaneFactory();
Moveable m = factory.create();
m.run();
//换成Car工厂
factory = new CarFactory();
m = factory.create();
m.run();
//换成扫帚工厂
factory = new BroomFactory();
m = factory.create();
m.run();
打印结果;
扇着翅膀飞呀飞...
冒着烟奔跑中...
扫帚摇着尾巴呼呼呼...
在某一个维度上有了可扩展了,不仅可以控制产生交通工具的类型,还可以控制产生交通工具的生产过程。需要改的只有一个地方:要是需要换交通工具,站在客户角度,需要改的只有交通工具的工厂,其他地方都不用动。如果用了配置文件的话,代码都不用该,只改配置文件即可。后面再说。
关于抽象类和接口的选择:
比如上面的交通工具工厂,是一个抽象类,也可以设计成接口,么问题。
假如这个概念在我们脑子是确确实实存在的,就用抽象类,
假如这个概念只是某些方面的特性:比如会飞的,会跑的,就用接口
假如两个概念模糊的时候,不知道选择哪个的时候,就用接口,原因是,从实现了这个接口后,还能从其它的抽象类继承,更灵活。
抽象工厂
看一下JDK里面,先看getInstance方法,有一大堆,不同类里面有同样的方法getInstance。
这些大多数都是静态的工厂方法,是不是单例不一定,得看具体的实现。
各种各样的Factory也很多。像下面的加密的key,就不适合new,用一个工厂去实现它,产生的时候可以实现各种各种算法,检测各种资质,new的话构造方法只能是写死的,用工厂的话,实现一个子类的时候还可以控制生产过程,更灵活。
总而言之,getInstance和Factory,JDK里面很常用。下面开始说抽象工厂。
回到最原始的状态,我们有一辆车Car。
控制一系列的产品(车、武器、食品补给)
现在让这个人,开着车,拿着AK47,吃着苹果。 意思就是,这是一些列的产品,要控制这一些列的产品的生产。
比如你要装修,海尔整体厨房,有微波炉,油烟机,洗衣机,电磁炉。一系列的产品。
public class Car{
public void run() {
System.out.println("冒着烟奔跑中...");
}
}
public class AK47 {
public void shoot(){
System.out.print("哒哒哒....");
}
}
public class Apple {
public void getName(){
System.out.println("Apple...");
}
}
测试类:
Car car = new Car();
car.run();
AK47 ak = new AK47();
ak.shoot();
Apple apple = new Apple();
apple.getName();
打印:
冒着烟奔跑中...
哒哒哒....
Apple...
产生这一系列的产品,需要有一个默认的工厂:
//默认的工厂
public class DefaultFactory {
public Car createCar(){
return new Car();
}
public AK47 createAK47(){
return new AK47();
}
public Apple createApple(){
return new Apple();
}
}
此时的测试程序,只要new出来一个默认工厂,就可以生产这一系列的产品了:
DefaultFactory factory = new DefaultFactory();
Car car = factory.createCar();
car.run();
AK47 ak = factory.createAK47();
ak.shoot();
Apple apple = factory.createApple();
apple.getName();
打印:
冒着烟奔跑中...
哒哒哒....
Apple...
当需要吧这一系列产品全换掉的话,把这个工厂换掉就可以了:
这里新建一个工厂,魔法工厂:
//哈利波特的魔法工厂
public class MagicFactory {
//交通工具:扫把
public Broom createBroom(){
return new Broom();
}
//武器:魔法棒
public MagicStick createMagicStick(){
return new MagicStick();
}
//食物:毒蘑菇
public MushRoom createMushRoom(){
return new MushRoom();
}
}
//扫帚
public class Broom{
public void run() {
System.out.println("扫帚摇着尾巴呼呼呼...");
}
}
//武器:魔法棒
public class MagicStick {
}
//食物:毒蘑菇
public class MushRoom {
}
但是此时,站在客户的角度,想把工厂从DefaultFactory换到MagicFactory:
有了之前的经验,这里自然就联想到,工厂创建的不能是具体的类,要是一个接口/或者是一个抽象类,比如你的Car,工厂创建的不能是Car,要是Car的父类,这样在换工厂的时候,下面的代码才可以不用改动。所以,我们要建一个抽象工厂,然后让DefaultFactory、MagicFactory都去继承/实现这个工厂。而且工厂的返回值,都是抽象类或者接口。
//抽象工厂
public abstract class AbstractFactory {
//生产 交通工具
public abstract Vehicle createVehicle();
//生产 武器
public abstract Weapon createWeapon();
//生产食物
public abstract Food createFood();
}
//交通工具
public abstract class Vehicle {
//实现由子类决定
public abstract void run();
}
//食物
public abstract class Food {
public abstract void printName();
}
//武器
public abstract class Weapon {
//
public abstract void shoot();
}
产品类:都继承产品的抽象类
public class Car extends Vehicle{
@Override
public void run() {
System.out.println("冒着烟奔跑中...");
}
}
//扫帚
public class Broom extends Vehicle{
@Override
public void run() {
System.out.println("扫帚摇着尾巴呼呼呼...");
}
}
//食物:毒蘑菇
public class MushRoom extends Food {
@Override
public void printName() {
System.out.println("mushroom");
}
}
public class Apple extends Food {
@Override
public void printName() {
System.out.println("apple");
}
}
public class AK47 extends Weapon{
public void shoot(){
System.out.println("哒哒哒....");
}
}
//武器:魔法棒
public class MagicStick extends Weapon {
@Override
public void shoot() {
System.out.println("fire hu hu hu ...");
}
}
魔法工厂继承抽象工厂:
//哈利波特的魔法工厂
public class MagicFactory extends AbstractFactory {
//交通工具:扫把
public Vehicle createVehicle(){
return new Broom();
}
//武器:魔法棒
public Weapon createWeapon(){
return new MagicStick();
}
//食物:毒蘑菇
public Food createFood(){
return new MushRoom();
}
}
DefaultFactory继承抽象工厂:
//默认的工厂
public class DefaultFactory extends AbstractFactory{
@Override
public Food createFood() {
return new Apple();
}
@Override
public Vehicle createVehicle() {
return new Car();
}
@Override
public Weapon createWeapon() {
return new AK47();
}
}
最终形成的类结构是这样的:
测试程序:
//换一个工厂,只需要改动这一处,就可以了,换一个工厂,就把生产的系列产品都换了
AbstractFactory factory = new MagicFactory(); //new DefaultFactory();
//换一个工厂
Vehicle vehicle = factory.createVehicle();
vehicle.run();
Weapon weapon = factory.createWeapon();
weapon.shoot();
Food food = factory.createFood();
food.printName();
DefaultFactory打印:
冒着烟奔跑中...
哒哒哒....
apple
**把工厂换为****MagicFactory
AbstractFactory factory = new DefaultFactory();
打印:
扫帚摇着尾巴呼呼呼...
fire hu hu hu ...
mushroom
抽象工厂:生产一系列的产品,如果你想换掉一系列的产品,或者你想在这一系列的产品上进行扩展,以及想对这一些列产品的生成过程进行控制,用抽象工厂。
抽象工厂和普通工厂的优缺点:
普通工厂:
可以在产品的维度上进行扩展,可以产生新的产品,可以产生新的产品的工厂。也就是说可以在产品的维度进行扩展。
在普通工厂想要产生产品系列,就会特别麻烦。产生一个产品,就会出现一个产品的Factory。就会出现”工厂泛滥”。
抽象工厂:
能换工厂,生产新的产品系列,但是不能产生新的产品品种,新添加一个产品,就要在抽象工厂里面加 createXXX(); 方法,所有子类都要实现这个方法。要改动的地方太多。
有没有一种工厂,结合普通工厂和抽象工厂的有点呢?
既可以随意添加产品品种,又很方便的添加产品系列? 没有。
Spring提供了一种方案,Spring的Bean工厂。
Spring说,你就不要这么Moveable m = new Car(); 就行new Car了,你给配置到配置文件里。
测试java读取properties配置文件反射生成对象
spring.properties:
VehicleType=com.lhy.springfactory.Car
测试代码:
public static void main(String[] args) throws Exception{
Properties props = new Properties();
props.load(Test.class.getClassLoader().getResourceAsStream("com/lhy/springfactory/spring.properties"));
String vehicleTypeName = props.getProperty("VehicleType");
System.out.println(vehicleTypeName);
//反射生成对象
Object o = Class.forName(vehicleTypeName).newInstance();
Moveable m = (Moveable)o;
m.run();
}
打印结果:
com.lhy.springfactory.Car
冒着烟奔跑中...
把配置文件换成或者Trian:
VehicleType=com.lhy.springfactory.Train
执行测试代码,打印:
com.lhy.springfactory.Train
小火车呜呜呜...
可以看到,只是改了配置文件,就可以动态控制生成的类了。代码都不用动。Spring就是这样的思路。
最简单的使用Spring:
引入必须的jar包,
Spring要求的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="v" class="com.bjsxt.spring.factory.Train">
</bean>
<!-- //v=com.bjsxt.spring.factory.Car -->
</beans>
测试程序:
package com.bjsxt.spring.factory;
import java.io.IOException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws Exception {
BeanFactory f = new ClassPathXmlApplicationContext("applicationContext.xml");
Object o = f.getBean("v");
Moveable m = (Moveable)o;
m.run();
}
}
配置文件配置的火车类,打印:
小火车呜呜呜...
换成Car,打印:
冒着烟奔跑中...
下面来模拟Spring的Bean工厂
Spring的BeanFactory ,就是一个容器,是用一个map实现的,就是从配置文件读取 <bean id="v" class="com.bjsxt.spring.factory.Train"/> 这样的配置,遍历解析这样的xml配置,以id为key,class后的类全限定名用反射生成的对象为value,放到这个map中去。当用的时候,直接map.get(id); 获取这个Bean对象。
ClassPathXmlApplicationContext是BeanFactory的一种实现。这里模拟这种实现。
这里模拟Spring的Bean工厂:
public interface BeanFactory {
Object getBean(String id);
}
ClassPathXmlApplicationContext:
public class ClassPathXmlApplicationContext implements BeanFactory{
//存放一个个Bean对象的容器,
private Map<String, Object> container = new HashMap<String, Object>();
// 构造方法找到配置文件,读取xml配置文件
public ClassPathXmlApplicationContext(String fileName) throws Exception{
SAXBuilder sb = new SAXBuilder();
Document doc = sb.build(this.getClass().getClassLoader()
.getResourceAsStream(fileName));
Element root = doc.getRootElement();
List list = XPath.selectNodes(root, "/beans/bean");
System.out.println(list.size());
for (int i = 0; i < list.size(); i++) {
Element bean = (Element) list.get(i);
String id = bean.getAttributeValue("id");
String clazz = bean.getAttributeValue("class");
Object o = Class.forName(clazz).newInstance();
container.put(id, o);
System.out.println(id + " " + clazz);
}
}
//读取配置文件,读取id为传进来的id的Bean,实例化
@Override
public Object getBean(String id) {
return container.get(id);
}
}
测试程序:
public static void main(String[] args) throws Exception{
BeanFactory f = new ClassPathXmlApplicationContext("com/lhy/springfactory/applicationContext.xml");
Object o = f.getBean("v");
Moveable m = (Moveable)o;
m.run();
Train trian = (Train)f.getBean("trian");
trian.run();
}
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="v" class="com.lhy.springfactory.Car"/>
<bean id="trian" class="com.lhy.springfactory.Train"/>
<!-- //v=com.bjsxt.spring.factory.Car -->
</beans>
打印:
2
v com.lhy.springfactory.Car
trian com.lhy.springfactory.Train
冒着烟奔跑中...
小火车呜呜呜...
这样,就把类配置在了配置文件里,更灵活了!