SqlSessionFactory.java
package com.lino.mybatis.session;
/**
* @description: 工厂模式接口,构建SqlSession的工厂
*/
public interface SqlSessionFactory {
/**
* 打开一个session
*
* @return SqlSession
*/
SqlSession openSession();
}
DefaultSqlSessionFactory.java
package com.lino.mybatis.session.defaults;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.SqlSession;
import com.lino.mybatis.session.SqlSessionFactory;
import com.lino.mybatis.session.TransactionIsolationLevel;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.transaction.TransactionFactory;
import java.sql.SQLException;
/**
* @description: 默认的SqlSessionFactory实现类
*/
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
TransactionFactory transactionFactory = environment.getTransactionFactory();
tx = transactionFactory.newTransaction(configuration.getEnvironment().getDataSource(), TransactionIsolationLevel.READ_COMMITTED, false);
// 创建执行器
final Executor executor = configuration.newExecutor(tx);
// 创建 DefaultSqlSession
return new DefaultSqlSession(configuration, executor);
} catch (Exception e) {
try {
assert tx != null;
tx.close();
} catch (SQLException ignore) {
}
throw new RuntimeException("Error opening session. Cause: " + e);
}
}
}
SqlSessionFactory
是获取会话的工厂,每次我们使用 Mybatis 操作数据库的时候,都会开启一个新的会话。
SqlSessionFactory
、ObjectFactory
、MapperproxyFactory
、DataSourceFactory
Configuration.java
package com.lino.mybatis.session;
import com.lino.mybatis.binding.MapperRegistry;
import com.lino.mybatis.cache.Cache;
import com.lino.mybatis.cache.decorators.FifoCache;
import com.lino.mybatis.cache.impl.PerpetualCache;
import com.lino.mybatis.datasource.druid.DruidDataSourceFactory;
import com.lino.mybatis.datasource.pooled.PooledDataSourceFactory;
import com.lino.mybatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.lino.mybatis.executor.CachingExecutor;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.SimpleExecutor;
import com.lino.mybatis.executor.keygen.KeyGenerator;
import com.lino.mybatis.executor.parameter.ParameterHandler;
import com.lino.mybatis.executor.resultset.DefaultResultSetHandler;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.executor.statement.PreparedStatementHandler;
import com.lino.mybatis.executor.statement.StatementHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.mapping.ResultMap;
import com.lino.mybatis.plugin.Interceptor;
import com.lino.mybatis.plugin.InterceptorChain;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.factory.DefaultObjectFactory;
import com.lino.mybatis.reflection.factory.ObjectFactory;
import com.lino.mybatis.reflection.wrapper.DefaultObjectWrapperFactory;
import com.lino.mybatis.reflection.wrapper.ObjectWrapperFactory;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.scripting.LanguageDriverRegistry;
import com.lino.mybatis.scripting.xmltags.XMLLanguageDriver;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.transaction.jdbc.JdbcTransactionFactory;
import com.lino.mybatis.type.TypeAliasRegistry;
import com.lino.mybatis.type.TypeHandlerRegistry;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @description: 配置项
*/
public class Configuration {
/**
* 环境
*/
protected Environment environment;
/**
* 是否使用自动生成键值对
*/
protected boolean useGeneratedKeys = false;
/**
* 默认启用缓存,cacheEnabled = true/false
*/
protected boolean cacheEnabled = true;
/**
* 缓存机制,默认不配置的情况是 SESSION
*/
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
/**
* 映射注册机
*/
protected MapperRegistry mapperRegistry = new MapperRegistry(this);
/**
* 映射的语句,存在Map里
*/
protected final Map<String, MappedStatement> mappedStatements = new HashMap<>(16);
/**
* 缓存,存在Map里
*/
protected final Map<String, Cache> caches = new HashMap<>(16);
/**
* 结果映射,存在Map里
*/
protected final Map<String, ResultMap> resultMaps = new HashMap<>(16);
/**
* 键值生成器,存在Map里
*/
protected final Map<String, KeyGenerator> keyGenerators = new HashMap<>(16);
/**
* 插件拦截器链
*/
protected final InterceptorChain interceptorChain = new InterceptorChain();
/**
* 类型别名注册机
*/
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
/**
* 脚本语言注册器
*/
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
/**
* 类型处理器注册机
*/
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
/**
* 对象工厂
*/
protected ObjectFactory objectFactory = new DefaultObjectFactory();
/**
* 对象包装工厂
*/
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
/**
* 准备资源列表
*/
protected final Set<String> loadedResources = new HashSet<>();
/**
* 数据库ID
*/
protected String databaseId;
//...
}
ErrorContext
、LogFactory
、Configutation
ResultMap.java
package com.lino.mybatis.mapping;
import com.lino.mybatis.session.Configuration;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
/**
* @description: 结果映射
*/
public class ResultMap {
private String id;
private Class<?> type;
private List<ResultMapping> resultMappings;
private Set<String> mappedColumns;
public ResultMap() {
}
public static class Builder {
private ResultMap resultMap = new ResultMap();
public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings) {
resultMap.id = id;
resultMap.type = type;
resultMap.resultMappings = resultMappings;
}
public ResultMap build() {
resultMap.mappedColumns = new HashSet<>();
// 添加 mappedColums 字段
for (ResultMapping resultMapping : resultMap.resultMappings) {
final String column = resultMapping.getColumn();
if (column != null) {
resultMap.mappedColumns.add(column.toUpperCase(Locale.ENGLISH));
}
}
return resultMap;
}
}
//...
}
SqlSesiionFactoryBuilder
、XMLConfigBuilder
、XMLMapperBuilder
、XMLStatementBuilder
、CacheBuilder
Log.java
package com.lino.mybatis.logging;
/**
* @description: 日志接口
*/
public interface Log {
boolean isDebugEnabled();
boolean isTraceEnabled();
void error(String s, Throwable e);
void error(String s);
void debug(String s);
void trace(String s);
void warn(String s);
}
Slf4jImpl.java
package com.lino.mybatis.logging.slf4j;
import com.lino.mybatis.logging.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @description: slf4j log 设计模式类,不做实现
*/
public class Slf4jImpl implements Log {
public Slf4jImpl(String clazz) {
Logger logger = LoggerFactory.getLogger(clazz);
}
@Override
public boolean isDebugEnabled() {
return false;
}
@Override
public boolean isTraceEnabled() {
return false;
}
@Override
public void error(String s, Throwable e) {
}
@Override
public void error(String s) {
}
@Override
public void debug(String s) {
}
@Override
public void trace(String s) {
}
@Override
public void warn(String s) {
}
}
Log4j、Log4j2、Slf4J
等等,而这些日志框架的使用接口又都各有差异,为了统一这些日志工具的接口,Mybatis 定义了一套统一的日志接口,为所有的其他日志工具接口做相应的适配操作。MapperProxy.java
package com.lino.mybatis.binding;
import com.lino.mybatis.session.SqlSession;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
/**
* @description: 映射器代理类
*/
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
final MapperMethod mapperMethod = cacheMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
}
/**
* 去缓存中找MapperMethod
*/
private MapperMethod cacheMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
DriverProxy
、Plugin
、Invoker
、MapperProxy
SqlNode.java
package com.lino.mybatis.scripting.xmltags;
/**
* @description: SQL 节点
*/
public interface SqlNode {
/**
* 应用动态上下文
*
* @param context 动态上下文
* @return boolean
*/
boolean apply(DynamicContext context);
}
IfSqlNode.java
package com.lino.mybatis.scripting.xmltags;
/**
* @description: IF SQL 节点
*/
public class IfSqlNode implements SqlNode {
private ExpressionEvaluator evaluator;
private String test;
private SqlNode contents;
public IfSqlNode(SqlNode contents, String test) {
this.test = test;
this.contents = contents;
this.evaluator = new ExpressionEvaluator();
}
@Override
public boolean apply(DynamicContext context) {
// 如果满足条件,则apply,并返回true
if (evaluator.evaluateBoolean(test, context.getBindings())) {
contents.apply(context);
return true;
}
return false;
}
}
XMLScriptBuilder.java
package com.lino.mybatis.scripting.xmltags;
import com.lino.mybatis.builder.BaseBuilder;
import com.lino.mybatis.mapping.SqlSource;
import com.lino.mybatis.scripting.defaults.RawSqlSource;
import com.lino.mybatis.session.Configuration;
import org.dom4j.Element;
import org.dom4j.Node;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @description: XML脚本构建器
*/
public class XMLScriptBuilder extends BaseBuilder {
private Element element;
private boolean isDynamic;
private Class<?> parameterType;
private final Map<String, NodeHandler> nodeHandlerMap = new HashMap<>();
public XMLScriptBuilder(Configuration configuration, Element element, Class<?> parameterType) {
super(configuration);
this.element = element;
this.parameterType = parameterType;
initNodeHandlerMap();
}
private void initNodeHandlerMap() {
// 9种,实现其中2种 trim/where/set/foreach/if/choose/when/otherwise/bind
nodeHandlerMap.put("trim", new TrimHandler());
nodeHandlerMap.put("if", new IfHandler());
}
public SqlSource parseScriptNode() {
List<SqlNode> contents = parseDynamicTags(element);
MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
SqlSource sqlSource = null;
if (isDynamic) {
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
private List<SqlNode> parseDynamicTags(Element element) {
List<SqlNode> contents = new ArrayList<>();
List<Node> children = element.content();
for (Node child : children) {
if (child.getNodeType() == Node.TEXT_NODE || child.getNodeType() == Node.CDATA_SECTION_NODE) {
String data = child.getText();
TextSqlNode textSqlNode = new TextSqlNode(data);
if (textSqlNode.isDynamic()) {
contents.add(textSqlNode);
isDynamic = true;
} else {
contents.add(new StaticTextSqlNode(data));
}
} else if (child.getNodeType() == Node.ELEMENT_NODE) {
String nodeName = child.getName();
NodeHandler handler = nodeHandlerMap.get(nodeName);
if (handler == null) {
throw new RuntimeException("Unknown element <" + nodeName + "> in SQL statement.");
}
handler.handleNode(element.element(child.getName()), contents);
isDynamic = true;
}
}
return contents;
}
private interface NodeHandler {
void handleNode(Element nodeToHandle, List<SqlNode> targetContents);
}
private class TrimHandler implements NodeHandler {
@Override
public void handleNode(Element nodeToHandle, List<SqlNode> targetContents) {
List<SqlNode> contents = parseDynamicTags(nodeToHandle);
MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
String prefix = nodeToHandle.attributeValue("prefix");
String prefixOverrides = nodeToHandle.attributeValue("prefixOverrides");
String suffix = nodeToHandle.attributeValue("suffix");
String suffixOverrides = nodeToHandle.attributeValue("suffixOverrides");
TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
targetContents.add(trim);
}
}
private class IfHandler implements NodeHandler {
@Override
public void handleNode(Element nodeToHandle, List<SqlNode> targetContents) {
List<SqlNode> contents = parseDynamicTags(nodeToHandle);
MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
String test = nodeToHandle.attributeValue("test");
IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
targetContents.add(ifSqlNode);
}
}
}
Activity_Mapper.xml
<select id="queryActivityById" parameterType="com.lino.mybatis.test.po.Activity" resultMap="activityMap">
SELECT activity_id, activity_name, activity_desc, create_time, update_time
FROM activity
<trim prefix="where" prefixOverrides="AND | OR" suffixOverrides="and">
<if test="null != activityId">
activity_id = #{activityId}
if>
trim>
select>
trim/where/set/foreach/if/choose/when/otherwise/bind
)标签的使用,让使用者可以组合出各类场景的 SQL 语句。而 SqlNode 接口的实现就是每一个组合结构种的规则节点,通过规则节点的组装完成一颗规则树组合模式的使用。Congiguration.java
package com.lino.mybatis.session;
import com.lino.mybatis.binding.MapperRegistry;
import com.lino.mybatis.cache.Cache;
import com.lino.mybatis.cache.decorators.FifoCache;
import com.lino.mybatis.cache.impl.PerpetualCache;
import com.lino.mybatis.datasource.druid.DruidDataSourceFactory;
import com.lino.mybatis.datasource.pooled.PooledDataSourceFactory;
import com.lino.mybatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.lino.mybatis.executor.CachingExecutor;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.SimpleExecutor;
import com.lino.mybatis.executor.keygen.KeyGenerator;
import com.lino.mybatis.executor.parameter.ParameterHandler;
import com.lino.mybatis.executor.resultset.DefaultResultSetHandler;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.executor.statement.PreparedStatementHandler;
import com.lino.mybatis.executor.statement.StatementHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.mapping.ResultMap;
import com.lino.mybatis.plugin.Interceptor;
import com.lino.mybatis.plugin.InterceptorChain;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.factory.DefaultObjectFactory;
import com.lino.mybatis.reflection.factory.ObjectFactory;
import com.lino.mybatis.reflection.wrapper.DefaultObjectWrapperFactory;
import com.lino.mybatis.reflection.wrapper.ObjectWrapperFactory;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.scripting.LanguageDriverRegistry;
import com.lino.mybatis.scripting.xmltags.XMLLanguageDriver;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.transaction.jdbc.JdbcTransactionFactory;
import com.lino.mybatis.type.TypeAliasRegistry;
import com.lino.mybatis.type.TypeHandlerRegistry;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @description: 配置项
*/
public class Configuration {
/**
* 生产执行器
*
* @param transaction 事务
* @return 执行器
*/
public Executor newExecutor(Transaction transaction) {
Executor executor = new SimpleExecutor(this, transaction);
// 配置开启缓存,创建 CachingExecutor(默认就是有缓存)装饰着模式
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
return executor;
}
}
BaseExecutor.java
package com.lino.mybatis.executor;
import com.lino.mybatis.cache.CacheKey;
import com.lino.mybatis.cache.impl.PerpetualCache;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.mapping.ParameterMapping;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.LocalCacheScope;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.session.RowBounds;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.type.TypeHandlerRegistry;
import org.slf4j.LoggerFactory;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
/**
* @description: 执行器抽象基类
*/
public abstract class BaseExecutor implements Executor {
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
if (closed) {
throw new RuntimeException("Executor was closed.");
}
// 清理局部缓存,查询堆栈为0则清理。queryStack 避免递归调用清理
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
// 根据cacheKey从localCache中查询数据
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list == null) {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache();
}
}
return list;
}
}
SimpleExecutor.java
package com.lino.mybatis.executor;
import com.lino.mybatis.executor.statement.StatementHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.session.RowBounds;
import com.lino.mybatis.transaction.Transaction;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
/**
* @description: 简单执行器
*/
public class SimpleExecutor extends BaseExecutor {
public SimpleExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
@Override
protected int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 新建一个 StatementHandler
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
// 准备语句
stmt = prepareStatement(handler);
// StatementHandler.update
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
@Override
protected <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 新建一个 StatementHandler
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
// 准备语句
stmt = prepareStatement(handler);
// 返回结果
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler) throws SQLException {
Statement stmt;
Connection connection = transaction.getConnection();
// 准备语句
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
}
BaseExecutor
、SimpleExecutor
、BaseTypeHandler
TypeHandler.java
package com.lino.mybatis.type;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @description: 类型处理器
*/
public interface TypeHandler<T> {
/**
* 设置参数
*
* @param ps 预处理语言
* @param i 次数
* @param parameter 参数对象
* @param jdbcType JDBC类型
* @throws SQLException SQL异常
*/
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
/**
* 获取结果
*
* @param rs 结果列表
* @param columnName 列名
* @return T 结果
* @throws SQLException
*/
T getResult(ResultSet rs, String columnName) throws SQLException;
/**
* 获取结果
*
* @param rs 结果列表
* @param columnIndex 列的索引
* @return T 结果
* @throws SQLException
*/
T getResult(ResultSet rs, int columnIndex) throws SQLException;
}
LongTypeHandler.java
package com.lino.mybatis.type;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @description: Long类型处理器
* @author: lingjian
* @createDate: 2022/11/11 13:59
*/
public class LongTypeHandler extends BaseTypeHandler<Long> {
@Override
protected void setNonNullParameter(PreparedStatement ps, int i, Long parameter, JdbcType jdbcType) throws SQLException {
ps.setLong(i, parameter);
}
@Override
protected Long getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getLong(columnName);
}
@Override
protected Long getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getLong(columnIndex);
}
}
if
判断。所以这里基于 TypeHandler 接口对每个参数类型分别做了自己的策略实现。PooledDataSource\UnpooledDataSource
、BatchExecutor\ResuseExecutor\SimpleExecutor\CachingExecutor
、LongTypeHandler\StringTypeHandler\DateTypeHandler
PropertyTokenizer.java
package com.lino.mybatis.reflection.property;
import java.util.Iterator;
/**
* @description: 属性分解标记
*/
public class PropertyTokenizer implements Iterable<PropertyTokenizer>, Iterator<PropertyTokenizer> {
// 例子:班级[0].学生.成绩
/**
* 属性名称:班级
*/
private String name;
/**
* 属性对象名称:班级[0]
*/
private String indexedName;
/**
* 属性索引:0
*/
private String index;
/**
* 子属性:学生
*/
private String children;
public PropertyTokenizer(String fullName) {
// 班级[0].学生.成绩:找.
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);
}
}
@Override
public Iterator<PropertyTokenizer> iterator() {
return this;
}
@Override
public boolean hasNext() {
return children != null;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Remove is not supported, as it has no meaning in the context of properties.");
}
@Override
public PropertyTokenizer next() {
return new PropertyTokenizer(children);
}
public String getName() {
return name;
}
public String getIndexedName() {
return indexedName;
}
public String getIndex() {
return index;
}
public String getChildren() {
return children;
}
}
PropertyTokenizer