栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring5核心原理学习笔记——《Spring5核心原理与30个类手写实战》

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring5核心原理学习笔记——《Spring5核心原理与30个类手写实战》

系列文章目录

系列文章目录

文章目录

前言

一、软件架构设计原则?

        1.1 开闭原则

        1.2 依赖倒置原则

        1.3 单一职责原则

        1.4 接口隔离原则

        1.5 迪米特法则

        1.6 里氏替换原则

        1.7 合成复用原则

        1.8 设计原则总结

二、Spring中常用的设计模式

2.1 设计模式好处都有啥?

2.2 工厂模式 

        2.2.1 简单工厂

        2.2.2 工厂方法模式

        2.2.3 抽象工厂模式

        2.3 单例模式

        2.3.1 IDEA线程调试模式

         2.3.2 双检锁单例

        2.3.3 静态内部类单例

         2.3.4 单例的破坏

总结



前言

        阅读《Spring核心原理和30个手写实战》做的笔记:
        设计原则、设计模式、Spring源码设计、


一、软件架构设计原则?

        六大设计原则+合成复用原则

        六大设计原则包括:开闭原则、依赖倒置、单一职责、迪米特法则、接口隔离、里氏替换原则

        1.1 开闭原则

        对扩展开放,对修改关闭,核心思想是面向抽象编程,指导我们如何建立稳定,灵活的系统。

        1.2 依赖倒置原则

        设计代码结构时,高层模块不应该依赖低层模块,二者应该依赖其抽象。

        好处是,减少代码耦合性,提高稳定性和可读性,可维护性,降低修改程序带来的风险。

        1.3 单一职责原则

        使类被变更的原因只能有一个。

        好处是,对实现解耦,提高后期可维护性,降低变更带来的风险,同时提高代码的可读性。

        1.4 接口隔离原则

        建议设计多个专门的接口,避免使用单一的总接口,使用者不应该依赖他不需要的接口,指接口方法尽量要少。

        符合高内聚、低耦合思想,我们在设计接口的时候要多花时间去思考,考虑业务模型,包括对以后可能发生变更的地方做一些预判,对于抽象、对于业务模型的理解非常重要。

        1.5 迪米特法则

        最少知道原则,指一个对象应该具有最少的成员类,出现在成员变量,方法输入输出参数中的都是成员类,而出现在方法体内部的不属于成员类。

        1.6 里氏替换原则

        指每个调用父类的地方,都可以替换成它的子类,而代码整体的逻辑没有变化,子类可以扩展父类的功能,但是不能改变父类原有的功能。

        优点是约束继承泛滥,是开闭原则的一种体现;加强程序健壮性,同时变更也可以做到非常好的兼容性,提高程序的可维护性,降低需求变更引入的风险。

        1.7 合成复用原则

        Composite/Aggregate Reuse Principle,CARP

        指尽量使用对象组合(has-a)/聚合(contains-a)而不是继承关系达到软件复用的目的。

        可以使系统更加灵活,降低类之间的耦合性。

        继承叫做白箱复用,相当于把所有的实现暴露给子类;组合/聚合称为黑箱复用,我们是无法获取到类以外对象的实现的细节的。

        1.8 设计原则总结

        设计原则是设计模式的基础,在实际开发中,并不要求所有代码都遵循设计原则,我们要考虑人力、时间、成本、质量,不能刻意追求完美,但要在适当的场景遵循设计原则,这体现的是一种平衡取舍,可以帮助我们设计出更加优雅的代码结构。————书中原话

二、Spring中常用的设计模式

2.1 设计模式好处都有啥?
  1. 写出优雅的代码;
  2. 更好的重构项目;

        设计模式从来都不是单个模式独立使用的。在实际应用中,通常是将多个设计模式混合使用,你中有我,我中有你。————书中原话 

