Skip to content

Latest commit

 

History

History
808 lines (664 loc) · 29.7 KB

设计模式-源码学习3.md

File metadata and controls

808 lines (664 loc) · 29.7 KB

源码学习3

mybatis的设计原则

JDBC是Java访问数据库的开发规范,提供了一套抽象的统一的开发接口,隐藏不同数据库的访问细节。 而java中的ORM框架是基于JDBC进行再封装,负责将程序中的对象存储到数据库中、将数据库中的数据转化为程序中的对象。

java中常用的orm框架有:JdbcTemplate,mybatis,hibernate

JdbcTemplate: 需要编写跟业务相关的代码(比如,SQL语句、数据库中数据与对象之间的互相转化的代码),其他流程性质的代码(比如,加载驱动、创建数据库连接、创建statement、关闭连接、关闭statement等)都封装在了JdbcTemplate类中,不需要我们重复编写。

mybatis: 只需要写sql语句即可,数据库字段与对象字段可以基于约定直接进行映射;更加简化;

hibernate: 全自动映射,sql都是自动生成;

  • 灵活性与易用性比较:

1 JdbcTemplate更加轻量,性能最好,易用性较差。因为它对JDBC只做了很简单的封装,所以性能损耗比较少。但是,它的SQL与代码耦合在一起,而且不具备ORM的功能,需要自己编写代码,解析对象跟数据库中的数据之间的映射关系。
2 Hibernate更加重量级。Hibernate提供了更加高级的映射功能,能够根据业务需求自动生成SQL语句。往往把Hibernate称作全自动化的ORM框架。不过,虽然自动生成SQL简化了开发,但是毕竟是自动生成的,没有针对性的优化。在性能方面,这样得到的SQL可能没有程序员编写得好。同时,这样也丧失了程序员自己编写SQL的灵活性。
3 MyBatis性能中等,易用性更好;自己编写SQL。MyBatis往往被称作半自动化的ORM框架.由于sql自定义,使用中更灵活.

职责链模式

MyBatis Plugin跟Servlet Filter、Spring Interceptor的功能是类似的,都是在不需要修改原有流程代码的情况下,拦截某些方法调用,在拦截的方法调用的前后,执行一些额外的代码逻辑。它们的唯一区别在于拦截的位置是不同的。Servlet Filter主要拦截Servlet请求,Spring Interceptor主要拦截Spring管理的Bean方法(比如Controller类的方法等),而MyBatis Plugin主要拦截的是MyBatis在执行SQL的过程中涉及的一些方法。

自定义拦截器示例

@Intercepts({
        @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
        @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
        @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})})
public class SqlCostTimeInterceptor implements Interceptor {
  private static Logger logger = LoggerFactory.getLogger(SqlCostTimeInterceptor.class);

  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    Object target = invocation.getTarget();
    long startTime = System.currentTimeMillis();
    StatementHandler statementHandler = (StatementHandler) target;
    try {
      return invocation.proceed();
    } finally {
      long costTime = System.currentTimeMillis() - startTime;
      BoundSql boundSql = statementHandler.getBoundSql();
      String sql = boundSql.getSql();
      logger.info("执行 SQL:[ {} ]执行耗时[ {} ms]", sql, costTime);
    }
  }

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

  @Override
  public void setProperties(Properties properties) {
    System.out.println("插件配置的信息:"+properties);
  }
}

<!-- MyBatis全局配置文件:mybatis-config.xml -->
<plugins>
  <plugin interceptor="com.xzg.cd.a88.SqlCostTimeInterceptor">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

拦截器的使用:

1 明确拦截目标方法
2 进行拦截方法前后处理操作

mybatis plugin的拦截目标方法通过@Intercepts,@Signature注解完成.

@Intercepts: 用于声明是拦截器
@Signature: 用于定义具体拦截参数type、method、args。其中,type指明要拦截的类、method指明方法名、args指明方法的参数列表。

具体拦截参数列表:
@Signature具体拦截参数

