Spring框架中的设计模式(四)

这篇文章是该系列关于spring框架设计模式文章的第四篇,会继续介绍3个框架使用的设计模式。

最开始,我们会介绍两个结构设计模式:适配器模式adapter和装饰模式decorator。第三个,也就是本文最后一部分,我们会介绍一个对象创建设计模式:单例singleton

Spring设计模式 - 适配器adapter

适配器模式应用在当我们需要将接口适配到特定的环境而且不需要修改接口自身行为的场合。这意味着在调用一个对象之前我们采用了一个非修改机制修改了所使用的对象。要在现实世界中描述这一点,想像一下你要用钻打一个洞的场景。要打一个小洞,你要使用小钻头,而大洞需要大钻头。用下面的代码演示这一点:

public class AdapterTest {

  public static void main(String[] args) {
    HoleMaker maker = new HoleMakerImpl();
    maker.makeHole(1);
    maker.makeHole(2);
    maker.makeHole(30);
    maker.makeHole(40);
  }
}

interface HoleMaker {
  public void makeHole(int diameter);
}

interface DrillBit {
  public void makeSmallHole();
  public void makeBigHole();
}

// Two adaptee objects
class BigDrillBit implements DrillBit {

  @Override
  public void makeSmallHole() {
    // do nothing
  }

  @Override
  public void makeBigHole() {
    System.out.println("Big hole is made byt WallBigHoleMaker");
  }
}

class SmallDrillBit implements DrillBit {

  @Override
  public void makeSmallHole() {
    System.out.println("Small hole is made byt WallSmallHoleMaker");
  }

  @Override
  public void makeBigHole() {
    // do nothing
  }
}

// Adapter class
class Drill implements HoleMaker {

  private DrillBit drillBit;

  public Drill(int diameter) {
    drillBit = getMakerByDiameter(diameter);
  }

  @Override
  public void makeHole(int diameter) {
    if (isSmallDiameter(diameter)) {
            drillBit.makeSmallHole();
    } else {
            drillBit.makeBigHole();
    }
  }

  private DrillBit getMakerByDiameter(int diameter) {
    if (isSmallDiameter(diameter)) {
            return new SmallDrillBit();
    }
    return new BigDrillBit();
  }

  private boolean isSmallDiameter(int diameter) {
    return diameter < 10;
  }
}

// Client class
class HoleMakerImpl implements HoleMaker {

  @Override
  public void makeHole(int diameter) {
    HoleMaker maker = new Drill(diameter);
    maker.makeHole(diameter);
  }
}

这段代码会输出:

Small hole is made byt SmallDrillBit
Small hole is made byt SmallDrillBit
Big hole is made byt BigDrillBit
Big hole is made byt BigDrillBit

你可以看到,打洞使用了适配对象DrillBit。如果要打的洞的直径小于10,使用SmallDrillBit,否则我们使用BigDrillBit

Spring使用了适配器模式来处理不同的servlet容器中加载时织入( load-time-weaving)。加载时织入被用在AOP中在类加载时用来注入AspectJ方面到字节码。其他的方面注入方法有编译时注入或者编译后类上的静态注入。

描述这种情况的一个比较好的例子是JBoss,在包org.springframework.instrument.classloading.jboss里面。我们在这里抽取了JBossLoadTimeWeaver类用于负责JBoss容器的织入管理。然而,JBoss6(使用JBossMCAdapter 实例)和JBoss7/8(使用JBossModulesAdapter 实例)的类加载器是不同的。根据JBoss版本的不同,我们在JBossLoadTimeWeaver构造函数中初始化相应的适配器(就像我们上面的例子中选钻头的例子一样):

public JBossLoadTimeWeaver(ClassLoader classLoader) {
  private final JBossClassLoaderAdapter adapter;

  Assert.notNull(classLoader, "ClassLoader must not be null");
  if (classLoader.getClass().getName().startsWith("org.jboss.modules")) {
    // JBoss AS 7 or WildFly 8
    this.adapter = new JBossModulesAdapter(classLoader);
  }
  else {
    // JBoss AS 6
    this.adapter = new JBossMCAdapter(classLoader);
  }
}

更进一步,这个适配器实例根据所运行的servlet容器的版本被用来做织入操作:

@Override
public void addTransformer(ClassFileTransformer transformer) {
  this.adapter.addTransformer(transformer);
}

@Override
public ClassLoader getInstrumentableClassLoader() {
  return this.adapter.getInstrumentableClassLoader();
}

