面试官:聊聊你读过的开源代码中用到的设计模式

时间:2022-07-23
本文章向大家介绍面试官:聊聊你读过的开源代码中用到的设计模式,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
  • spring中的bean单例创建
  • spring中的代理模式
  • slf4j中的适配器
  • tomcat中的facade设计模式
  • jdk中的享元模式
  • google guava中的观察者模式
  • spring中的观察者模式
  • mybatis中的职责链模式
  • spring mvc中的模板模式
  • tomcat中的模板模式
  • mybatis中的策略模式

面试官:聊聊你读过的开源代码中的设计模式

我:大家都知道,经典的设计模式中有创建型、结构型、行为型3大类,共23种设计模式。但是我们日常开发中经常使用到的非常少,不到10种,比如代理模式、装饰器模式、策略模式、模板模式、职责链模式...

面试官:那你先聊聊创建型的设计模式

我:

spring中的bean单例创建

我们说的单例模式通常是指在一个进程内维护一个单例对象。spring的bean默认是单例的,Spring中bean的注解主要是@Component,@Controller @Service @Repository @Configuration都是依赖于@Component注解的 下面我们以@Component注解的bean实例化介绍一下源码中的实现,大家可以在springboot工程启动过程中debug看一下 //ClassPathScanningCandidateComponentProvider类

把Component注解添加到includeFilters列表

protected void registerDefaultFilters() {
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    //省略其他代码
}
我们看一下ConfigurationClassParser类的doProcessConfigurationClass方法
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)throws IOException {
        //省略部分代码
        // Process any @ComponentScan annotations
        Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
                sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
        if (!componentScans.isEmpty() &&
                !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
            for (AnnotationAttributes componentScan : componentScans) {
                // The config class is annotated with @ComponentScan -> perform the scan immediately
                Set<BeanDefinitionHolder> scannedBeanDefinitions =
                        this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
                // Check the set of scanned definitions for any further config classes and parse recursively if needed
                for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                    BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                    if (bdCand == null) {
                        bdCand = holder.getBeanDefinition();
                    }
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                        parse(bdCand.getBeanClassName(), holder.getBeanName());
                    }
                }
            }
        }
        //省略部分代码
        return null;
    }

再看ComponentScanAnnotationParser类,扫描器添加包括和排除的过滤器

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
        //省略部分代码
        for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
            for (TypeFilter typeFilter : typeFiltersFor(filter)) {
                scanner.addIncludeFilter(typeFilter);
            }
        }
        for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
            for (TypeFilter typeFilter : typeFiltersFor(filter)) {
                scanner.addExcludeFilter(typeFilter);
            }
        }
        //省略部分代码
        scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
            @Override
            protected boolean matchClassName(String className) {
                return declaringClass.equals(className);
            }
        });
        return scanner.doScan(StringUtils.toStringArray(basePackages));
    }

下面我们看ClassPathBeanDefinitionScanner类的doScan方法,创建BeanDefinition并且去注册

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
        for (String basePackage : basePackages) {
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            for (BeanDefinition candidate : candidates) {
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                candidate.setScope(scopeMetadata.getScopeName());
                String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                if (candidate instanceof AbstractBeanDefinition) {
                    postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
                }
                if (candidate instanceof AnnotatedBeanDefinition) {
                    //candidate增加属性
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                }
                if (checkCandidate(beanName, candidate)) {
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    definitionHolder =
                            AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
                    //注册beanDefinition
                    registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }
        return beanDefinitions;
    }

下面我们看上面registerBeanDefinition的代码,深入后主要逻辑在DefaultListableBeanFactory

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
        //省略部分代码
        if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    removeManualSingletonName(beanName);
                }
            }
            else {
                // Still in startup registration phase
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                removeManualSingletonName(beanName);
            }
            this.frozenBeanDefinitionNames = null;
    }:

我:接下来我聊一聊建造者模式 面试官:等一下,谈谈你知道的结构型设计模式吧 我:

spring中的代理模式

代理模式的作用是对原始类进行增强,但是这个增强是跟业务无关的,这点跟装饰器模式有区别。spring中使用的代理模式有基于JDK的和基于cglib的。大名鼎鼎的事务管理,就是通过代理模实现的。代理模式的实现其实很简单,就是从代理类中发起目标类方法的调动, TransactionInterceptor类实现了MethodInterceptor方法,invoke方法如下:

public Object invoke(MethodInvocation invocation) throws Throwable {
    // Work out the target class: may be {@code null}.
    // The TransactionAttributeSource should be passed the target class
    // as well as the method, which may be from an interface.
    Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
    // Adapt to TransactionAspectSupport's invokeWithinTransaction...
    return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
  }

我们再来看看invokeWithinTransaction方法

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,  final InvocationCallback invocation) throws Throwable {
    // If the transaction attribute is null, the method is non-transactional.
    TransactionAttributeSource tas = getTransactionAttributeSource();
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
      //创建一个标准的事务
      TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
      Object retVal;
      try {
        执行目标类方法
        retVal = invocation.proceedWithInvocation();
      }
      catch (Throwable ex) {
        //目标方法事务回滚
        completeTransactionAfterThrowing(txInfo, ex);
        throw ex;
      }
      finally {
        //Reset the TransactionInfo ThreadLocal
        cleanupTransactionInfo(txInfo);
      }
      //提交事务
      commitTransactionAfterReturning(txInfo);
      return retVal;
    }
    //省略callback事务
  }

slf4j中的适配器

适配器模式的是让原本功能类似但接口定义不同的接口统一到一个相同的接口上,比如我们生活中的USB转接头。 在slf4j中,统一抽象了一个Logger接口,部分代码如下:

public void trace(String format, Object arg);
public void info(String format, Object arg);
public void debug(String format, Object arg);
public void error(String format, Object arg);

log4j的Log4jLoggerAdapter和logback的Logger都实现了这个接口,在项目中只要引入对应的日志依赖jar包,就可以决定是使用log4j还是logback

Tomcat中的facade设计模式

facade模式也叫门面模式主要是为子系统提供一组统一的上次接口,是子系统调用更简单。Tomcat使用了很多设计模式,让上层调用更简单,下次核心类更安全。比如RequestFacade和request都实现了HttpServletRequest类,但是当我们要获取请求中的session时,必须通过RequestFacade中getSession,

代码如下

public HttpSession getSession(boolean create) {

    if (request == null) {
        throw new IllegalStateException(
                        sm.getString("requestFacade.nullRequest"));
    }

    if (SecurityUtil.isPackageProtectionEnabled()){
        return AccessController.
            doPrivileged(new GetSessionPrivilegedAction(create));
    } else {
  //获取Request中的session
        return request.getSession(create);
    }
}

再看Request中的getSession方法

public HttpSession getSession(boolean create) {
        Session session = doGetSession(create);
        if (session == null) {
            return null;
        }
        //返回一个StandardSessionFacade
        return session.getSession();
    }

StandardSession代码如下

   
public HttpSession getSession() {
        if (facade == null) {
            if (SecurityUtil.isPackageProtectionEnabled()) {
                facade = AccessController.doPrivileged(new PrivilegedNewSessionFacade(this));
            } else {
                facade = new StandardSessionFacade(this);
            }
        }
        return facade;
    }

jdk中的享元模式

大家知道当我们在java中创建一个对象的时候,jvm需要在堆内存中分配空间,当我们对一个类创建的对象非常多的时候,我们考虑抽象出一个共享的对象,当然这个对象是不可变的。对于搞金融的小伙伴,我们对BigDecimal这个类不陌生,那它的享元你了解吗?直接上代码

// Cache of common small BigDecimal values.
    private static final BigDecimal zeroThroughTen[] = {
        new BigDecimal(BigInteger.ZERO,       0,  0, 1),
        new BigDecimal(BigInteger.ONE,        1,  0, 1),
        new BigDecimal(BigInteger.valueOf(2), 2,  0, 1),
        new BigDecimal(BigInteger.valueOf(3), 3,  0, 1),
        new BigDecimal(BigInteger.valueOf(4), 4,  0, 1),
        new BigDecimal(BigInteger.valueOf(5), 5,  0, 1),
        new BigDecimal(BigInteger.valueOf(6), 6,  0, 1),
        new BigDecimal(BigInteger.valueOf(7), 7,  0, 1),
        new BigDecimal(BigInteger.valueOf(8), 8,  0, 1),
        new BigDecimal(BigInteger.valueOf(9), 9,  0, 1),
        new BigDecimal(BigInteger.TEN,        10, 0, 2),
    };

    // Cache of zero scaled by 0 - 15
    private static final BigDecimal[] ZERO_SCALED_BY = {
        zeroThroughTen[0],
        new BigDecimal(BigInteger.ZERO, 0, 1, 1),
        new BigDecimal(BigInteger.ZERO, 0, 2, 1),
        new BigDecimal(BigInteger.ZERO, 0, 3, 1),
        new BigDecimal(BigInteger.ZERO, 0, 4, 1),
        new BigDecimal(BigInteger.ZERO, 0, 5, 1),
        new BigDecimal(BigInteger.ZERO, 0, 6, 1),
        new BigDecimal(BigInteger.ZERO, 0, 7, 1),
        new BigDecimal(BigInteger.ZERO, 0, 8, 1),
        new BigDecimal(BigInteger.ZERO, 0, 9, 1),
        new BigDecimal(BigInteger.ZERO, 0, 10, 1),
        new BigDecimal(BigInteger.ZERO, 0, 11, 1),
        new BigDecimal(BigInteger.ZERO, 0, 12, 1),
        new BigDecimal(BigInteger.ZERO, 0, 13, 1),
        new BigDecimal(BigInteger.ZERO, 0, 14, 1),
        new BigDecimal(BigInteger.ZERO, 0, 15, 1),
    };
