浅谈Spring中设计模式(单例、工厂、观察者、建造者)

224 阅读13分钟

单例模式

单例模式介绍

单例模式属于三种设计模式分类中的创建者模式,与工厂模式相同,其都是用于进行对象创建的设计模式。单例模式实例化出来的对象在内存中有且仅有一个,可以实现减少内存的开销,共享共用资源,应用范围广,且属于最简单的一种设计模式。下面逐一介绍单例模式的各种实现。

类图

单例模式的实现方式

(1)懒汉式

        public class LazySingleton{
        //单例对象声明
                private static LazySingleton lazySingleton = null;
                //私有化构造器
                private LazySingleton(){
                }
                //获取单例对象,当调用该方法时才去判断是否存在对象,不存在实例化一个对象
                public static synchronized LazySingleton getSingletonInstance(){ //需要加锁才能保证线程安全
                        if(lazySingleton == null){
                                return new LazySingleton();
                        }
                        return lazySingleton;
                }
        }

(2)饿汉式

 public class HungrySingleton{
                //私有化构造器
                private HungrySingleton(){
                }
                //单例对象实例化,在类初始化的时候就实例化该对象
                private static HungrySingleton hungrySingleton = getSingletonInstance();
                
                //获取单例对象
                public static HungrySingleton getSingletonInstance(){
                        if(hungrySingleton == null){
                                return new HungrySingleton();
                        }
                        return hungrySingleton;
                }
        }

(3)双重校验锁

   public class DoubleCheckedLockSingleton{
                //单例对象声明
                private static DoubleCheckedLockSingleton doubleCheckedLockSingleton = null;
                //私有化构造器
                private DoubleCheckedLockSingleton(){
                }
                //获取单例对象,当调用该方法时才去判断是否存在对象,不存在实例化一个对象
                public static DoubleCheckedLockSingleton getSingletonInstance(){ 
                        if(doubleCheckedLockSingleton == null){
                            synchronized(DoubleCheckedLockSingleton.class){ //将锁的位置换在实例化对象的地方
                                    if(doubleCheckedLockSingleton == null){ //因为有可能两个线程同时进入到上一层判断,因此需要在同步代码块中再添加一次校验
                                            return new DoubleCheckedLockSingleton();
                                    }
                            } 
                        }
                        return doubleCheckedLockSingleton;
                }
        }

(4)内部类

public class StaticSingleton {  
        //静态内部类
    private static class StaticSingletonHolder {  
                   private static final StaticSingleton INSTANCE = new StaticSingleton();  
    }  
    //私有化构造器
    private StaticSingleton (){
    }  
    //通过内部类来获取单例对象
    public static final StaticSingleton getInstance() {  
        return StaticSingletonHolder.INSTANCE;  
    }  
}

看源码设计模式前我的疑问:

问题1,spring 中的bean 默认都是单例的,是怎么实现的,如果不是单例会怎么样?

通过RootBeanDefinition.isSingleton()判断是否为单例,单例的话会走单例模式,非单例的话直接new bean

问题2,spring 是怎么写代码实现单例的,有几种方式

1.线程安全模式双重判断(IOC容器初始化中的getSingel())2.TRUE 的方式 3.其他

问题3,spring 单例拓展,IOC容器如何解决循环依赖问题,三级缓存是那三级缓存,实现原理是什么。

原理解析:

我们知道Spring的底层支持是IOC,IOC的refresh()初始化过程中会先去获取所有被管理bean的BeanDefinition,Spring框架中的BeanDefinition会有一个作用域scope,当我们没有进行设置时,Bean工厂根据其默认创建单例Bean,因此Spring框架中我们所用到的Bean通常都是单例的。

源码流程截图如下:

注解方式还是XML配置方式都是一样的,最终bean都会被解析获取beanDefinition,放到IOC的map缓存中,供正在创建bean的时候去获取基本信息初始化。

由此我们可见创建的bean默认都是单例的

补充:我们都知道在springboot 启动时会默认加载tomcat 容器,那springboot是怎么做到的呢,重点是在IOC容器初始化时候的重载了onrefresh()方法

OK ,我们继续说Spring 中单例模式的应用

Spring Bean采用了双重校验锁以及ConcurrentHashMap作为容器实现了单例设计,并且通过三级缓存解决循环依赖的问题。 我们来看下Spring Bean的创建方法,在AbstractBeanFactory类中。

如果能拿到bean信息,就返回bean

