学习笔记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中常用的设计模式的主要内容,如果未能解决你的问题,请参考以下文章

IOS开发-OC学习-常用功能代码片段整理

学习笔记——定义切面优先级 ;Spring中的JdbcTemplate;JdbcTemplate的常用API

Spring学习笔记 IOC

学习笔记:python3,代码片段(2017)

Spring学习笔记--Spring IOC

Spring 学习笔记03