10k

设计模式之美-课程笔记49-开源实战4-从Spring框架学习经典设计思想/原则

Spring中的经典设计思想

Spring框架介绍

从Spring看框架作用

  1. 解耦业务和非业务开发;
  2. 隐藏复杂实现细节,降低开发难度
  3. 实现代码复用,节省开发时间

蕴含的设计思想

1. 约定优于配置

基于注解的配置方式我们在指定类上使用指定注解代替XML配置;

基于约定的配置,通过约定的代码结构或者命名来减少配置。比如代码中的Order类就对应数据库的order表。只有在偏离这个约定的时候我们才需要显示的配置映射关系。

2. 低侵入、松耦合

框架代码很少耦合业务代码。所谓耦合就可以理解为当替换一个框架的时候是否需要改动业务代码。

Spring 提供的 IOC 容器,在不需要 Bean 继承任何父类或者实现任何接口的情况下,仅仅通过配置,就能将它们纳入进 Spring 的管理中。如果我们换一个 IOC 容器,也只是重新配置一下就可以了,原有的 Bean 都不需要任何修改。

除此之外,Spring 提供的 AOP 功能,也体现了低侵入的特性

3. 模块化、轻量级

每个模块只负责相对独立的功能,模块之间只有上层对下层的依赖,同层之间或者下层对上层几乎没有依赖。

4. 再封装、再抽象

Spring对于市面上主流的中间件、系统的访问类库作了进一步的封装和抽象,提供额更高层次的访问接口。

Spring中支持扩展的设计模式

常用来实现扩展特性的设计模式有观察者模式、模板模式、指责连模式和策略模式。

观察者模式在Spring中的应用

// Event事件
public class DemoEvent extends ApplicationEvent {
  private String message;

  public DemoEvent(Object source, String message) {
    super(source);
  }

  public String getMessage() {
    return this.message;
  }
}

// Listener监听者
@Component
public class DemoListener implements ApplicationListener<DemoEvent> {
  @Override
  public void onApplicationEvent(DemoEvent demoEvent) {
    String message = demoEvent.getMessage();
    System.out.println(message);
  }
}

// Publisher发送者
@Component
public class DemoPublisher {
  @Autowired
  private ApplicationContext applicationContext;

  public void publishEvent(DemoEvent demoEvent) {
    this.applicationContext.publishEvent(demoEvent);
  }
}

观察者注册到ApplicationContext中。相当于总线(但是他不是专门为这个服务的,他提供应用启动、运行时的上下文信息)。

借助Spring,我们只需要定义事件,定义监听器、往Application Context中发送事件就可以了,剩下的Spring会完成。

模板模式在Spring中的应用

img

  1. 将要执行的函数(initializeBean)封装成对象,传递给模版(BeanFactory)来执行。
  2. Spring bean 的创建分为创建和初始化。
  3. 创建是通过反射动态生成对象。ioc
  4. 初始化有两种
    1. 一种是显示在配置文件中告知-》 框架利用反射-〉影响性能
    2. 类实现Initializebean接口: 这个接口包含一个固定的初始化函数定义(afterPropertiesSet() 函数)。Spring 在初始化 Bean 的时候,可以直接通过 bean.afterPropertiesSet() 的方式,调用 Bean 对象上的这个函数,而不需要使用反射来调用了。
  5. 尽管第二种方式效率好点,但是与业务代码耦合也不是很好。整体上说。
  6. 销毁过程也是有类似上述的两种方式。
  7. 在初始化步骤还分了三小步,初始化的前后操作定义在这个接口
public interface BeanPostProcessor {
  Object postProcessBeforeInitialization(Object var1, String var2) throws BeansException;

  Object postProcessAfterInitialization(Object var1, String var2) throws BeansException;
}

我们只需要定义一个实现了这个接口的类,配置在配置文件,然后Spring会在初始化的时候,applicationContext检测配置文件,逐一调用这些处理器。

Spring框架中的设计模式

适配器模式

定义Controller的三种方式

// 方法一:通过@Controller、@RequestMapping来定义
@Controller
public class DemoController {
    @RequestMapping("/employname")
    public ModelAndView getEmployeeName() {
        ModelAndView model = new ModelAndView("Greeting");        
        model.addObject("message", "Dinesh");       
        return model; 
    }  
}

// 方法二:实现Controller接口 + xml配置文件:配置DemoController与URL的对应关系
public class DemoController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        ModelAndView model = new ModelAndView("Greeting");
        model.addObject("message", "Dinesh Madhwal");
        return model;
    }
}

// 方法三:实现Servlet接口 + xml配置文件:配置DemoController类与URL的对应关系
public class DemoServlet extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doPost(req, resp);
  }
  
  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.getWriter().write("Hello World.");
  }
}