获取单例bean 会通过单例模式去三级缓存中获取,截图中的分别是一级,三级,二级缓存,三级缓存主要用来解决单例bean的循环依赖问题,具体这里就不展开了,有兴趣的同学仔细查阅资料

如果缓存中没有拿到对象,则会尝试去一级级缓存在获取,如果还是没有,则调用传入函数的方法,也就是下图中的createBean()去创建bean,至于bean怎么创建,这涉及到bean的生命周期,包括bean的前置处理,后置处理,bean实例化,初始化方法等,另外bean的创建于IOC容器初始化阶段的beanDefinition 对bean的创建提供了至关重要的作用,有兴趣的同学可以自行去研究下bean创建的详细过程,其中包括正常创建,懒加载等等。

doCreateBean ->bean初始化前置、后置处理->bean 实例化 ->beanPostProcessor (before ->initMehtod ->after)

创建完的bean会被放到一级缓存在缓存中,并且会从二级缓存在去掉

在查看AOP源码的时候也看到了很多.TRUE 方式的单例写法,这种方式有点类似静态内部类

Spring源码中还有其他方式的单例写法欢迎同学们及时补充

工厂模式

工厂模式介绍

工厂模式属于三种设计模式分类中的创建者模式。工厂模式主要是将创建对象的过程交给工厂进行处理,通过工厂提供给我们需要创建的对象,而不用自己直接去进行对象的创建。工厂模式包括三种:简单工厂模式(其不符合设计模式的开闭原则,不属于23中设计模式之一)、工厂方法模式以及抽象工厂模式,下面对每个模式进行逐一介绍。

1、简单工厂模式(固定的工厂,固定的产品)

(1)静态工厂模式

        //电脑产品
        public class Computer{
        }
        //Asus电脑
        public class AsusComputer extends Computer{
        }
        //Dell电脑
        public class DellComputer extends Computer{
        }
        //电脑创建工厂
        public class ComputerFactory{
          public enum ComputerType{
                  ASUS,DELL;
          }
          //创建方法:当有新的电脑产品的增加时,需要在create方法中进行添加
          static Computer create(ComputerType computerType){
                  if(computerType.equals(ComputerType.ASUS)){
                          return new AsusComputer();
                  }
                  if(computerType.equals(ComputerType.DELL)){
                          return new DellComputer();
                  }else{
                          return null;
                  }
          }
        }

(2)利用反射机制实现的简单工厂

由于静态工厂模式会破坏开闭原则,因此,从静态工厂模式扩展出一种利用反射可以动态添加创建对象的简单工厂模式。通过新对象注册到容器中,并利用反射机制来实现对象的创建,从而不用修改代码,不会违背开闭原则。还是以电脑产品为例子,来看下其实现。

        //电脑产品
        public class Computer{
        }
        //Asus电脑
        public class AsusComputer extends Computer{
        }
        //Dell电脑
        public class DellComputer extends Computer{
        }
        //电脑创建工厂
        public class ComputerFactory{
           //注册容器
           private static Map<String,Class<? extends Computer>> computerContext = new ConcurrentHashMap<>();
           //注册电脑类型
           static void registerComputer(String computerType,Class<? extends Computer> cls){
                           computerContext.put(computerType,cls);
           }
          //创建方法:当有新的电脑产品的增加时,可以通过registerComputer方法将产品注册进来,不用改变代码
            static Computer create(String computerType){
                Computer needComputer = computerContext.get(computerType).newInstance();
                          if(needComputer != null){
                                  return needComputer;
                          }else{
                                  return null;
                          }
          }
        }

简单工厂模式,不够抽象,耦合度高,虽然可以利用反射机制来解决静态工厂模式的弊端,但是需要使用注册空间以及反射,在创建对象量大的场景下会降低效率,因此,便会有工厂方法模式来解决这些弊端。

2、工厂方法模式

抽象产品,抽象工厂,但是工厂只能穿件单一的产品

工厂方法模式,降低了耦合度,提高扩展性,符合开闭原则,但是其存在一个问题,即仅仅只能在同一个维度上进行对象的创建,这样也就引出了抽象工厂模式。

