原型模式
1.引出原型模式的思路
- Java中的Object类是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个java对象复制一份,需要实现clone的类要实现Cloneable接口,该接口表示该类能够复制而且具有复制的能力
2.原型模式(Prototype)的介绍
- 用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建对象
- 原型模式允许一个对象创建另一个可定制的对象,无需知道创建的细节
- 工作原理:通过将一个原型对象传给要发动创建的对象,这个要发动创建的对象通过请求原型模型的拷贝来完成对象的创建,即原型模式.clone()
3.原理结构图UML图
- Prototype:原型类,声明一个克隆自己的接口
- ConcretePrototype:具体的圆形类,实现一个克隆自己的操作
- client:发动创建的对象,让一个原型对象克隆自己,从而创建一个新的对象(属性一样)
4.代码示例
-
实现cloneable接口,重写clone方法;注意,不重写的话也可以获取到对象,但是获取到的和发动创建的对象是同一个对象,即只是克隆了引用,hashCode是一样的
public class Sheep implements Cloneable{ private String name; private int age; //getter、setter、构造器 .................. //重写克隆方法:调用的是父类默认的clone方法来拷贝sheep类 @Override protected Object clone(){ Object sheep = null; try { sheep = super.clone(); } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // TODO Auto-generated method stub return sheep; } }
-
发动克隆
public class client { public static void main(String[] args) { Sheep sheep = new Sheep("Tom", 2); Sheep sheep2 = (Sheep)sheep.clone(); System.out.println(sheep.hashCode()); //366712642 System.out.println(sheep2.hashCode()); //1829164700 }
5.原型模式在Spring中的使用
-
Spring中配置bean的时候,scope属性可以配置一个prototype值,该值指定该bean的创建是使用原型模式
-
在创建ioc容器后,通过getBean()获取bean对象时,往里追可以发现在核心方法处spring对bean的scope属性进行了判断,配置了prototype时,进入了原型模式的使用
创建ioc的时候创建了一个这样的容器实例
该类中对getBean()方法的定义如下
public Object getBean(String name) throws BeansException { this.assertBeanFactoryActive(); //我们先进入getBeanFactory()方法,看看得到的是哪个BeanFactory,再寻找他的getBean()方法 return this.getBeanFactory().getBean(name); } //追踪发现这是一个抽象方法,应该是由AbstractApplicationContext的子类实现 public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
往下找getBeanFactory()方法的实现
发现在AbstractRefreshableApplicationContext中实现了这个方法,返回的是一个DefaultListableBeanFactory,也就是调用了DefaultListableBeanFactory的getBean()方法
private DefaultListableBeanFactory beanFactory; //实现了getBeanFactory方法,确保了线程安全的前提下返回了一个DefaultListableBeanFactory public final ConfigurableListableBeanFactory getBeanFactory() { synchronized(this.beanFactoryMonitor) { if (this.beanFactory == null) { throw new IllegalStateException("BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext"); } else { return this.beanFactory; } } }
去到DefaultListableBeanFactory中没有找到getBean()方法,于是往他的父类去找
从这个bean的父类的父类AbstractBeanFactory可以看到这个getBean(),同时调用了核心方法doGetBean();
public Object getBean(String name) throws BeansException { return this.doGetBean(name, (Class)null, (Object[])null, false); }
进入到doGetBean()方法可以发现,spring对参数进行了判断,对应创建了原型模式的对象
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { String beanName = this.transformedBeanName(name); Object sharedInstance = this.getSingleton(beanName); ................. if (mbd.isSingleton()) { sharedInstance = this.getSingleton(beanName, () -> { try { return this.createBean(beanName, mbd, args); } catch (BeansException var5) { this.destroySingleton(beanName); throw var5; } }); bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd); //判断了是否设置了原型模式 } else if (mbd.isPrototype()) { var11 = null; Object prototypeInstance; try { this.beforePrototypeCreation(beanName); //进入了原型模式的对象创建 prototypeInstance = this.createBean(beanName, mbd, args); } finally { this.afterPrototypeCreation(beanName); } bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { ...................
同时可以发现AbstractBeanFactory也是BeanFactory的实现类
6.关于原型模式的浅拷贝与深拷贝
1.浅拷贝
-
对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象属性
-
对于数据类型是引用类型的成员变量(对象、数组等),浅拷贝会进行引用传递,即将该成员变量实例的引用地址复制到新的对象属性中,新对象属性中的成员变量所指向的还是和原型成员变量所指向的是同一个实例,在一个对象中修改成员变量会影响到其它对象中的成员变量
-
浅拷贝就是使用Object默认的clone()来实现的
2.深拷贝
-
复制所有基本类型的成员变量值
-
为所有引用数据类型的变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,也就是说,对象进行深拷贝要对整个对象进行拷贝
-
实现方式
-
重写clone方法来实现深拷贝
public class DeepCloneableTarget implements Serializable,Cloneable{ private static final long serialVersionUID= 1L; private String cloneName; private String cloneClass; ....................... } public class DeepPrototype implements Cloneable{ private String name; private DeepCloneableTarget deepCloneableTarget;//引用类型 public DeepPrototype() { // TODO Auto-generated constructor stub super(); } //深拷贝:方式一:重写clone() @Override protected Object clone() throws CloneNotSupportedException { // TODO Auto-generated method stub Object deep = null; DeepPrototype deepPrototype = (DeepPrototype)deep; deepPrototype.deepCloneableTarget = (DeepCloneableTarget)deepCloneableTarget.clone(); return deepPrototype; }
这个方法不推荐,当有多个引用数据类型时,代码量特别多,而且复杂
-
通过对象序列化l实现深拷贝(拷贝的类继承Serializable,还有内部成员变量时对象的类也要继承Serializable)
//深拷贝:方式二:通过对象的序列化实现 public Object deepClone() { //创建流 ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ByteArrayInputStream bis =null; ObjectInputStream ois = null; try { //序列化 bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this);//当前的对象以流的方式输出 //反序列化 bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); DeepPrototype deepPrototype = (DeepPrototype)ois.readObject(); return deepPrototype; } catch (Exception e) { // TODO: handle exception e.printStackTrace(); return null; }finally { try { bos.close(); oos.close(); bis.close(); ois.close(); } catch (Exception e2) { // TODO: handle exception } } }
推荐使用该方法,因为该方法直接将整个对象进行流操作,将整个对象序列输出,再反序列输入,就会自动拷贝一个对象,缺陷在于流操作效率较低,而且基本数据类型也要进行流操作。
-
小结
- 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时提高效率
- 不是初始化对象,而是可以动态的克隆当前对象以及他的状态
- 如果原始对象发生改变,其他克隆对象的也会发生相应的变化,无需修改代码
- 在实现深克隆的时候可能需要比较复杂的代码
- 缺陷:需要为每一个类配备一个克隆方法,对于已经存在的类来说得修改源代码,这违背了ocp原则