MyBatis底层是通过Executor类来执行SQL的。Executor类会创建StatementHandler、ParameterHandler、ResultSetHandler三个对象,并且,首先使用ParameterHandler设置SQL中的占位符参数,然后使用StatementHandler执行SQL语句,最后使用ResultSetHandler封装执行结果。所以,我们只需要拦截Executor、ParameterHandler、ResultSetHandler、StatementHandler这几个类的方法,基本上就能满足我们对整个SQL执行流程的拦截了。

通过动态代理+拦截器,可以实现分库分表、自动分页、数据脱敏、加密解密等前置后置操作,业务实现更灵活

mybatis plugin 拦截器实现方式:

1 配置解析,获取拦截器配置并填充拦截器链
2 sql执行时进行调用拦截器链中方法,触发自定义拦截器
3 自定义拦截器中通过重新plugin方法,并且通过动态代理来在sql执行方法前后增加代理行为

1 配置解析

public class XMLConfigBuilder extends BaseBuilder {
  //解析配置
  private void parseConfiguration(XNode root) {
    try {
     //省略部分代码...
      pluginElement(root.evalNode("plugins")); //解析插件
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

  //解析插件
   private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        String interceptor = child.getStringAttribute("interceptor");
        Properties properties = child.getChildrenAsProperties();
        //创建Interceptor类对象
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        //调用Interceptor上的setProperties()方法设置properties
        interceptorInstance.setProperties(properties);
        //下面这行代码会调用InterceptorChain.addInterceptor()方法
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }
}

// Configuration类的addInterceptor()方法的代码如下所示
public void addInterceptor(Interceptor interceptor) {
  interceptorChain.addInterceptor(interceptor);
}

拦截器类声明

public class Invocation {
  private final Object target;
  private final Method method;
  private final Object[] args;
  // 省略构造函数和getter方法...
  public Object proceed() throws InvocationTargetException, IllegalAccessException {
    return method.invoke(target, args);
  }
}
// 拦截器抽象定义,需要用户实现该拦截器后进行拦截扩展
public interface Interceptor {
  Object intercept(Invocation invocation) throws Throwable;
  Object plugin(Object target);
  void setProperties(Properties properties);
}

public class InterceptorChain {
  private final List<Interceptor> interceptors = new ArrayList<Interceptor>();

  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      // 这里是实际触发拦截的调用
      target = interceptor.plugin(target);
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }
  
  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }
}

2 sql执行时触发自定义拦截器

在执行SQL的过程中,MyBatis会创建Executor、StatementHandler、ParameterHandler、ResultSetHandler这几个类的对象,对应的创建代码在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;
}

public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
  ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
  parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
  return parameterHandler;
}

public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
    ResultHandler resultHandler, BoundSql boundSql) {
  ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
  resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
  return resultSetHandler;
}

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
  statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
  return statementHandler;
}

3 自定义拦截器中通过重新plugin方法实现业务逻辑

自定义拦截器完整的代码见之前的"自定义拦截器示例".职责链模式的核心是统一处理方式和响应方式,而mybatis plugin通过动态代理又进一步的强化了处理行为.

// 上面的自定义拦截器示例中重写了plugin,intercept方法;intercept是具体的代理方法;plugin用于触发动态代理;
  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    Object target = invocation.getTarget();
    long startTime = System.currentTimeMillis();
    StatementHandler statementHandler = (StatementHandler) target;
    try {
      return invocation.proceed();
    } finally {
      long costTime = System.currentTimeMillis() - startTime;
      BoundSql boundSql = statementHandler.getBoundSql();
      String sql = boundSql.getSql();
      logger.info("执行 SQL:[ {} ]执行耗时[ {} ms]", sql, costTime);
    }
  }

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

动态代理触发插件类,这是mybatis plugin的职责链模式的特殊的地方,它通过实际判断用户自定义的动态代理是否要执行.

// 借助Java InvocationHandler实现的动态代理模式
public class Plugin implements InvocationHandler {
  private final Object target;
  private final Interceptor interceptor;
  private final Map<Class<?>, Set<Method>> signatureMap;