类图

        //电脑产品
        public class Computer{
        }
        //Asus电脑
        public class AsusComputer extends Computer{
        }
        //Dell电脑
        public class DellComputer extends Computer{
        }
        //电脑创建工厂接口
        public interface ComputerFactory{
          public Computer createComputer();
        }
        //AsusComputer创建工厂
        public class AsusComputerFactory implements ComputerFactory {
                  public Computer createComputer(){
                          return new AsusComputer();
                  }
        }
        
        //DellComputer创建工厂
        public class DellComputerFactory implements ComputerFactory {
                  public Computer createComputer(){
                          return new DellComputer();
                  }
        }
        //...需要创建更多对象时,去实现ComputerFactory接口即可,这样即降低了耦合度,又不违背开闭原则

3、抽象工厂模式

抽象产品,抽象工厂,工厂包含多个抽象产品,是的工厂可以创建多个产品

类图

        //电脑产品
        public class Computer{
        }
        //Asus电脑
        public class AsusComputer extends Computer{
        }
        //Dell电脑
        public class DellComputer extends Computer{
        }
        //服务器产品
        public class Server{
        }
        //Asus服务器
        public class AsusServer extends Server{
        }
        //Dell服务器
        public class DellServer extends Server{
        }
        
        //创建工厂接口
        public interface Factory{
          public Computer createComputer();
          public Server createServer();
        }
        //Asus创建工厂
        public class AsusFactory implements Factory{
                  public Computer createComputer(){
                          return new AsusComputer();
                  }
                   public Server createServer(){
                           return new AsusServer(); 
                 }
        }
        //Dell创建工厂
        public class DellFactory implements Factory {
                  public Computer createComputer(){
                          return new DellComputer();
                  }
                   public Server createServer(){
                           return new DellServer(); 
                 }
        }

原理解析:

1.BeanFactory

上面我们说单例模式的时候离不开IOC容器,说到IOC的容器就一定离不开BeanFactory,ApplicationContentext ,IOC容器中BeanFactory是Spring容器的顶层接口

BeanFactory 相当于抽象工厂方法模式中的工厂接口,其中getBean是我们很常见的功能(抽象产品),是spring依赖查询的基本保障。

有很多工厂类(抽象工厂类)去继承Beanfactory,重写类似getBean()的接口实现产品获取的具体逻辑

从BeanFactory接口的结构我们可以看出,通过getBean重载方法,为我们创建不同的Bean对象,当然其也有很多工厂实现,例如我们用的最多的DefaultListableBeanFactory,还有SimpleJndiBeanFactory、StaticListableBeanFactory等等。Spring工厂模式的应用还加入了反射及配置。通过对各种配置,例如xml,注解等等解析成BeanDefinition,然后根据不同工厂要求通过反射创建不同的Bean对象,这样开发过程中,我们可以将需要创建的对象通过配置等方式交给Bean工厂去完成,使用时直接获取便可。Bean工厂的设计,大大降低耦合度,并增强可扩展性,提高了代码的可维护性,符合设计模式的原则。

2.FactoryBean

说到BeanFactoy,很多场景我就不多说了,会经常问我们FactoryBean和BeanFactory的区别,

BeanFactory是IOC容器,FactoryBean 是springBean的顶层接口。

Spring源码中FactoryBean的设计页数使用了抽象工厂模式,spring通过FactoryBean去自定制个性化的Bean,从而Spring框架提高扩展能力。

FactoryBean工厂通过getObject()方法来创建并返回对象,我们可以通过实现FactoryBean来定制化自己需要的Bean对象,因此,FactoryBean是一种典型的工厂方法模式的实现。

我们在引入其他框架整合Spring时,便会有很多桥接整合包,例如mybatis-spring等,其中就会有FactoryBean的实现,例如SqlSessionFactoryBean、MapperFactoryBean等,将需要整合的定制化Bean通过工厂方法的模式,加入进Spring容器中,从而大大降低了耦合程度,也为开发提供了扩展能力。

观察者模式

在我们上面查看IOC容器初始化过程中就能直截了当看到event创建和注册,这里就使用了观察者模式

观察者模式介绍

观察者模式属于三种设计模式分类中的** 行为型模式**,当对象之间存在一对多,且“一”变化时,需要“多”进行同步变化/通知的行为时,则可以使用观察者模式来实现。观察者模式的设计可以通过在被观察者对象中存放观察者对象的集合,当被观察的变化信息需要通知到观察者时,则去遍历执行观察者对象集合中的每一个观察者,实现一对多的通知。通过集合来完成一对多关系的依赖,从而实现行为通知,可以降低耦合程度。下面我们来看下观察者模式的简单实现。

类图