public static final BigDecimal ZERO = zeroThroughTen[0];
public static final BigDecimal ONE = zeroThroughTen[1];
public static final BigDecimal TEN = zeroThroughTen[10];

这样我们需要使用这些变量的时候就可以不用new了,像这样:BigDecimal.ONE 或者BigDecimal.zeroThroughTen[1]

再举一个例子,java中的Integer类,valueOf方法就用享元模式获取对象,代码如下

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

面试官:好了,那谈谈行为型设计模式吧 我:

Google guava中的观察者模式

java中的观察者模式大家都熟悉了,定义观察者,定义被观察者,被观察者把观察者添加到自己的观察者列表中。Google guava中的观察者模式也是非常简单,用EventBus封装了封装了注册逻辑,并且可以传入线程池,看如下代码:

public class AObserver {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Subscribe
    public void handleMessage(Message msg){
        logger.info("a obsesrver receive message:{}", msg.getContent());
    }
}

public class EventBusUtil {

    public static EventBus getEventBus(){
        return EventBusFactory.getAsyncInstance();
    }

    public static class EventBusFactory{
        private static EventBus asyncEventBus = new AsyncEventBus(LocalThreadPoolExecutor.getExecutor());
        private static EventBus syncEventBus = new AsyncEventBus(MoreExecutors.directExecutor());

        public static EventBus getAsyncInstance(){
            return asyncEventBus;
        }

        public static EventBus getyncInstance(){
            return syncEventBus;
        }

    }

    public static void main(String[] args){
        EventBusUtil.getEventBus().register(new AObserver());
        EventBusUtil.getEventBus().register(new BObserver());
        EventBusUtil.getEventBus().post(new Message(100, "read me"));
    }
}

上面的观察者类方法上加了@Subscribe注解,这样在一个类中可以增加多个观察者,看看源码SubscriberRegistry类注册方法

/** Registers all subscriber methods on the given listener object. */
  void register(Object listener) {
    Multimap<Class<?>, Subscriber> listenerMethods = findAllSubscribers(listener);

    for (Entry<Class<?>, Collection<Subscriber>> entry : listenerMethods.asMap().entrySet()) {
      Class<?> eventType = entry.getKey();
      Collection<Subscriber> eventMethodsInListener = entry.getValue();

      CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);

      if (eventSubscribers == null) {
        CopyOnWriteArraySet<Subscriber> newSet = new CopyOnWriteArraySet<>();
        eventSubscribers =
            MoreObjects.firstNonNull(subscribers.putIfAbsent(eventType, newSet), newSet);
      }

      eventSubscribers.addAll(eventMethodsInListener);
    }
  }
  
private Multimap<Class<?>, Subscriber> findAllSubscribers(Object listener) {
    Multimap<Class<?>, Subscriber> methodsInListener = HashMultimap.create();
    Class<?> clazz = listener.getClass();
    for (Method method : getAnnotatedMethods(clazz)) {
      Class<?>[] parameterTypes = method.getParameterTypes();
      Class<?> eventType = parameterTypes[0];
      methodsInListener.put(eventType, Subscriber.create(bus, listener, method));
    }
    return methodsInListener;
  }

再看一下源码EventBus类中的通知方法