2.2 工厂模式 

        属于创建型模式,注重产品实例的提供,不注重构建过程。

        2.2.1 简单工厂

        简单工厂模式是指一个工厂对象决定创建哪一种产品类的实例,工厂和产品类都不抽象。

        缺点:工厂职责过重,不易于扩展过于复杂的产品结构。 

        2.2.2 工厂方法模式

        工厂抽象,产品不抽象,让类的实例化延迟到子类中进行。

        适用场景:创建对象需要大量重复的代码;调用方不依赖实例创建和实现细节;一个类通过子类来指定创建哪个对象。

        缺点:类个数多,增加了系统的复杂度,系统的抽象程度和理解难度。

        2.2.3 抽象工厂模式

        工厂抽象,产品抽象成产品等级结构和产品族。

        缺点:规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口,增加系统抽象性和理解难度。

        2.3 单例模式

        属于创建型模式,确保一个类在全局只有一个实例可被访问。

        J2EE 标准中的ServletContext、ServletContextConfig 等、Spring框架应用中的ApplicationContext、数据库的连接池都是单例模式。

        饿汉式单例:类加载立即初始化,优点是不用加任何锁、执行效率高,用户体验比懒汉式好,缺点是不用的话浪费空间。ApplicationContext是饿汉式。

        懒汉式单例:被外部类调用的时候才会被加载。

        2.3.1 IDEA线程调试模式

        手动控制线程的执行顺序来跟踪内存的变化。

        准备代码:

public class LazySimpleSingleton {

    private static LazySimpleSingleton lazySimpleSingleton = null;

    private LazySimpleSingleton() {
    }

    public  static LazySimpleSingleton getInstance() {
        if(null == lazySimpleSingleton) {
            lazySimpleSingleton = new LazySimpleSingleton();
        }
        return lazySimpleSingleton;
    }
}
public class LazySimpleSingletonTest {

    public static void main(String[] args) {
        Runnable runnable = ()->{
            LazySimpleSingleton lazySimpleSingleton = LazySimpleSingleton.getInstance();
            System.out.println(Thread.currentThread().getName()+":"+lazySimpleSingleton);
        };
        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable);
        t1.start();
        t2.start();
        System.out.println("END");
    }

}

 

        使用鼠标右键单击断点,切换Thread模式

 

        开始Debug,控制台可以自由切换Thread运行状态

     通过切换线程,调试,会出现下面的情况,线程1和线程0都进入了new LazySimpleSingleton()

 以上则是演示IDEA线程模式调试过程

         2.3.2 双检锁单例
public class LazySimpleSingleton {
    // volatile 禁止指令重排序
    private volatile static LazySimpleSingleton lazySimpleSingleton = null;

    private LazySimpleSingleton() {
    }

    public  static LazySimpleSingleton getInstance() {
        if(null == lazySimpleSingleton) {
            synchronized (LazySimpleSingleton.class) {
                if(null == lazySimpleSingleton) {
                    lazySimpleSingleton = new LazySimpleSingleton();
                }
            }
        }
        return lazySimpleSingleton;
    }
}

        2.3.3 静态内部类单例

        兼顾饿汉式单例模式的内存浪费问题和 synchronized 的性能问题,完美屏蔽了这俩个缺点

public class LazyInnerClassSingleton {

    private LazyInnerClassSingleton() {

    }

    
    public static final LazyInnerClassSingleton getInstance() {
        return LazyHolder.LAZY;
    }

    
    private static class LazyHolder {
        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
    }

}

         2.3.4 单例的破坏

        反序列化破坏单例,可以通过默认构造器抛异常来避免;

        序列话破坏单例,给类增加 readResolve() 方法来避免;

public class LazyInnerClassSingleton implements Serializable {

    private LazyInnerClassSingleton() {

    }
    
    public static final LazyInnerClassSingleton getInstance() {
        return LazyHolder.LAZY;
    }

    
    private static class LazyHolder {
        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
    }

    private Object readResolve() {
        return getInstance();
    }

}

        为什么加上 readResolve() 就可以了呢,阅读 jdk 源码,从ObjectInputStream 类的方法开始。 

isInstantiable()会判断构造器是否为空

        判断 readResolveMethod 是否为空

 

         虽然增加 readResolve()方法返回实例解决了单例模式被破坏的问题,但是实际上实例化了两次,只不过新创建的对象没有被返回,如果对象创建频率加快,意味着内存分配开销也会随之增大。

        单例模式除了上面的,还有枚举值单例和容器式单例,但是容器式单例是非线程安全的。 


 

 

 

 

 

 

 

 

 

 

总结

        果然边看书边记笔记不容易困。

        未完待续......

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/530717.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号