//观察的主题(被观察者)
public class Subject {
   private List<Observer> observers 
      = new ArrayList<Observer>();
   private int state;
   public int getState() {
      return state;
   }
   public void setState(int state) {
      this.state = state;
      notifyAllObservers();
   }
   public void attach(Observer observer){
      observers.add(observer);      
   }
   public void notifyAllObservers(){
      for (Observer observer : observers) {
         observer.update();
      }
   }  
}

//观察者抽象,与被观察者抽象耦合
public abstract class Observer {
   protected Subject subject;
   public abstract void update();
}
//观察者实现1
public class AddObserver{
        public AddObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
        }
    @Override
    public void update() {
      System.out.println( "加法观察者:" + subject.getState()); 
    }
}
//观察者实现2
public class MinusObserver{
        public AddObserver(Subject subject){
     this.subject = subject;
     this.subject.attach(this);
        }
   @Override
   public void update() {
      System.out.println( "减法观察者:" + subject.getState()); 
   }
}  

从上面的案例我们可以看到,有几个关键信息

1.主题(subject) 2.抽象观察者(Observer)3.具体观察者(AddObserver,MinusObserver)

4.主题调用具体观察者的入口方法(notifyAllObservers)5具体观察者被通知时实现具体逻辑的方法(update),掌握了这几个关键信息之后,,接着上面的IOC入口图我们去看下spring的源码。

原理解析:

初始化ApplicationEventMulticaster(主题)

listener(观察者) multicastEvent(主题通知观察者的入口方法)

event(观察者时间具体逻辑的抽象对象)

判断时候有多线程,有多线程就异步执行,没有则同步

最终找到对应的listener(观察者),调用观察者的onApplicationEvent(具体实现方法),传入的参数就是event

通过IOC容器去发布事件

最终会找到ApplicationEventMulticaster去通知对应的listener 去实现业务逻辑

并且通过重载onApplicationEvent去定制化业务逻辑

建造者模式

建造者模式介绍

建造者模式属于三种设计模式分类中的创建者模式,与工厂模式、单例模式相同,其都是用于进行对象创建的设计模式。建造者模式实际上是将复杂的对象构建过程进行抽取封装,并暴露出来对象的创建方法,使得不同的对象展示,可以通过同样的建造者来实现不同的表象。下面介绍一下建造者模式的实现方式。

类图

//创建的对象
public class Computer{
        private String mouse;
        private String keyBoard;
        private String display;
        //getter...
        //setter...
        //构造器
}
public class DellComputer extends Computer{
}
//builder的抽象设计
public abstract class ComputerBuilder {
    abstract void buildMouse(String mouse);
    abstract void buildKeyBoard(String keyBoard);
    abstract void buildDisplay(String display);
    abstract Computer build();
}
//DellComputerBuilder的设计
public class DellComputerBuilder extends ComputerBuilder {

    private DellComputer dellComputer = new DellComputer(); 
    
        @Override
    void buildMouse(String mouse) {
        dellComputer.setMouse(mouse);
    }
    
    @Override
    void buildBoard(String keyBoard) {
        dellComputer.setKeyBoard(keyBoard);
    }
 
    @Override
    void buildDisplay(String display) {
        dellComputer.setDisplay(display);
    }
    
    @Override
    Computer build() {
        return dellComputer;
    }
}

采用建造者模式,将一个复杂的对象一步一步通过建造者去构建而成,将对象的创建过程抽取并利用面向对象封装性可以实现有效构建和表示的分离,提高解耦程度,方便扩展。

原理解析:

上面我们说过,Spring IOC 容器初始化的过程中会先去解析生产BeanDefinition,我们还可以通过BeanDefinitionBuilder手动的方式给IOC容器注册Bean,从类名中我们可以看到xxxxBuilder,这大概率就是建造者模式的使用。

总结

以上我们主要是围绕着Spring的IOC源码查看了一些设计模式的使用,Spring 中还有很多其他设计模式的使用,比如AOP源码中的代理模式,说到代理模式就能展开简单代理,动态代理,以及Spring 源码中还用到的Cjlib代理,当然AOP源码往下挖我们还能看到xxxxAdapeter适配器模式,用于advice转换的适配。在我们使用数据库组件的时候还能看到各种xxxTemplate,这一下子就让我想到了模板方法模式等等。源码中还有更多的设计模式等待我们去发现挖掘,多看看源码,对提升自己代码的架构设计能力应该很有帮助,不说一下子要能把系统设计的多好,厚积薄发,循序渐进,慢慢提升,总有一天也能让你设计的系统变得高内聚,低耦合,高扩展。