在函数启动的时候,Spring会加载这些类,解析出url对应的处理函数,封装成handler对象存储到handlerMapping中。当请求到来的时候DispatcherServlet会从Mapping中查找url对应的handler,调用执行相应的函数代码。

Dispatcher中实际是这么调用的(伪代码逻辑):

Handler handler = handlerMapping.get(URL);
if (handler instanceof Controller) {
  ((Controller)handler).handleRequest(...);
} else if (handler instanceof Servlet) {
  ((Servlet)handler).service(...); // 这个地方是通过模板模式调用Service中的doGet、doPost
} else if (hanlder 对应通过注解来定义的Controller) {
  反射调用方法...
}

如果要增加新的Controller定义方法就要在这加一个else,不符合开闭原则。 Spring的具体实现是是基于模板模式,利用一个统一的接口不同的实现放到一样的函数里。

public interface HandlerAdapter {
  boolean supports(Object var1);

  ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;

  long getLastModified(HttpServletRequest var1, Object var2);
}

// 对应实现Controller接口的Controller
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
  public SimpleControllerHandlerAdapter() {
  }

  public boolean supports(Object handler) {
    return handler instanceof Controller;
  }

  public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    return ((Controller)handler).handleRequest(request, response);
  }

  public long getLastModified(HttpServletRequest request, Object handler) {
    return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L;
  }
}

// 对应实现Servlet接口的Controller
public class SimpleServletHandlerAdapter implements HandlerAdapter {
  public SimpleServletHandlerAdapter() {
  }

  public boolean supports(Object handler) {
    return handler instanceof Servlet;
  }

  public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    ((Servlet)handler).service(request, response);
    return null;
  }

  public long getLastModified(HttpServletRequest request, Object handler) {
    return -1L;
  }
}

//AnnotationMethodHandlerAdapter对应通过注解实现的Controller,
//代码太多,略

这样在DispatcherServlet中就不需要区分对待Controller对象来处理。

// 之前的实现方式
Handler handler = handlerMapping.get(URL);
if (handler instanceof Controller) {
  ((Controller)handler).handleRequest(...);
} else if (handler instanceof Servlet) {
  ((Servlet)handler).service(...);
} else if (hanlder 对应通过注解来定义的Controller) {
  反射调用方法...
}

// 现在实现方式
HandlerAdapter handlerAdapter = handlerMapping.get(URL);
handlerAdapter.handle(...);

策略模式

  1. Spring AOP 是通过动态代理实现的。Spring支持两种动态代理: JDK提供的动态代理,另一种是Cglib提供的动态代理实现方式。
  2. 前者需要被代理的类有抽象的接口定义,后者不需要。针对不同的被代理类,Spring会在运行时动态的选择不同的代理方式。
  3. 策略模式分三步:定义、创建和使用。
public interface AopProxy {
  Object getProxy();
  Object getProxy(ClassLoader var1);
}

这是策略类接口,JdkDynamicAopProxy、CglibAopProxy 是两个实现了 AopProxy 接口的策略类。

策略的创建一般通过工厂方法实现:对应到 Spring 源码,AopProxyFactory 是一个工厂类接口,DefaultAopProxyFactory 是一个默认的工厂类,用来创建 AopProxy 对象:

public interface AopProxyFactory {
  AopProxy createAopProxy(AdvisedSupport var1) throws AopConfigException;
}

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
  public DefaultAopProxyFactory() {
  }

  public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
      return new JdkDynamicAopProxy(config);
    } else {
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
        throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
      } else {
        return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
      }
    }
  }

  //用来判断用哪个动态代理实现方式
  private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
    Class<?>[] ifcs = config.getProxiedInterfaces();
    return ifcs.length == 0 || ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]);
  }
}

策略模式的典型场景一般是通过环境变量、状态值、计算结果等动态的决定使用哪个策略。

组合模式

Spring Cache 提供了一套抽象的 Cache 接口。使用它我们能够统一不同缓存实现(Redis、Google Guava…)的不同的访问方式。Spring 中针对不同缓存实现的不同缓存访问类,都依赖这个接口,比如:EhCacheCache、GuavaCache、NoOpCache、RedisCache、JCacheCache、ConcurrentMapCache、CaffeineCache。

public interface Cache {
  String getName();
  Object getNativeCache();
  Cache.ValueWrapper get(Object var1);
  <T> T get(Object var1, Class<T> var2);
  <T> T get(Object var1, Callable<T> var2);
  void put(Object var1, Object var2);
  Cache.ValueWrapper putIfAbsent(Object var1, Object var2);
  void evict(Object var1);
  void clear();

  public static class ValueRetrievalException extends RuntimeException {
    private final Object key;