public void post(Object event) {
    Iterator<Subscriber> eventSubscribers = subscribers.getSubscribers(event);
    if (eventSubscribers.hasNext()) {
      dispatcher.dispatch(event, eventSubscribers);
    } else if (!(event instanceof DeadEvent)) {
      // the event had no subscribers and was not itself a DeadEvent
      post(new DeadEvent(this, event));
    }
  }

spring中的观察者模式

spring的观察者模式使用起来也是非常方便的,结合springboot使用,代码如下

//消息类需要继承ApplicationEvent
public class Message extends ApplicationEvent {

    private String content;

    public Message(Object source, String content) {
        super(source);
        this.content = content;
    }

    public String getContent(){
        return this.content;
    }
}
//观察者需要实现ApplicationListener接口并且注册为spring的bean
@Component
public class ObserverC implements ApplicationListener<Message> {

    @Override
    public void onApplicationEvent(Message event) {
        System.out.println(event.getContent());
    }
}
//需要用applicationContext来发送消息
@SpringBootApplication(scanBasePackages = {"design"})
public class Application {

    public static void main(String[] args){
        ApplicationContext applicationContext = SpringApplication.run(Application.class);
        applicationContext.publishEvent(new Message(new ObserverC(), "spring observable event"));

    }
}

下面我们看一下spring的源码是怎么注册观察者和做事件通知的

//注册观察者
ConditionEvaluationReportLoggingListener类
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
  this.applicationContext = applicationContext;
  applicationContext.addApplicationListener(new ConditionEvaluationReportListener());
  if (applicationContext instanceof GenericApplicationContext) {
    // Get the report early in case the context fails to load
    this.report = ConditionEvaluationReport.get(this.applicationContext.getBeanFactory());
  }
}
//AbstractApplicationContext类
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
  Assert.notNull(listener, "ApplicationListener must not be null");
  if (this.applicationEventMulticaster != null) {
    this.applicationEventMulticaster.addApplicationListener(listener);
  }
  this.applicationListeners.add(listener);
}
//再来看事件通知
//AbstractApplicationContext类
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    //省略部分代码
    // Multicast right now if possible - or lazily once the multicaster is initialized
    if (this.earlyApplicationEvents != null) {
      this.earlyApplicationEvents.add(applicationEvent);
    }
    else {
      //通过这个方法来发送通知
      getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    }
    //省略部分代码
  }

mybatis中的职责链模式

职责链模式定义是把一个请求传递给多个处理器来处理,这些处理器都放在一条链上,以实现发送和接受解耦 下面代码就是一个职责链模式的职责链类,定义处理器放到这个职责链上,就可以依次处理了。mybatis可以对Executor、ParameterHandler、ResultSetHandler、StatementHandler这4个对象的一些方法进行植入

public class HandlerChain {
 
    private List<Handler> handlers = new ArrayList<>(5);
 
    public void addHandler(Handler handler){
        handlers.add(handler);
    }
 
    public void handle(String event){
        handlers.forEach(r -> r.handle(event));
    }
}

mybatis中预留的plug实现了职责链模式,其中处理器类实现了org.apache.ibatis.plugin.Interceptor接口,并且在实现类上加注解@Intercepts,在原始sql语句末尾加入:AND EXISTS (SELECT * FROM zh_user r WHERE e.name=r.username) limit 1 , 看如下的代码:

public abstract class AbstractInterceptor implements Interceptor {
    
    protected MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
        MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        if (ms.getKeyProperties() != null) {
            for (String keyProperty : ms.getKeyProperties()) {
                builder.keyProperty(keyProperty);
            }
        }
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.cache(ms.getCache());
        builder.useCache(ms.isUseCache());
        return builder.build();
    }
}

@Intercepts(value = {@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class })})
public class LimitInterceptor extends AbstractInterceptor {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        logger.info("Interceptor......");

        // 获取原始sql语句
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameter = invocation.getArgs()[1];
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);
        String oldsql = boundSql.getSql();
        logger.info("old:"+oldsql);

        // 加入limit语句
        BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(), oldsql + " limit 1",
                boundSql.getParameterMappings(), boundSql.getParameterObject());
        MappedStatement newMs = copyFromMappedStatement(mappedStatement, new BoundSqlSqlSource(newBoundSql));
        invocation.getArgs()[0] = newMs;

        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

