Spring中的设计模式

简单工厂

在Spring中经常利用BeanFactory的getBean方法去获取Bean就是一个简单工厂的设计模式的实现,通过Bean的ID去获取这个对象的实例。Bean的ID一般配置在XML文件中。

工厂方法

在工厂方法模式中, Spring不会直接利用反射机制创建bean对象, 而是会利用反射机制先找到Factory类,然后利用Factory再去生成bean对象。

而Factory Mothod方式也分两种, 分别是静态工厂方法 和 实例工厂方法。

抽象工厂

在工厂方法中有一个产品树的概念,简单来说就是某一种工厂类。

增加了一个产品族的概念,同一种类型可以用同一个工厂来生产。

弊端:当增加一个产品树,所有的产品族都需要修改。

https://www.jianshu.com/p/6d447cea14c7

在Spring中我们要结合简单工厂配合反射来改进抽象工厂

单例模式

保证一个类仅有一个实例,并提供一个访问它的全局访问点。
spring中的单例模式完成了后半句话,即提供了全局的访问点BeanFactory。但没有从构造器级别去控制单例,这是因为spring管理的是是任意的java对象。

1.普通懒汉式:

public Singleton{
      private Singleton(){}
      private static Singleton singleton=null;
      public static Singleton getInstance(){
                if(singleton==null)
                   singleton=new Singleton();
                return singleton;
      }
}

线程不安全:

2.解决方法:双重检查锁式懒汉式