  private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
    this.target = target;
    this.interceptor = interceptor;
    this.signatureMap = signatureMap;
  }

  // wrap()静态方法,用来生成target的动态代理,
  // 动态代理对象=target对象+interceptor对象。
  public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }

  // 调用target上的f()方法,会触发执行下面这个方法。
  // 这个方法包含:执行interceptor的intecept()方法 + 执行target上f()方法。
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      if (methods != null && methods.contains(method)) {
        return interceptor.intercept(new Invocation(target, method, args));
      }
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }

  private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
    // issue #251
    if (interceptsAnnotation == null) {
      throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());      
    }
    Signature[] sigs = interceptsAnnotation.value();
    Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
    for (Signature sig : sigs) {
      Set<Method> methods = signatureMap.get(sig.type());
      if (methods == null) {
        methods = new HashSet<Method>();
        signatureMap.put(sig.type(), methods);
      }
      try {
        Method method = sig.type().getMethod(sig.method(), sig.args());
        methods.add(method);
      } catch (NoSuchMethodException e) {
        throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
      }
    }
    return signatureMap;
  }

  private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
    Set<Class<?>> interfaces = new HashSet<Class<?>>();
    while (type != null) {
      for (Class<?> c : type.getInterfaces()) {
        if (signatureMap.containsKey(c)) {
          interfaces.add(c);
        }
      }
      type = type.getSuperclass();
    }
    return interfaces.toArray(new Class<?>[interfaces.size()]);
  }
}

当执行Executor、StatementHandler、ParameterHandler、ResultSetHandler这四个类上的某个方法的时候,MyBatis会嵌套执行每层代理对象(Plugin对象)上的invoke()方法。而invoke()方法会先执行代理对象中的interceptor的intecept()函数,然后再执行被代理对象上的方法。就这样,一层一层地把代理对象上的intercept()函数执行完之后,MyBatis才最终执行那4个原始类对象上的方法。

mybatis中SqlSessionFactoryBuilder,SqlSessionFactory与ApplicationContext

SqlSessionFactoryBuilder:用于配置文件后,创建SqlSessionFactory
SqlSessionFactory:用于根据传入参数创建SqlSession

使用示例

  public static void main(String[] args) throws IOException {
    Reader reader = Resources.getResourceAsReader("mybatis.xml");
    SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
    SqlSession session = sessionFactory.openSession();
    UserMapper userMapper = session.getMapper(UserMapper.class);
    UserDo userDo = userMapper.selectById(8);
    //...
  }

SqlSessionFactoryBuilder根据命名和使用,都是指明它是建造者模式,但是实际它的定义又不是标准的建造者模式:它没有赋值方法,且build()方法需要传入参数.所以这里使用setter+构造方法也可以实现.但是由于参数的灵活性,构造方法可能存在过多,过长的问题;然后这里builder的核心价值是封装配置文件解析过程,使调用更简单.

public class SqlSessionFactoryBuilder {
  public SqlSessionFactory build(Reader reader);
  public SqlSessionFactory build(Reader reader, String environment);
  public SqlSessionFactory build(Reader reader, Properties properties);
  public SqlSessionFactory build(Reader reader, String environment, Properties properties);
  
  public SqlSessionFactory build(InputStream inputStream);
  public SqlSessionFactory build(InputStream inputStream, String environment);
  public SqlSessionFactory build(InputStream inputStream, Properties properties);
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties);

  // 上面所有的方法最终都调用这个方法    
  public SqlSessionFactory build(Configuration config);
}

SqlSessionFactory:到底属于工厂模式还是建造器模式?

public interface SqlSessionFactory {
  SqlSession openSession();
  SqlSession openSession(boolean autoCommit);
  SqlSession openSession(Connection connection);
  SqlSession openSession(TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType);
  SqlSession openSession(ExecutorType execType, boolean autoCommit);
  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType, Connection connection);
  Configuration getConfiguration();
}

它的唯一实现类:DefaultSqlSessionFactory,不像工厂模式而更新构造者模式.因为工厂模式是基于同一声明创建不同类型的类;而构造者模式是根据不同的参数创建同一类型的类.这里都是创建DefaultSqlSession类.所以更像构造者模式.