    public ValueRetrievalException(Object key, Callable<?> loader, Throwable ex) {
      super(String.format("Value for key '%s' could not be loaded using '%s'", key, loader), ex);
      this.key = key;
    }

    public Object getKey() {
      return this.key;
    }
  }

  public interface ValueWrapper {
    Object get();
  }
}

实际开发中可能存在不同的缓存,为了管理多个缓存Spring提供了缓存管理器。在缓存管理器的实现上用到了组合模式

public interface CacheManager {
  Cache getCache(String var1);
  Collection<String> getCacheNames();
}

组合模式应用在能表示成树形结构的数据上。对应到 Spring 源码,EhCacheManager、SimpleCacheManager、NoOpCacheManager、RedisCacheManager 等表示叶子节点,CompositeCacheManager 表示中间节点。

public class CompositeCacheManager implements CacheManager, InitializingBean {
  private final List<CacheManager> cacheManagers = new ArrayList();
  private boolean fallbackToNoOpCache = false;

  public CompositeCacheManager() {
  }

  public CompositeCacheManager(CacheManager... cacheManagers) {
    this.setCacheManagers(Arrays.asList(cacheManagers));
  }

  public void setCacheManagers(Collection<CacheManager> cacheManagers) {
    this.cacheManagers.addAll(cacheManagers);
  }

  public void setFallbackToNoOpCache(boolean fallbackToNoOpCache) {
    this.fallbackToNoOpCache = fallbackToNoOpCache;
  }

  public void afterPropertiesSet() {
    if (this.fallbackToNoOpCache) {
      this.cacheManagers.add(new NoOpCacheManager());
    }

  }

  public Cache getCache(String name) {
    Iterator var2 = this.cacheManagers.iterator();

    Cache cache;
    do {
      if (!var2.hasNext()) {
        return null;
      }

      CacheManager cacheManager = (CacheManager)var2.next();
      cache = cacheManager.getCache(name);
    } while(cache == null);

    return cache;
  }

  public Collection<String> getCacheNames() {
    Set<String> names = new LinkedHashSet();
    Iterator var2 = this.cacheManagers.iterator();

    while(var2.hasNext()) {
      CacheManager manager = (CacheManager)var2.next();
      names.addAll(manager.getCacheNames());
    }

    return Collections.unmodifiableSet(names);
  }
}

其中递归函数正是遍历复合节点的关键。

装饰器模式

缓存一般配合数据库。如果写缓存成功但是数据库回滚,那缓存就有脏数据。为了保持一致,将缓存的写操作和数据库的写操作放到一个事务。Spring借助装饰器模式实现这个功能:对数据库事务的写进行增强。

public class TransactionAwareCacheDecorator implements Cache {
  private final Cache targetCache;

  public TransactionAwareCacheDecorator(Cache targetCache) {
    Assert.notNull(targetCache, "Target Cache must not be null");
    this.targetCache = targetCache;
  }

  public Cache getTargetCache() {
    return this.targetCache;
  }

  public String getName() {
    return this.targetCache.getName();
  }

  public Object getNativeCache() {
    return this.targetCache.getNativeCache();
  }

  public ValueWrapper get(Object key) {
    return this.targetCache.get(key);
  }

  public <T> T get(Object key, Class<T> type) {
    return this.targetCache.get(key, type);
  }

  public <T> T get(Object key, Callable<T> valueLoader) {
    return this.targetCache.get(key, valueLoader);
  }

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

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

  }

  public void clear() {
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
      TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
        public void afterCommit() {
          TransactionAwareCacheDecorator.this.targetCache.clear();
        }
      });
    } else {
      this.targetCache.clear();
    }
  }
}

工厂模式

  1. 简单IOC容器的实现
  2. Spring除了支持setter和构造函数创建bean, 还可以用工厂方法
public class StudentFactory {
  private static Map<Long, Student> students = new HashMap<>();
  
  static{
    map.put(1, new Student(1,"wang"));
    map.put(2, new Student(2,"zheng"));
    map.put(3, new Student(3,"xzg"));
  }
 
  public static Student getStudent(long id){
    return students.get(id);
  }
}

// 通过工厂方法getStudent(2)来创建BeanId="zheng""的Bean
<bean id="zheng" class="com.xzg.cd.StudentFactory" factory-method="getStudent">
    <constructor-arg value="2"></constructor-arg>           
</bean>

其他模式

  1. Spring用来编写配置的表达式语言,他有一系列语法规则。这就是Spring中使用解释器模式的案例。
  2. 单例模式存在弊端,应对策略就可以通过Ioc控制在IOC容器中。
  3. 后缀带有Template的都是模板类
  4. 拦截器是职责链模式
  5. AOP是代理模式的经典应用。
Thoughts? Leave a comment