public Singleton{
      private Singleton(){}
      private volatile static Singleton singleton=null;
      public static Singleton getInstance(){
                if(singleton==null){
                   synchronized(Singleton.class){
                       if(singleton==null)
                          singleton=new Singleton();
                   }
                   
                return singleton;
      }
}

3.饿汉式:

在类初始化时自行实例化,因此是线程安全的

4.静态内部类式:

这种方式是SingletonDemo3 类被装载了,instance不一定被初始化。因为SingletonClassGetInstance 类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonClassGetInstance 类,从而实例化instance。

线程安全,调用效率高,并且可以延时加载。推荐使用 。

public class SingletonDemo3 {
    //静态内部类
    private static class SingletonClassGetInstance {
        public static final SingletonDemo3 instance = new SingletonDemo3();
    }
    // 私有构造器
    private SingletonDemo3() {
    }
    public static  SingletonDemo3 getInstance() {
        return SingletonClassGetInstance.instance;
    }
}

5.枚举模式

6.单例注册表:

import java.util.HashMap;  
Public class RegSingleton{  
    Static private HashMap registry=new HashMap();  
       //静态块,在类被加载时自动执行  
       Static{  
         RegSingleton rs=new RegSingleton();  
         registry.put(rs.getClass().getName(),rs);  
       }  
    //受保护的默认构造函数,如果为继承关系,则可以调用,克服了单例类不能为继承的缺点  
    Protected RegSingleton(){}  
    //静态工厂方法,返回此类的唯一实例  
    public static RegSingleton getInstance(String name){  
        if(name==null){  
          name=” RegSingleton”;  
        }
        if(registry.get(name)==null){  
          try{  
              registry.put(name,Class.forName(name).newInstance());  
          }Catch(Exception ex)
{
ex.printStackTrace();
} } return (RegSingleton)registry.
get(name); } }

Spring中对单例额支持就是采用单例注册表的方式:

好像是采用的ConcurrentHashMap是出于线程安全的考虑。

public abstract class AbstractBeanFactory implements ConfigurableBeanFactory{  
       /** 
        * 充当了Bean实例的缓存,实现方式和单例注册表相同 
        */  
       private final Map singletonCache=new HashMap();  
       public Object getBean(String name)throws BeansException{  
           return getBean(name,null,null);  
       }  
       public Object getBean(String name,Class requiredType,Object[] args)throws BeansException{  
          //对传入的Bean name稍做处理,防止传入的Bean name名有非法字符(或则做转码)  
          String beanName=transformedBeanName(name);  
          Object bean=null;  
          //手工检测单例注册表  
          Object sharedInstance=null;  
          //使用了代码锁定同步块,原理和同步方法相似,但是这种写法效率更高  
          synchronized(this.singletonCache){  
             sharedInstance=this.singletonCache.get(beanName);  
           }  
          if(sharedInstance!=null){  
             //返回合适的缓存Bean实例  
             bean=getObjectForSharedInstance(name,sharedInstance);  
          }else{  
            //取得Bean的定义  
            RootBeanDefinition mergedBeanDefinition=getMergedBeanDefinition(beanName,false);  
            //根据Bean定义判断,此判断依据通常来自于组件配置文件的单例属性开关  
            //<bean id="date" class="java.util.Date" scope="singleton"/>  
            //如果是单例,做如下处理  
            if(mergedBeanDefinition.isSingleton()){  
               synchronized(this.singletonCache){  
                //再次检测单例注册表  
                 sharedInstance=this.singletonCache.get(beanName);  
                 if(sharedInstance==null){  
                   try {  
                      //真正创建Bean实例  
                      sharedInstance=createBean(beanName,mergedBeanDefinition,args);  
                      //向单例注册表注册Bean实例  
                      addSingleton(beanName,sharedInstance);  
                   }catch (Exception ex) {  
                      ...  
                   }finally{  
                      ...  
                  }  
                 }  
               }  
              bean=getObjectForSharedInstance(name,sharedInstance);  
            }  
           //如果是非单例,即prototpye,每次都要新创建一个Bean实例  
           //<bean id="date" class="java.util.Date" scope="prototype"/>  
           else{  
              bean=createBean(beanName,mergedBeanDefinition,args);  
           }  
    }  
    ...  
       return bean;  
    }  
    } 

代理模式

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

在方法的前后加一些操作,JDK动态代理的invoke方法,cglib动态代理的intercept方法就是做这个的
spring的Proxy模式在aop中有体现,比如JdkDynamicAopProxy和Cglib2AopProxy。

观察者模式

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新(通常是组合的方式)。
spring中Observer模式常用的地方是listener的实现。如ApplicationListener。ServletContextListener

servlet和Filter初始化前和销毁后,都会给实现了servletContextListener接口的监听器发出相应的通知。

底层通过回调来实现,在满足某些条件的时候执行回调接口。

https://www.cnblogs.com/luohanguo/p/7825656.html

模板方法

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

在AQS的acquire和release中有用到

同时在Spring的JDBCTemplate中也有用到。(具体流程还需要了解)

适配器(Adapter)

三种模式:类适配器模式(通过继承调用),对象适配器(组合的方式),接口适配器(通过一个抽象类实现部分方法)

应用场景:

类适配器与对象适配器的使用场景一致,仅仅是实现手段稍有区别,二者主要用于如下场景:

(1)想要使用一个已经存在的类,但是它却不符合现有的接口规范,导致无法直接去访问,这时创建一个适配器就能间接去访问这个类中的方法。

(2)我们有一个类,想将其设计为可重用的类(可被多处访问),我们可以创建适配器来将这个类来适配其他没有提供合适接口的类。

 

接口适配器使用场景:

  (1)想要使用接口中的某个或某些方法,但是接口中有太多方法,我们要使用时必须实现接口并实现其中的所有方法,可以使用抽象类来实现接口,并不对方法进行实现(仅置空),然后我们再继承这个抽象类来通过重写想用的方法的方式来实现。这个抽象类就是适配器。

 

https://blog.csdn.net/elegant_shadow/article/details/5006175 

https://www.cnblogs.com/V1haoge/p/6479118.html

https://blog.csdn.net/zhousenshan/article/details/83119549

包装器(Decorator),这个和代理模式很像

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。

装饰器模式关注于在一个对象上动态地添加方法,而代理模式关注于控制对对象的访问。换句话说,用代理模式,代理类可以对它的客户隐藏一个对象的具体信息。

装饰者和被装饰者都是实现同一个接口,这个JDK动态代理是一样的

因此当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例;当使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰器的构造器。(在代码中的具体实现是这样)

包装器模式原对象还是需要我们手动实例化的,然后把它作为参数传到装饰类里面,然后装饰类会在某个方法之前加一些操作,而代理模式是直接创建一个代理类,它更强调对整个对象的控制。

帮助理解:

装饰器模式和代理模式的使用场景不一样,比如IO流使用的是装饰者模式,可以层层增加功能。而代理模式则一般是用于增加特殊的功能,有些动态代理不支持多层嵌套。

代理和装饰其实从另一个角度更容易去理解两个模式的区别:代理更多的是强调对对象的访问控制,比如说,访问A对象的查询功能时,访问B对象的更新功能时,访问C对象的删除功能时,都需要判断对象是否登陆,那么我需要将判断用户是否登陆的功能抽提出来,并对A对象、B对象和C对象进行代理,使访问它们时都需要去判断用户是否登陆,简单地说就是将某个控制访问权限应用到多个对象上;而装饰器更多的强调给对象加强功能,比如说要给只会唱歌的A对象添加跳舞功能,添加说唱功能等,简单地说就是将多个功能附加在一个对象上。

 

https://blog.csdn.net/u014401141/article/details/79017464

建造者模式

使类可以链式方法实例化对象

https://blog.csdn.net/evanxuhe/article/details/79477163

策略(Strategy)模式

定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。 在java中的实现就是同一个接口,同一个方法有不同的实现方式,我们将这些不同的对象作为内部属性加入到一个context中,在context的构造方法中通过传入不同的对象,在调用context的同一个方法的时候就实现了不同的方法。

我们需要根据不同的条件传入不同的对象。

Spring中在实例化对象的时候有用到这个模式。

 

Spring在org.springframework.web.servlet.mvc.multiaction.MethodNameResolver类(过时,但不影响拿来研究)中使用策略设计模式。它是MultiActionController(同样过时)的参数化实现。在开始解释策略之前,我们需要了解MultiActionController的实用性。这个类允许同一个类处理几种类型的请求。作为Spring中的每个控制器,MultiActionController执行方法来响应提供的请求。策略用于检测应使用哪种方法。解析过程在MethodNameResolver实现中实现,例如在同一个包中的ParameterMethodNameResolver中。方法可以通过多个条件解决:属性映射,HTTP请求参数或URL路径。

策略模式例子参考:http://blog.didispace.com/spring-design-partern-2/

这个策略模式和状态模式很像很像 

 简单工厂加策略模式的结合:

https://blog.csdn.net/qq_33157666/article/details/80399234

复合模式:复合模式确实是多个模式的组合,但满足了这一条并不一定是复合模式,注意它的定义:将多个模式结合起来形成一个“框架”,以解决一般性问题

 

对象池模式:

对象池模式是单例模式的一个变种,它提供了获取一系列相同对象实例的入口。

对象池模式管理一个可代替对象的集合。组件从池中借出对象,用它来完成一些任务并当任务完成时归还该对象。被归还的对象接着满足请求,不管是同一个组件还是其他组件的请求。对象池模式可以管理那些代表的现实资源或者通过重用来分摊昂贵初始化代价的对象。

Spring中使用的另一个模型是对象池设计模式。其主要目的在于在一个池中保存特定数量的对象,并根据需要重新使用。通过它,我们可以改善我们想要使用巨型对象的响应时间。巨型意味着这些对象的构造需要很多时间(例如:持有数据库连接的对象),最好重用已经存在的和未获取的对象,而不是创建新对象。

Spring还使用线程池来管理其调度部分。一些示例位于org.springframework.scheduling.concurrent中。我们检索数据库(Spring JDBC)项目中的对象池的想法。数据库连接池不是由Spring直接实现的,而是适用于Spring工作方式的项目,如C3P0Jakarta Commons DBCP连接池。

参考

https://www.cnblogs.com/yuefan/p/3763898.html

https://www.jianshu.com/p/40d0a6ad21d2

posted @ 2019-03-11 21:11  LeeJuly  阅读(183)  评论(0编辑  收藏  举报