@Intercepts(value = {@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class })})
public class ExistInterceptor extends AbstractInterceptor {
    private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameter = invocation.getArgs()[1];
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);
        String oldsql = boundSql.getSql();
        logger.info("原始sql语句:"+oldsql);

        // 加入exist语句
        BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(), oldsql + " AND EXISTS (SELECT * FROM zh_user r WHERE e.name=r.username)",
                boundSql.getParameterMappings(), boundSql.getParameterObject());
        MappedStatement newMs = copyFromMappedStatement(mappedStatement, new BoundSqlSqlSource(newBoundSql));
        invocation.getArgs()[0] = newMs;

        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

使用的时候需要在mybatis配置文件中加入如下代码

<plugins>
  <plugin interceptor="boot.mybatis.plugin.LimitInterceptor"></plugin>
  <plugin interceptor="boot.mybatis.plugin.ExistInterceptor"></plugin>
</plugins>

而在mybatis初始化的时候会加载这2个处理器类,在执行查询语句额时候会植入插件中的内容,源码在InterceptorChain类

public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }
//在SqlSessionFactoryBean类中如下代码把处理器加入到职责链
  protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
  //省略其他代码
    if (!isEmpty(this.plugins)) {
      Stream.of(this.plugins).forEach(plugin -> {
        targetConfiguration.addInterceptor(plugin);
        LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
      });
    }
  //省略其他代码
    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
  }
//Configuration类中,在Executor的创建过程中调用职责链的pluginAll方法
  public Executor newExecutor(Transaction transaction) {
    return newExecutor(transaction, defaultExecutorType);
  }

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

Spring mvc中的模板模式

大家都知道,模板模式是父类提供一个抽象的模板方法,方法里面实现基本的处理逻辑或流程,但是方法里面的具体细节调用的是类中的抽象方法,这些抽象方法由子类实现。在spring的AbstractTemplateView类中定义了一个渲染视图的模板方法,

代码如下:

protected final void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
    if (this.exposeRequestAttributes) {
      //省略代码
    }

    if (this.exposeSessionAttributes) {
      //省略代码
    }

    if (this.exposeSpringMacroHelpers) {
      //省略代码
    }
    //省略代码,下面renderMergedTemplateModel是抽象方法,由FreeMarkerView、GroovyMarkupView和MustacheView来实现,大家自行查看源代码
    renderMergedTemplateModel(model, request, response);
  }

tomcat中的模板模式

tomcat定义了一个完整的声明周期Lifecycle,拿出几个方法看一下

protected abstract void startInternal() throws LifecycleException;
protected abstract void stopInternal() throws LifecycleException;
protected abstract void destroyInternal() throws LifecycleException;

做了一个抽象模板类LifecycleBase,以start方法为例,下面的代码做了一个start的模板代码,startInternal由子类去实现

public final synchronized void start() throws LifecycleException {
  //省略部分代码
        try {
            setStateInternal(LifecycleState.STARTING_PREP, null, false);
      //下面的方法由子类自己实现
            startInternal();
            if (state.equals(LifecycleState.FAILED)) {
                // This is a 'controlled' failure. The component put itself into the
                // FAILED state so call stop() to complete the clean-up.
                stop();
            } else if (!state.equals(LifecycleState.STARTING)) {
                // Shouldn't be necessary but acts as a check that sub-classes are
                // doing what they are supposed to.
                invalidTransition(Lifecycle.AFTER_START_EVENT);
            } else {
                setStateInternal(LifecycleState.STARTED, null, false);
            }
        } catch (Throwable t) {
            // This is an 'uncontrolled' failure so put the component into the
            // FAILED state and throw an exception.
            handleSubClassException(t, "lifecycleBase.startFail", toString());
        }
    }

mybatis中的策略模式

策略模式是让策略的实现与策略的使用解耦,在我们的日常开发中,经常用来优化繁琐的if-else的代码,mybatis的Executor是一个策略接口,具体实现的类有6个,如下图

策略工厂类是Configuration,代码如下:

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

我们可以看到mybatis的代码并没有解决if-else问题,这种策略模式被称为有状态的设计模式,我们可修改策略工厂,改成无状态的策略模式,如下,但是堆内存中会有一些不用的对象

static Map<ExecutorType, Executor> executors = new HashMap<>(8);
static{
    executors.put(ExecutorType.SIMPLE, new SimpleExecutor(this, transaction));
    executors.put(ExecutorType.BATCH, new BatchExecutor(this, transaction));
    ...
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    return executors.get(executorType);
}

我:接下来我再聊spring bean生命周期管理中用到的模板模式... 面试官:可以了,恭喜你通过了