设计模式-Spring中常用的设计模式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式-Spring中常用的设计模式相关的知识,希望对你有一定的参考价值。

参考技术A

设计模式是一种思想,是一种更快更好更优雅地解决问题的一种思想。这种思想在很多优秀的框架中都有落地。比如 Spring 框架。

接下来,我将从我的角度出发,粗浅的想一下,Spring 框架中常用的一些设计模式。

Spring 框架核心解决的问题是什么呢?个人理解是为了解决对象之间复杂的依赖关系,降低耦合。或者可以这么说,做项目我们也可以什么框架都不用,实现功能的时候,创建多个类,自己去维护类什么时候创建、使用、销毁等生命周期以及类之间的关系。自己维护的很好或者功能很简单的话,也可以不使用框架。框架本身也就是一个黑盒工具而已,开源使我们可以将其白盒化。

既然Spring框架的核心内容假设是解决对象之间复杂的依赖关系。 通俗来说便是“要啥给啥”。为了实现这个目的,Spring 核心的 IOC 容器出现了,对象都放在这个容器里,需要的时候从里面取。那就涉及到几点:

1. 创建对象,保存对象,保存对象之间的关联关系

2. 获取对象

涉及到如何创建,是只创建一次还是创建多次,有关联关系,先创建A还是先创建B等。 单例模式、原型模式、工厂模式、策略模式 可以来帮忙。

工厂模式:

Spring 中的 BeanFactory、FactoryBean

单例模式

保证一个类仅有一个实例,并提供一个全局访问点。Spring 下默认创建的 Bean 都是单例对象。

常用的单例模式写法又有很多:最简单的就是 懒汉式了,还有 饿汉式、注册式、序列化方式、枚举方式等。

原型模式

Java 中的克隆对象。以某个对象为原型,复制出一个新的对象。两个对象内容相同,但是对象实例不同。

用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

策略模式:

一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

获取对象的时候,有策略方法,也可以通过代理进行功能增强等。策略模式、代理模式、模板方法模式、适配器模式、装饰器模式等

代理模式:

为其他对象提供一种代理以控制对这个对象的访问。从结构上来看和 装饰器模式类似, 但 Proxy 是控制,更像是一种对功能的限制,而 装饰器是增加职责。

Spring 的 AOP 代理,应该是很出名的。JdkDynamicAopProxy、Cglib2AopProxy。

模板模式:

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。比如 JdbcTemplate

适配器模式

将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

Spring AOP 模块对 BeforeAdvice、AfterAdvice、ThrowsAdvice 三种通知类型的支持实际上是借助适配器模式来实现的,这样的好处是使得框架允许用户向框架中加入自己想要支持的任何一种通知类型,上述三种通知类型是 Spring AOP 模块定义的,它们是 AOP 联盟定义的 Advice 的子类型。

属于结构型模式,适配类与被适配类之间没有必然联系。满足 has-a 关系。

装饰器模式

属于结构型模式。满足 is-a 关系。

Spring 中用到的包装器模式在类名上有两种表现:一种是类名中含有 Wrapper,另一种是类名中含有 Decorator。基本上都是动态地给一个对象添加一些额外的职责。

观察者模式:

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象 都得到通知并被自动更新。

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

Spring 中 Observer 模式常用的地方是 Listener 的实现。如 ApplicationListener。

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

spring(bean生命周期用到的设计模式常用注解)

面试官常问的设计模式及常用框架中设计模式的使用

Spring中应用的那些设计模式

spring中用到哪些设计模式?

初探设计模式5:Spring涉及到的9种设计模式

Spring中的用到的设计模式大全