Spring设计模式 - 装饰器decorator

这里我们要看的第二种设计模式跟适配器模式看起来很像。它是装饰器模式。该模式的主要角色是向指定对象补充增加功能。现实世界中,该模式可以用咖啡来描述。通常黑咖啡口感很浓烈,你可以加(装饰)点糖和牛奶来淡化咖啡口感。这里咖啡就是被装饰的对象,而糖和牛奶就是装饰器。下面你可以看到这个咖啡装饰的例子:

public class DecoratorSample {

  @Test
  public void test() {
    Coffee sugarMilkCoffee=new MilkDecorator(new SugarDecorator(new BlackCoffee()));
    assertEquals(sugarMilkCoffee.getPrice(), 6d, 0d);
  }
}

// decorated
abstract class Coffee{
  protected int candied=0;
  protected double price=2d;
  public abstract int makeMoreCandied();
  public double getPrice(){
    return this.price;
  }
  public void setPrice(double price){
    this.price+=price;
  }
}
class BlackCoffee extends Coffee{
  @Override
  public int makeMoreCandied(){
    return 0;
  }
  @Override
  public double getPrice(){
    return this.price;
  }
}

// abstract decorator
abstract class CoffeeDecorator extends Coffee{
  protected Coffee coffee;
  public CoffeeDecorator(Coffee coffee){
    this.coffee=coffee;
  }
  @Override
  public double getPrice(){
    return this.coffee.getPrice();
  }
  @Override
  public int makeMoreCandied(){
    return this.coffee.makeMoreCandied();
  }
}

// concrete decorators
class MilkDecorator extends CoffeeDecorator{
  public MilkDecorator(Coffee coffee){
    super(coffee);
  }
  @Override
  public double getPrice(){
    return super.getPrice()+1d;
  }
  @Override
  public int makeMoreCandied(){
    return super.makeMoreCandied()+1;
  }
}
class SugarDecorator extends CoffeeDecorator{
  public SugarDecorator(Coffee coffee){
    super(coffee);
  }
  @Override
  public double getPrice(){
    return super.getPrice()+3d;
  }
  @Override
  public int makeMoreCandied(){
    return super.makeMoreCandied()+1;
  }
}

这个装饰器例子的基础是调用父类不同的用于调整最终属性的方法(这里是价格和糖水平)。Spring中,我们把装饰器设计模式抽取到了一个处理Spring事务管理的缓存同步的类。这个类是org.springframework.cache.transaction.TransactionAwareCacheDecorator

这个类的哪些特点证明了它是org.springframework.cache.Cache对象的装饰器呢?首先,跟在我们的咖啡例子里面的一样,TransactionAwareCacheDecorator的构造方法在参数中接收被装饰对象(Cache):

private final Cache targetCache;
/**
 * Create a new TransactionAwareCache for the given target Cache.
 * @param targetCache the target Cache to decorate
 */
public TransactionAwareCacheDecorator(Cache targetCache) {
  Assert.notNull(targetCache, "Target Cache must not be null");
  this.targetCache = targetCache;
}

接下来,一个新的行为被添加到了被装饰的Cache上。正如我们在TransactionAwareCacheDecorator的注释上所看到的,它的主要目的是提供缓存和Spring事物之间的同步级别。要达到此目的,要感谢org.springframework.transaction.support.TransactionSynchronizationManager的两个Cache方法:put和evict :

@Override
public void put(final Object key, final Object value) {
  if (TransactionSynchronizationManager.isSynchronizationActive()) {
    TransactionSynchronizationManager.registerSynchronization(
      new TransactionSynchronizationAdapter() {
        @Override
        public void afterCommit() {
          targetCache.put(key, value);
        }
    });
  }
  else {
    this.targetCache.put(key, value);
  }
}

@Override
public void evict(final Object key) {
  if (TransactionSynchronizationManager.isSynchronizationActive()) {
          TransactionSynchronizationManager.registerSynchronization(
            new TransactionSynchronizationAdapter() {
              @Override
              public void afterCommit() {
                targetCache.evict(key);
              }
          });
  }
  else {
    this.targetCache.evict(key);
  }
}

这个模式跟适配器模式很像是不是?然而,它们是不同的。正如我们所看到的,适配器适配对象到运行时环境,比如,假如我们在JBoss 6中运行,我们会使用和JBoss 7不同的类加载器。而装饰器每次都和同一个主对象(Cache)一起工作而且仅向它增加新的行为(在我们的例子中,就像是同步和Spring事务的关系)。