实际上,这两个类的作用只不过是为了创建SqlSession对象,没有其他作用。所以,我更建议参照Spring的设计思路,把SqlSessionFactoryBuilder和SqlSessionFactory的逻辑,放到一个叫“ApplicationContext”的类中。让这个类来全权负责读入配置文件,创建Congfiguration,生成SqlSession。
按照通用方式进行命名和分类,有利于代码的理解;可读性很重要.

public class DefaultSqlSessionFactory implements SqlSessionFactory {
  private final Configuration configuration;
  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }

  @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

  @Override
  public SqlSession openSession(boolean autoCommit) {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
  }

  @Override
  public SqlSession openSession(ExecutorType execType) {
    return openSessionFromDataSource(execType, null, false);
  }

  @Override
  public SqlSession openSession(TransactionIsolationLevel level) {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
  }

  @Override
  public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
    return openSessionFromDataSource(execType, level, false);
  }

  @Override
  public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
    return openSessionFromDataSource(execType, null, autoCommit);
  }

  @Override
  public SqlSession openSession(Connection connection) {
    return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
  }

  @Override
  public SqlSession openSession(ExecutorType execType, Connection connection) {
    return openSessionFromConnection(execType, connection);
  }

  @Override
  public Configuration getConfiguration() {
    return configuration;
  }

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
    try {
      boolean autoCommit;
      try {
        autoCommit = connection.getAutoCommit();
      } catch (SQLException e) {
        // Failover to true, as most poor drivers
        // or databases won't support transactions
        autoCommit = true;
      }      
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      final Transaction tx = transactionFactory.newTransaction(connection);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  //...省略部分代码...
}

模板模式

抽象方法提取功能方法,并定义抽象实现类让其子类实现,通过不同的实现进而在调用时动态实现类的执行,实现方法的扩展性.

public abstract class BaseExecutor implements Executor {
  //...省略其他无关代码...
  
  @Override
  public int update(MappedStatement ms, Object parameter) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    clearLocalCache();
    return doUpdate(ms, parameter);
  }

  public List<BatchResult> flushStatements(boolean isRollBack) throws SQLException {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    return doFlushStatements(isRollBack);
  }

  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

  @Override
  public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    return doQueryCursor(ms, parameter, rowBounds, boundSql);
  }

  protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;

  protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException;

  protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException;

  protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException;
}

解释器模式

mybatis的一大特性就是动态sql解析,它的实现是基于解析器模式来实现的.
解析器是自定义一种解析语法,来进行数据的识别和转义.它的定义分为三部分:

1 定义语法声明
2 判断要解析的语法
3 进行语法解析

解析器示例

// 定义解析语法: "123"

// 语法解析器定义
public interface Expression {
  boolean apply(DynamicContext context);//判断要解析的语法
  long interpret();// 进行语法解析
}
// 解析器实现类
public class NumberExpression implements Expression {
  private long number;

  public NumberExpression(long number) {
    this.number = number;
  }

  public NumberExpression(String number) {
    this.number = Long.parseLong(number);
  }

  @Override
  public long interpret() {
    return this.number;
  }

  boolean apply(DynamicContext context){return context.isNum()};
}

sql配置(语法声明):根据不同的请求参数,生成不同的sql

<update id="update" parameterType="com.xzg.cd.a89.User"
   UPDATE user
   <trim prefix="SET" prefixOverrides=",">
       <if test="name != null and name != ''">
           name = #{name}
       </if>
       <if test="age != null and age != ''">
           , age = #{age}
       </if>
       <if test="birthday != null and birthday != ''">
           , birthday = #{birthday}
       </if>
   </trim>
   where id = ${id}
</update>

解析器模型

public interface SqlNode {
 boolean apply(DynamicContext context);
}

具体的解析语法 解析器模型实现

整个解释器的调用入口在DynamicSqlSource.getBoundSql方法中,它调用了rootSqlNode.apply(context)方法。

装饰器模式

通过组合来灵活进行方法增强.MyBatis是一个ORM框架。实际上,它不只是简单地完成了对象和数据库数据之间的互相转化,还提供了很多其他功能,比如缓存、事务等。接下来,我们再讲讲它的缓存实现。

