Spring中常见的设计模式——原型模式
1、原型模式应用场景
当遇到大量耗费劳动力的 get,set赋值场景时,如下:
public class SetGetParam { public void setParam(UserDto userDto) { User user = new User(); user.setAge(userDto.getAge()); //...
userDao.addUser(user); } }
原型模式(Prototype pattern)是指原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。原型模式主要适用于以下:
(1)类初始化消耗资源较多;
(2)使用new 生成一个对象需要非常繁琐的过程(数据准备访问权限等);
(3)构造函数比较复杂;
(4)在循环体中产生大量对象;
在spring中用到的原型模式有:scope="prototype" ,还有常用的JSON.parseObject()也是一种原型模式
2、浅克隆
创建具体需要克隆的类:
@Data public class User { private String name; private Integer age; private List<String> hobbies; public UserDto clone() { UserDto dto = new UserDto(); dto.setAge(this.age); dto.setName(this.name); dto.setHobbies(this.hobbies); return dto; } }
创建Client:
public class Client { private User user; public Client(User user) { this.user = user; } public UserDto startClone(User user) { return user.clone(); } }
测试克隆,对比复制过来的值是否有自己的地址,还是用的原来的地址
public class PrototypeTest { public static void main(String[] args) { //创建具体需要克隆的对象 User user = new User(); user.setName("皮肤黝黑的小白"); user.setHobbies(new ArrayList<>()); System.out.println(user); //创建Client对象,准备开始克隆 Client client = new Client(user); UserDto dto = client.startClone(user); System.out.println(dto); System.out.println(user.getHobbies() == dto.getHobbies()); System.out.println(user.getName() == dto.getName()); } }
结果:
User(name=皮肤黝黑的小白, age=null, hobbies=[]) UserDto(name=皮肤黝黑的小白, age=null, hobbies=[]) true true
从测试结果可以看出:hobbies和name的内存地址是相同的,这说明我们并没有重新创建对象,这就是浅克隆。
3、深克隆
采用序列化反序列化克隆,实现深克隆,
@Data public class UserDeepClone implements Cloneable { private String name; private Integer age; private List<String> hobbies; @Override protected Object clone() throws CloneNotSupportedException { return this.deepClone(); } public UserDto deepClone() { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); UserDto dto = (UserDto) ois.readObject(); return dto; } catch (Exception e) { e.printStackTrace(); return null; } } }
public class DeepCloneTest { public static void main(String[] args) { DeepCloneTest deepCloneTest = new DeepCloneTest(); UserDeepClone user = new UserDeepClone(); user.setName("皮肤黝黑的小白"); user.setHobbies(new ArrayList<>()); System.out.println(user); UserDto dto = null; try { dto = (UserDto) deepCloneTest.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } System.out.println(dto); System.out.println(user.getName() == dto.getName()); System.out.println(user.getHobbies() == dto.getHobbies()); } }
4、克隆破坏单例
深克隆会破坏单例,其实防御方式很简单,单例类不要实现Cloneable接口即可。
5、ArrayList中clone()方法的源码
public Object clone() { try { ArrayList<?> v = (ArrayList<?>) super.clone(); v.elementData = Arrays.copyOf(elementData, size); v.modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); } }