Spring设计模式 - 单例singleton

现在,轮到非常流行的设计模式单例了。正如我们在Spring框架有关单例和原型bean的文章中所解释的,单例被看作是几种bean作用域的一种。这个作用域在应用上下文中对给定的bean仅创建一个实例。跟单例设计模式不同,Spring在应用上下文中限制了实例的数量。而Java应用中的单例设计模式是在整个类加载器的整个管理空间内限制这些实例的数量。这意味着你可以使用同一个类加载器管理两个应用上下文,每个上下文中有某个bean的一个实例,加起来也就是在这个类加载器空间中有两个这样的单例bean实例。

在深入Spring单例之前,我们来看一个Java单例的例子:

public class SingletonTest {

  @Test
  public void test() {
    President president1 = (President) SingletonsHolder.PRESIDENT.getHoldedObject();
    President president2 = (President) SingletonsHolder.PRESIDENT.getHoldedObject();
    assertTrue("Both references of President should point to the same object", 
        president1 == president2);
    System.out.println("president1 = "+president1+" and president2 = "+president2);
    // 例子输出 : 
    // president1 = com.waitingforcode.test.President@17414c8 
    // and president2 = com.waitingforcode.test.President@17414c8

  }

}

enum SingletonsHolder {

  PRESIDENT(new President());

  private Object holdedObject;

  private SingletonsHolder(Object o) {
          this.holdedObject = o;
  }

  public Object getHoldedObject() {
          return this.holdedObject;
  }

}

class President {
}

这个测试用例证明了SingletonsHolder持有的President实例一共只有一个。在Spring中,我们可以在bean工厂中发现单例bean的概念(比如在org.springframework.beans.factory.config.AbstractFactoryBean中):

/**
 * Expose the singleton instance or create a new prototype instance.
 * @see #createInstance()
 * @see #getEarlySingletonInterfaces()
 */
@Override
public final T getObject() throws Exception {
  if (isSingleton()) {
    return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());
  }
  else {
    return createInstance();
  }
}

可以看到如果被请求的bean是单例bean,它仅会被初始化一次而随后每次对该bean的访问都返回同样的对象。在所给的例子中,我们可以看到这一点,跟我们之前看到的President的例子很类似。测试bean定义如下:

<bean id="shoppingCart" class="com.waitingforcode.data.ShoppingCart" />

测试用例如下:

public class SingletonSpringTest {

  @Test
  public void test() {
    // retreive two different contexts
    ApplicationContext firstContext = 
        new FileSystemXmlApplicationContext("applicationContext-test.xml");
    ApplicationContext secondContext 
        = new FileSystemXmlApplicationContext("applicationContext-test.xml");

    // prove that both contexts are loaded by the same class loader
    assertTrue("Class loaders for both contexts should be the same", 
      firstContext.getClassLoader() == secondContext.getClassLoader());
    // compare the objects from different contexts
    ShoppingCart firstShoppingCart = (ShoppingCart) firstContext.getBean("shoppingCart");
    ShoppingCart secondShoppingCart = (ShoppingCart) secondContext.getBean("shoppingCart");
    assertFalse("ShoppingCart instances got from different application 
        context shouldn't be the same", 
      firstShoppingCart == secondShoppingCart);

    // compare the objects from the same context
    ShoppingCart firstShoppingCartBis = (ShoppingCart) firstContext.getBean("shoppingCart");
    assertTrue("ShoppingCart instances got from the same application context should 
        be the same", 
      firstShoppingCart == firstShoppingCartBis);
  }
}

这个测试用例展示了Spring单例和纯粹的Java单例设计模式的主要不同。尽管使用了同一个类加载器加载了两个应用上下文,ShoppingCart的实例却不同。但当我们从同一个应用上下文中两次获取同一个bean类的实例作比较,我们能认识到它们是同一个。

总结

这篇文章中描述的设计模式被Spring用于处理bean的创建。通过单例模式,Spring可以控制每个应用上下文中某个特定类型的bean仅有一个实例。通过适配器模式,Spring决定哪一层会被用于处理JBoss servlet容器的加载时织入。第三种设计模式,装饰器,被用来向缓存Cache对象增加同步功能。

英文原文

该系列文章目录

Spring框架中的设计模式(五)
Spring框架中的设计模式(四)
Spring框架中的设计模式(三)
Spring框架中的设计模式(二)
Spring框架中的设计模式(一)

猜你喜欢

转载自blog.csdn.net/andy_zhang2007/article/details/79781914
今日推荐