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指明方法的参数列表。
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个原始类对象上的方法。
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.");
}
}