在MyBatis中,缓存功能由接口Cache定义。PerpetualCache类是最基础的缓存类,是一个大小无限的缓存。除此之外,MyBatis还设计了9个包裹PerpetualCache类的装饰器类,用来实现功能增强。它们分别是:FifoCache、LoggingCache、LruCache、ScheduledCache、SerializedCache、SoftCache、SynchronizedCache、WeakCache、TransactionalCache。

public interface Cache {
  String getId();
  void putObject(Object key, Object value);
  Object getObject(Object key);
  Object removeObject(Object key);
  void clear();
  int getSize();
  ReadWriteLock getReadWriteLock();
}

public class PerpetualCache implements Cache {
  private final String id;
  private Map<Object, Object> cache = new HashMap<Object, Object>();

  public PerpetualCache(String id) {
    this.id = id;
  }

  @Override
  public String getId() {
    return id;
  }

  @Override
  public int getSize() {
    return cache.size();
  }

  @Override
  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }

  @Override
  public Object getObject(Object key) {
    return cache.get(key);
  }

  @Override
  public Object removeObject(Object key) {
    return cache.remove(key);
  }

  @Override
  public void clear() {
    cache.clear();
  }

  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }
  //省略部分代码...
}

这9个装饰器类的代码结构都类似,我只将其中的LruCache的源码贴到这里

public class LruCache implements Cache {
  private final Cache delegate;
  private Map<Object, Object> keyMap;
  private Object eldestKey;

  public LruCache(Cache delegate) {
    this.delegate = delegate;
    setSize(1024);
  }

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public int getSize() {
    return delegate.getSize();
  }

  public void setSize(final int size) {
    keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
      private static final long serialVersionUID = 4267176411845948333L;

      @Override
      protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
        boolean tooBig = size() > size;
        if (tooBig) {
          eldestKey = eldest.getKey();
        }
        return tooBig;
      }
    };
  }

  @Override
  public void putObject(Object key, Object value) {
    delegate.putObject(key, value);
    cycleKeyList(key);
  }

  @Override
  public Object getObject(Object key) {
    keyMap.get(key); //touch
    return delegate.getObject(key);
  }

  @Override
  public Object removeObject(Object key) {
    return delegate.removeObject(key);
  }

  @Override
  public void clear() {
    delegate.clear();
    keyMap.clear();
  }

  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }

  private void cycleKeyList(Object key) {
    keyMap.put(key, key);
    if (eldestKey != null) {
      delegate.removeObject(eldestKey);
      eldestKey = null;
    }
  }
}

迭代器模式

PropertyTokenizer类也并非标准的迭代器类。它将配置的解析、解析之后的元素、迭代器,这三部分本该放到三个类中的代码,都耦合在一个类中,所以看起来稍微有点难懂。不过,这样做的好处是能够做到惰性解析。我们不需要事先将整个配置,解析成多个PropertyTokenizer对象。只有当我们在调用next()函数的时候,才会解析其中部分配置。

// person[0].birthdate.year 会被分解为3个PropertyTokenizer对象。其中,第一个PropertyTokenizer对象的各个属性值如注释所示。
public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
  private String name; // person
  private final String indexedName; // person[0]
  private String index; // 0
  private final String children; // birthdate.year

  public PropertyTokenizer(String fullname) {
    int delim = fullname.indexOf('.');
    if (delim > -1) {
      name = fullname.substring(0, delim);
      children = fullname.substring(delim + 1);
    } else {
      name = fullname;
      children = null;
    }
    indexedName = name;
    delim = name.indexOf('[');
    if (delim > -1) {
      index = name.substring(delim + 1, name.length() - 1);
      name = name.substring(0, delim);
    }
  }

  public String getName() {
    return name;
  }

  public String getIndex() {
    return index;
  }

  public String getIndexedName() {
    return indexedName;
  }

  public String getChildren() {
    return children;
  }

  @Override
  public boolean hasNext() {
    return children != null;
  }

  @Override
  public PropertyTokenizer next() {
    return new PropertyTokenizer(children);
  }

  @Override
  public void remove() {
    throw new UnsupportedOperationException("Remove is not supported, as it has no meaning in the context of properties.");
  }
}