学习笔记Spring中常用的设计模式
Posted 陌筱明
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习笔记Spring中常用的设计模式相关的知识,希望对你有一定的参考价值。
简介
此文基于之前看【Spring核心原理】整理的学习笔记,建议手撸一遍代码加深印象。
一. Spring用到的设计模式类别
设计模式名称 | 举例 | 描述 |
---|---|---|
单例模式 | ApplocationContext | 一个Bean只有一个实例 |
原型模式 | scope=“prototype” | 指定创建对象的种类,并且通过复制这些原型创建新的对象 |
工厂模式 | BeanFactory | 只对结果负责,封装创建过程 |
装饰者模式 | BeanWrapper | 包装,同源 |
代理模式 | ProxyFactoryBean, JdkDynamicAopProxy,CglibAopProxy | 找人办事,增强职责 |
委派模式 | DispatcherServlet | 项目找外包公司做 |
策略模式 | HandlerMapping | 用户选择,结果统一 |
适配器模式 | HandlerAdapter | 兼容转换头 |
模板模式 | JdbcTemplate | 流程标准化,自己实现定制 |
观察者模式 | ContextLoaderListener | 在任务完成时通知 |
1. 创建型模式
- 工厂模式(Factory Pattern)
- 单例模式 (Singleton Pattern)
- 原型模式 (Prototype Pattern)
2. 结构性模式
- 适配器模式 (Adapter Pattern)
- 装饰者模式 (Decorator Pattern)
- 代理模式 (Proxy Pattern)
3. 行为型模式
- 策略模式 (Strategy Pattern)
- 模板模式 (Template Pattern)
- 委托模式 (Delegate Pattern)
- 观察者模式 (Observer Pattern)
二. 设计模式详解
1. 工厂模式
1.1 简单工厂模式
简单工厂模式(Simple Factory Pattern)是指由一个工厂对象决定创建哪一种产品类的实例,但它不属于GoF的23种设计模式.
通过反射的方式生成对象
缺点: 工厂类的职责相对过重,不易于扩展过于复杂的逻辑代码
public interface Course {
void record();
}
public class JavaCourse implements Course {
@Override
public void record() {
System.out.println("java课程");
}
}
public class CourseFactory {
public static Course getCourse(Class<? extends Course> clas) {
try {
if (null != clas) {
return clas.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
Course course=CourseFactory.getCourse(JavaCourse.class);
course.record();
}
}
1.2 工厂方法模式
工厂方法模式(Fatory Method Pattern)是指定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法模式让类的实例化推迟到子类进行,在工厂方法模式中用户只需要关心所需产品对应的工厂,无需关心创建细节,而加入新的产品也符合开闭原则
public interface Course {
void record();
}
public class JavaCourse implements Course {
@Override
public void record() {
System.out.println("java课程");
}
}
public interface CourseFactory {
Course getCourse();
}
public class JavaCourseFacotry implements CourseFactory {
@Override
public Course getCourse() {
return new JavaCourse();
}
public static void main(String[] args) {
Course course = new JavaCourseFacotry().getCourse();
course.record();
}
}
1.3 抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是指提供一个创建一系列相关或者相关依赖对象的接口,无需指定他们的具体实现类.
spring中应用得最广泛的一种设计模式
public interface Note {
void edit();
}
public interface Video {
void record();
}
public class JavaNote implements Note {
@Override
public void edit() {
System.out.println("编写Java笔记");
}
}
public class JavaVideo implements Video {
@Override
public void record() {
System.out.println("录制Java视频");
}
}
public interface CourseFactory {
Note getNote();
Video getVideo();
}
public class JavaCourseFactory implements CourseFactory {
@Override
public Note getNote() {
return new JavaNote();
}
@Override
public Video getVideo() {
return new JavaVideo();
}
public static void main(String[] args) {
CourseFactory courseFactory = new JavaCourseFactory();
courseFactory.getNote().edit();
courseFactory.getVideo().record();
}
}
2. 单例模式
单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点,单例模式是创建型模式,Spring框架中ApplicationContext,数据库的连接池等也是单例形式
2.1 饿汉模式
饿汉单例模式在类加载的时候就立即初始化,并且创建对象了,它绝对线程安全,在线程还没出现之前就实例化了,不存在访问安全问题
缺点: 类加载时候就初始化,不管用与不用都占用空间,浪费内存
public class HungrySingleton {
private static final HungrySingleton INSTANCE = new HungrySingleton();
private HungrySingleton() { }
public static HungrySingleton getInstance() {
return INSTANCE;
}
}
2.2 懒汉模式
懒汉模式的特点是: 被外部类调用的时候内部类才会初始化
下面采用内部类方式实现
public class LazySingleton {
private LazySingleton() { }
public static LazySingleton getInstance() {
return LazyHolder.INSTANCE;
}
private static class LazyHolder {
private static final LazySingleton INSTANCE = new LazySingleton();
}
}
2.3 破坏单例的方式
2.3.1反射破坏
public class Main {
public static void main(String[] args) throws Exception {
LazySingleton s1 = LazySingleton.getInstance();
//无聊的情况下,进行破坏
Constructor<LazySingleton> constructor = LazySingleton.class.getDeclaredConstructor();
//强制访问
constructor.setAccessible(true);
//暴力初始化
Object s2 = constructor.newInstance();
System.out.println(s1 == s2);
}
}
改善LazySingleton防止反射破坏
public class LazySingleton {
private LazySingleton() {
if(LazyHolder.INSTANCE!=null){
throw new RuntimeException("不允许创建多个实例");
}
}
public static LazySingleton getInstance() {
return LazyHolder.INSTANCE;
}
private static class LazyHolder {
private static final LazySingleton INSTANCE = new LazySingleton();
}
}
2.3.2序列化破坏单例
public class SeriableSingleton implements Serializable {
private static final SeriableSingleton INSTANCE = new SeriableSingleton();
private SeriableSingleton() { }
public static SeriableSingleton getInstance() {
return INSTANCE;
}
}
public static void main(String[] args) {
SeriableSingleton s1 = null;
SeriableSingleton s2 = SeriableSingleton.getInstance();
FileOutputStream fos = null;
try {
fos = new FileOutputStream("SeriableSingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("SeriableSingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (SeriableSingleton) ois.readObject();
ois.close();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
添加readResolve()方法即修复此问题
public class SeriableSingleton implements Serializable {
private static final SeriableSingleton INSTANCE = new SeriableSingleton();
private SeriableSingleton() { }
public static SeriableSingleton getInstance() {
return INSTANCE;
}
private Object readResolve() {
return INSTANCE;
}
}
ObjectInputStream
对象的序列化过程通过ObjectOutputStream和ObjectInputputStream来实现的,那么带着刚刚的问题,分析一下ObjectInputputStream 的readObject 方法执行情况到底是怎样的。
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
//此处省略部分代码
Object obj;
try {
//这里创建的这个obj对象,就是本方法要返回的对象,也可以暂时理解为是ObjectInputStream的readObject返回的对象。
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}
//此处省略部分代码
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
-
isInstantiable:如果一个serializable/externalizable的类可以在运行时被实例化,那么该方法就返回true。
-
desc.newInstance:该方法通过反射的方式调用无参构造方法新建一个对象。
-
hasReadResolveMethod:如果实现了serializable 或者 externalizable接口的类中包含readResolve则返回true
-
invokeReadResolve:通过反射的方式调用要被反序列化的类的readResolve方法。
所以,原理也就清楚了,主要在Singleton中定义readResolve方法,并在该方法中指定要返回的对象的生成策略,就可以防止单例被破坏。
为什么序列化可以破坏单例了?
答:序列化会通过反射调用无参数的构造方法创建一个新的对象。
2.4 注册式单例模式
注册式单例模式又称登记式单例模式,都是将每一个实例登记到某个地方,使用唯一的标识来获取实例.注册式单例模式有两种:一种为枚举式单例模式,另一种为容器式单例模式
2.4.1 枚举单例模式
public enum EnumSingleton {
INSTANCE;
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public static EnumSingleton getInstance() {
return INSTANCE;
}
public static class User {
}
public static void main(String[] args) {
EnumSingleton s1 = EnumSingleton.getInstance();
EnumSingleton s2 = EnumSingleton.getInstance();
//true
System.out.println(s1 == s2);
}
}
测试反射破坏枚举单例模式
EnumSingleton s1 = EnumSingleton.getInstance();
//无聊的情况下,进行破坏
Constructor<EnumSingleton> constructor = EnumSingleton.class.getDeclaredConstructor();
//强制访问
constructor.setAccessible(true);
//暴力初始化
Object s2 = constructor.newInstance();
System.out.println(s1 == s2);
下面异常显示没有找到构造方法,枚举类只有protected类型的构造方法
测试序列化破坏枚举单例模式
EnumSingleton s1 = null;
EnumSingleton s2 = EnumSingleton.getInstance();
FileOutputStream fos = null;
try {
fos = new FileOutputStream("EnumSingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("EnumSingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (EnumSingleton) ois.readObject();
ois.close();
//输出true
System.out.println(s1==s2);
} catch (Exception e) {
e.printStackTrace();
}
查看ObjectInputStream的readObject0方法
readEnum方法实现如下
private Enum<?> readEnum(boolean unshared) throws IOException {
if (bin.readByte() != TC_ENUM) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false);
if (!desc.isEnum()) {
throw new InvalidClassException("non-enum class: " + desc);
}
int enumHandle = handles.assign(unshared ? unsharedMarker : null);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(enumHandle, resolveEx);
}
String name = readString(false);
Enum<?> result = null;
Class<?> cl = desc.forClass();
if (cl != null) {
try {
@SuppressWarnings("unchecked")
Enum<?> en = Enum.valueOf((Class)cl, name);
result = en;
} catch (IllegalArgumentException ex) {
throw (IOException) new InvalidObjectException(
"enum constant " + name 以上是关于学习笔记Spring中常用的设计模式的主要内容,如果未能解决你的问题,请参考以下文章