Mybatis源碼解讀-設(shè)計模式總結(jié)

Mybatis源碼解讀-設(shè)計模式總結(jié)

Builder模式,例如SqlSessionFactoryBuilder菇怀、XMLConfigBuilder、XMLMapperBuilder概而、XMLStatementBuilder悯仙、CacheBuilder;

工廠模式想诅,例如SqlSessionFactory召庞、ObjectFactory、MapperProxyFactory来破;

單例模式篮灼,例如ErrorContext和LogFactory;

代理模式徘禁,Mybatis實(shí)現(xiàn)的核心诅诱,比如MapperProxy、ConnectionLogger送朱,用的jdk的動態(tài)代理娘荡;還有executor.loader包使用了cglib或者javassist達(dá)到延遲加載的效果;

組合模式驶沼,例如SqlNode和各個子類ChooseSqlNode等炮沐;

模板方法模式,例如BaseExecutor和SimpleExecutor商乎,還有BaseTypeHandler和所有的子類例如IntegerTypeHandler央拖;

適配器模式祭阀,例如Log的Mybatis接口和它對jdbc鹉戚、log4j等各種日志框架的適配實(shí)現(xiàn);

裝飾者模式专控,例如Cache包中的cache.decorators子包中等各個裝飾者的實(shí)現(xiàn)抹凳;

迭代器模式,例如迭代器模式PropertyTokenizer伦腐;

接下來挨個模式進(jìn)行解讀赢底,先介紹模式自身的知識,然后解讀在Mybatis中怎樣應(yīng)用了該模式柏蘑。

1幸冻、Builder模式

Builder模式的定義是“將一個復(fù)雜對象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示咳焚∏⑺穑”,它屬于創(chuàng)建類模式革半,一般來說碑定,如果一個對象的構(gòu)建比較復(fù)雜流码,超出了構(gòu)造函數(shù)所能包含的范圍,就可以使用工廠模式和Builder模式延刘,相對于工廠模式會產(chǎn)出一個完整的產(chǎn)品漫试,Builder應(yīng)用于更加復(fù)雜的對象的構(gòu)建,甚至只會構(gòu)建產(chǎn)品的一個部分碘赖。

在Mybatis環(huán)境的初始化過程中驾荣,SqlSessionFactoryBuilder會調(diào)用XMLConfigBuilder讀取所有的MybatisMapConfig.xml和所有的*Mapper.xml文件,構(gòu)建Mybatis運(yùn)行的核心對象Configuration對象普泡,然后將該Configuration對象作為參數(shù)構(gòu)建一個SqlSessionFactory對象秘车。

其中XMLConfigBuilder在構(gòu)建Configuration對象時,也會調(diào)用XMLMapperBuilder用于讀取*Mapper文件劫哼,而XMLMapperBuilder會使用XMLStatementBuilder來讀取和build所有的SQL語句叮趴。

在這個過程中,有一個相似的特點(diǎn)权烧,就是這些Builder會讀取文件或者配置眯亦,然后做大量的XpathParser解析、配置或語法的解析般码、反射生成對象妻率、存入結(jié)果緩存等步驟,這么多的工作都不是一個構(gòu)造函數(shù)所能包括的板祝,因此大量采用了Builder模式來解決宫静。

對于builder的具體類,方法都大都用build*開頭券时,比如SqlSessionFactoryBuilder為例孤里,它包含以下方法:

即根據(jù)不同的輸入?yún)?shù)來構(gòu)建SqlSessionFactory這個工廠對象。

2橘洞、工廠模式

在Mybatis中比如SqlSessionFactory使用的是工廠模式捌袜,該工廠沒有那么復(fù)雜的邏輯,是一個簡單工廠模式炸枣。

簡單工廠模式(Simple Factory Pattern):又稱為靜態(tài)工廠方法(Static Factory Method)模式虏等,它屬于類創(chuàng)建型模式。在簡單工廠模式中适肠,可以根據(jù)參數(shù)的不同返回不同類的實(shí)例霍衫。簡單工廠模式專門定義一個類來負(fù)責(zé)創(chuàng)建其他類的實(shí)例,被創(chuàng)建的實(shí)例通常都具有共同的父類侯养。

SqlSession可以認(rèn)為是一個Mybatis工作的核心的接口敦跌,通過這個接口可以執(zhí)行執(zhí)行SQL語句、獲取Mappers沸毁、管理事務(wù)峰髓。類似于連接MySQL的Connection對象傻寂。

可以看到,該Factory的openSession方法重載了很多個携兵,分別支持autoCommit疾掰、Executor、Transaction等參數(shù)的輸入徐紧,來構(gòu)建核心的SqlSession對象静檬。

在DefaultSqlSessionFactory的默認(rèn)工廠實(shí)現(xiàn)里,有一個方法可以看出工廠怎么產(chǎn)出一個產(chǎn)品:

Java

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();

}

}

這是一個openSession調(diào)用的底層方法并级,該方法先從configuration讀取對應(yīng)的環(huán)境配置拂檩,然后初始化TransactionFactory獲得一個Transaction對象,然后通過Transaction獲取一個Executor對象嘲碧,最后通過configuration稻励、Executor、是否autoCommit三個參數(shù)構(gòu)建了SqlSession愈涩。在這里其實(shí)也可以看到端倪望抽,SqlSession的執(zhí)行,其實(shí)是委托給對應(yīng)的Executor來進(jìn)行的履婉。

而對于LogFactory煤篙,它的實(shí)現(xiàn)代碼:

Java

public final?class?LogFactory {

private static Constructorextends?Log> logConstructor;

private LogFactory() {

// disable construction

}

public static Log getLog(Class aClass) {

return?getLog(aClass.getName());

}

這里有個特別的地方,是Log變量的的類型是Constructorextends?Log>毁腿,也就是說該工廠生產(chǎn)的不只是一個產(chǎn)品辑奈,而是具有Log公共接口的一系列產(chǎn)品,比如Log4jImpl已烤、Slf4jImpl等很多具體的Log鸠窗。

3、單例模式

單例模式(Singleton Pattern):單例模式確保某一個類只有一個實(shí)例草戈,而且自行實(shí)例化并向整個系統(tǒng)提供這個實(shí)例塌鸯,這個類稱為單例類侍瑟,它提供全局訪問的方法唐片。

單例模式的要點(diǎn)有三個:一是某個類只能有一個實(shí)例;二是它必須自行創(chuàng)建這個實(shí)例涨颜;三是它必須自行向整個系統(tǒng)提供這個實(shí)例费韭。單例模式是一種對象創(chuàng)建型模式。單例模式又名單件模式或單態(tài)模式庭瑰。

在Mybatis中有兩個地方用到單例模式星持,ErrorContext和LogFactory,其中ErrorContext是用在每個線程范圍內(nèi)的單例弹灭,用于記錄該線程的執(zhí)行環(huán)境錯誤信息督暂,而LogFactory則是提供給整個Mybatis使用的日志工廠揪垄,用于獲得針對項(xiàng)目配置好的日志對象。

ErrorContext的單例實(shí)現(xiàn)代碼:

Java

public?class?ErrorContext {

private static final ThreadLocal LOCAL =?newThreadLocal();

private ErrorContext() {

}

public static ErrorContext instance() {

ErrorContext context = LOCAL.get();

if?(context ==?null) {

context =?new?ErrorContext();

LOCAL.set(context);

}

return?context;

}

構(gòu)造函數(shù)是private修飾逻翁,具有一個static的局部instance變量和一個獲取instance變量的方法饥努,在獲取實(shí)例的方法中,先判斷是否為空如果是的話就先創(chuàng)建八回,然后返回構(gòu)造好的對象酷愧。

只是這里有個有趣的地方是,LOCAL的靜態(tài)實(shí)例變量使用了ThreadLocal修飾缠诅,也就是說它屬于每個線程各自的數(shù)據(jù)溶浴,而在instance()方法中,先獲取本線程的該實(shí)例管引,如果沒有就創(chuàng)建該線程獨(dú)有的ErrorContext士败。

4、代理模式

代理模式可以認(rèn)為是Mybatis的核心使用的模式褥伴,正是由于這個模式拱烁,我們只需要編寫Mapper.java接口,不需要實(shí)現(xiàn)噩翠,由Mybatis后臺幫我們完成具體SQL的執(zhí)行戏自。

代理模式(Proxy Pattern) :給某一個對象提供一個代 理,并由代理對象控制對原對象的引用伤锚。代理模式的英 文叫做Proxy或Surrogate擅笔,它是一種對象結(jié)構(gòu)型模式。

代理模式包含如下角色:

Subject: 抽象主題角色

Proxy: 代理主題角色

RealSubject: 真實(shí)主題角色

這里有兩個步驟屯援,第一個是提前創(chuàng)建一個Proxy猛们,第二個是使用的時候會自動請求Proxy,然后由Proxy來執(zhí)行具體事務(wù)狞洋;

當(dāng)我們使用Configuration的getMapper方法時弯淘,會調(diào)用mapperRegistry.getMapper方法,而該方法又會調(diào)用mapperProxyFactory.newInstance(sqlSession)來生成一個具體的代理:

Java

/**

* @author Lasse Voss

*/

public?class?MapperProxyFactory {

private final?Class mapperInterface;

private final Map methodCache =?newConcurrentHashMap();

public MapperProxyFactory(Class mapperInterface) {

this.mapperInterface = mapperInterface;

}

public?Class getMapperInterface() {

return?mapperInterface;

}

public Map<Method, MapperMethod> getMethodCache() {

return?methodCache;

}

@SuppressWarnings("unchecked")

protected T newInstance(MapperProxy<T> mapperProxy) {

return?(T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),?new?Class[] { mapperInterface },

mapperProxy);

}

public T newInstance(SqlSession sqlSession) {

final MapperProxy mapperProxy =?new?MapperProxy(sqlSession, mapperInterface, methodCache);

return?newInstance(mapperProxy);

}

}

在這里吉懊,先通過T newInstance(SqlSession sqlSession)方法會得到一個MapperProxy對象庐橙,然后調(diào)用T newInstance(MapperProxy<T> mapperProxy)生成代理對象然后返回。

而查看MapperProxy的代碼借嗽,可以看到如下內(nèi)容:

Java

public?class?MapperProxy?implements?InvocationHandler, Serializable {

@Override

public?Object?invoke(Object?proxy, Method method,?Object[] args) throws Throwable {

try?{

if?(Object.class.equals(method.getDeclaringClass())) {

return?method.invoke(this, args);

}?else?if?(isDefaultMethod(method)) {

return?invokeDefaultMethod(proxy, method, args);

}

}?catch?(Throwable t) {

throw?ExceptionUtil.unwrapThrowable(t);

}

final MapperMethod mapperMethod = cachedMapperMethod(method);

return?mapperMethod.execute(sqlSession, args);

}

非常典型的态鳖,該MapperProxy類實(shí)現(xiàn)了InvocationHandler接口,并且實(shí)現(xiàn)了該接口的invoke方法恶导。

通過這種方式浆竭,我們只需要編寫Mapper.java接口類,當(dāng)真正執(zhí)行一個Mapper接口的時候,就會轉(zhuǎn)發(fā)給MapperProxy.invoke方法邦泄,而該方法則會調(diào)用后續(xù)的sqlSession.cud>executor.execute>prepareStatement等一系列方法删窒,完成SQL的執(zhí)行和返回。

5顺囊、組合模式

組合模式組合多個對象形成樹形結(jié)構(gòu)以表示“整體-部分”的結(jié)構(gòu)層次易稠。

組合模式對單個對象(葉子對象)和組合對象(組合對象)具有一致性,它將對象組織到樹結(jié)構(gòu)中包蓝,可以用來描述整體與部分的關(guān)系驶社。同時它也模糊了簡單元素(葉子對象)和復(fù)雜元素(容器對象)的概念,使得客戶能夠像處理簡單元素一樣來處理復(fù)雜元素测萎,從而使客戶程序能夠與復(fù)雜元素的內(nèi)部結(jié)構(gòu)解耦亡电。

在使用組合模式中需要注意一點(diǎn)也是組合模式最關(guān)鍵的地方:葉子對象和組合對象實(shí)現(xiàn)相同的接口。這就是組合模式能夠?qū)⑷~子節(jié)點(diǎn)和對象節(jié)點(diǎn)進(jìn)行一致處理的原因硅瞧。

Mybatis支持動態(tài)SQL的強(qiáng)大功能份乒,比如下面的這個SQL:

XHTML

id="update" parameterType="org.format.dynamicproxy.mybatis.bean.User">

UPDATE users

prefix="SET" prefixOverrides=",">

test="name != null and name != ''">

name = #{name}

</if>

test="age != null and age != ''">

, age = #{age}

</if>

test="birthday != null and birthday != ''">

, birthday = #{birthday}

</if>

</trim>

where id = ${id}

</update>

在這里面使用到了trim、if等動態(tài)元素腕唧,可以根據(jù)條件來生成不同情況下的SQL或辖;

在DynamicSqlSource.getBoundSql方法里,調(diào)用了rootSqlNode.apply(context)方法枣接,apply方法是所有的動態(tài)節(jié)點(diǎn)都實(shí)現(xiàn)的接口:

Java

public?interface?SqlNode {

boolean?apply(DynamicContext context);

}

對于實(shí)現(xiàn)該SqlSource接口的所有節(jié)點(diǎn)颂暇,就是整個組合模式樹的各個節(jié)點(diǎn):

組合模式的簡單之處在于,所有的子節(jié)點(diǎn)都是同一類節(jié)點(diǎn)但惶,可以遞歸的向下執(zhí)行耳鸯,比如對于TextSqlNode,因?yàn)樗亲畹讓拥娜~子節(jié)點(diǎn)膀曾,所以直接將對應(yīng)的內(nèi)容append到SQL語句中:

Java

@Override

public?boolean?apply(DynamicContext context) {

GenericTokenParser parser = createParser(new?BindingTokenParser(context, injectionFilter));

context.appendSql(parser.parse(text));

return?true;

}

但是對于IfSqlNode县爬,就需要先做判斷,如果判斷通過添谊,仍然會調(diào)用子元素的SqlNode财喳,即contents.apply方法,實(shí)現(xiàn)遞歸的解析斩狱。

Java

@Override

public?boolean?apply(DynamicContext context) {

if?(evaluator.evaluateBoolean(test, context.getBindings())) {

contents.apply(context);

return?true;

}

return?false;

}

6耳高、模板方法模式

模板方法模式是所有模式中最為常見的幾個模式之一,是基于繼承的代碼復(fù)用的基本技術(shù)喊废。

模板方法模式需要開發(fā)抽象類和具體子類的設(shè)計師之間的協(xié)作祝高。一個設(shè)計師負(fù)責(zé)給出一個算法的輪廓和骨架,另一些設(shè)計師則負(fù)責(zé)給出這個算法的各個邏輯步驟污筷。代表這些具體邏輯步驟的方法稱做基本方法(primitive method);而將這些基本方法匯總起來的方法叫做模板方法(template method),這個設(shè)計模式的名字就是從此而來瓣蛀。

模板類定義一個操作中的算法的骨架陆蟆,而將一些步驟延遲到子類中。使得子類可以不改變一個算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟惋增。

在Mybatis中叠殷,sqlSession的SQL執(zhí)行,都是委托給Executor實(shí)現(xiàn)的诈皿,Executor包含以下結(jié)構(gòu):

其中的BaseExecutor就采用了模板方法模式林束,它實(shí)現(xiàn)了大部分的SQL執(zhí)行邏輯,然后把以下幾個方法交給子類定制化完成:

Java

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

protected abstract List doFlushStatements(boolean?isRollback) throws SQLException;

protected abstract List doQuery(MappedStatement ms,?Object?parameter, RowBounds rowBounds,

ResultHandler resultHandler, BoundSql boundSql) throws SQLException;

該模板方法類有幾個子類的具體實(shí)現(xiàn)稽亏,使用了不同的策略:

簡單SimpleExecutor:每執(zhí)行一次update或select壶冒,就開啟一個Statement對象,用完立刻關(guān)閉Statement對象截歉。(可以是Statement或PrepareStatement對象)

重用ReuseExecutor:執(zhí)行update或select胖腾,以sql作為key查找Statement對象,存在就使用瘪松,不存在就創(chuàng)建咸作,用完后,不關(guān)閉Statement對象宵睦,而是放置于Map<String, Statement>內(nèi)记罚,供下一次使用。(可以是Statement或PrepareStatement對象)

批量BatchExecutor:執(zhí)行update(沒有select壳嚎,JDBC批處理不支持select)毫胜,將所有sql都添加到批處理中(addBatch()),等待統(tǒng)一執(zhí)行(executeBatch())诬辈,它緩存了多個Statement對象酵使,每個Statement對象都是addBatch()完畢后,等待逐一執(zhí)行executeBatch()批處理的焙糟;BatchExecutor相當(dāng)于維護(hù)了多個桶口渔,每個桶里都裝了很多屬于自己的SQL,就像蘋果藍(lán)里裝了很多蘋果穿撮,番茄藍(lán)里裝了很多番茄缺脉,最后,再統(tǒng)一倒進(jìn)倉庫悦穿。(可以是Statement或PrepareStatement對象)

比如在SimpleExecutor中這樣實(shí)現(xiàn)update方法:

Java

@Override

public?int?doUpdate(MappedStatement ms,?Object?parameter) throws SQLException {

Statement stmt =?null;

try?{

Configuration configuration = ms.getConfiguration();

StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT,?null,

null);

stmt = prepareStatement(handler, ms.getStatementLog());

return?handler.update(stmt);

}?finally?{

closeStatement(stmt);

}

}

7攻礼、適配器模式

適配器模式(Adapter Pattern) :將一個接口轉(zhuǎn)換成客戶希望的另一個接口,適配器模式使接口不兼容的那些類可以一起工作栗柒,其別名為包裝器(Wrapper)礁扮。適配器模式既可以作為類結(jié)構(gòu)型模式,也可以作為對象結(jié)構(gòu)型模式。

在Mybatsi的logging包中太伊,有一個Log接口:

Java

/**

* @author Clinton Begin

*/

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);

}

該接口定義了Mybatis直接使用的日志方法雇锡,而Log接口具體由誰來實(shí)現(xiàn)呢?Mybatis提供了多種日志框架的實(shí)現(xiàn)僚焦,這些實(shí)現(xiàn)都匹配這個Log接口所定義的接口方法锰提,最終實(shí)現(xiàn)了所有外部日志框架到Mybatis日志包的適配:

比如對于Log4jImpl的實(shí)現(xiàn)來說,該實(shí)現(xiàn)持有了org.apache.log4j.Logger的實(shí)例芳悲,然后所有的日志方法立肘,均委托該實(shí)例來實(shí)現(xiàn)。

Java

public?class?Log4jImpl?implements?Log {

private static final?String?FQCN = Log4jImpl.class.getName();

private Logger log;

public Log4jImpl(String?clazz) {

log = Logger.getLogger(clazz);

}

@Override

public?boolean?isDebugEnabled() {

return?log.isDebugEnabled();

}

@Override

public?boolean?isTraceEnabled() {

return?log.isTraceEnabled();

}

@Override

public?void?error(String?s, Throwable e) {

log.log(FQCN, Level.ERROR, s, e);

}

@Override

public?void?error(String?s) {

log.log(FQCN, Level.ERROR, s,?null);

}

@Override

public?void?debug(String?s) {

log.log(FQCN, Level.DEBUG, s,?null);

}

@Override

public?void?trace(String?s) {

log.log(FQCN, Level.TRACE, s,?null);

}

@Override

public?void?warn(String?s) {

log.log(FQCN, Level.WARN, s,?null);

}

}

8名扛、裝飾者模式

裝飾模式(Decorator Pattern) :動態(tài)地給一個對象增加一些額外的職責(zé)(Responsibility)谅年,就增加對象功能來說,裝飾模式比生成子類實(shí)現(xiàn)更為靈活罢洲。其別名也可以稱為包裝器(Wrapper)踢故,與適配器模式的別名相同,但它們適用于不同的場合惹苗。根據(jù)翻譯的不同殿较,裝飾模式也有人稱之為“油漆工模式”,它是一種對象結(jié)構(gòu)型模式桩蓉。

在mybatis中淋纲,緩存的功能由根接口Cache(org.apache.ibatis.cache.Cache)定義。整個體系采用裝飾器設(shè)計模式院究,數(shù)據(jù)存儲和緩存的基本功能由PerpetualCache(org.apache.ibatis.cache.impl.PerpetualCache)永久緩存實(shí)現(xiàn)洽瞬,然后通過一系列的裝飾器來對PerpetualCache永久緩存進(jìn)行緩存策略等方便的控制。如下圖:

用于裝飾PerpetualCache的標(biāo)準(zhǔn)裝飾器共有8個(全部在org.apache.ibatis.cache.decorators包中):

FifoCache:先進(jìn)先出算法业汰,緩存回收策略

LoggingCache:輸出緩存命中的日志信息

LruCache:最近最少使用算法伙窃,緩存回收策略

ScheduledCache:調(diào)度緩存,負(fù)責(zé)定時清空緩存

SerializedCache:緩存序列化和反序列化存儲

SoftCache:基于軟引用實(shí)現(xiàn)的緩存管理策略

SynchronizedCache:同步的緩存裝飾器样漆,用于防止多線程并發(fā)訪問

WeakCache:基于弱引用實(shí)現(xiàn)的緩存管理策略

另外为障,還有一個特殊的裝飾器TransactionalCache:事務(wù)性的緩存

正如大多數(shù)持久層框架一樣,mybatis緩存同樣分為一級緩存和二級緩存

一級緩存放祟,又叫本地緩存鳍怨,是PerpetualCache類型的永久緩存,保存在執(zhí)行器中(BaseExecutor)跪妥,而執(zhí)行器又在SqlSession(DefaultSqlSession)中鞋喇,所以一級緩存的生命周期與SqlSession是相同的。

二級緩存眉撵,又叫自定義緩存侦香,實(shí)現(xiàn)了Cache接口的類都可以作為二級緩存落塑,所以可配置如encache等的第三方緩存。二級緩存以namespace名稱空間為其唯一標(biāo)識鄙皇,被保存在Configuration核心配置對象中芜赌。

二級緩存對象的默認(rèn)類型為PerpetualCache仰挣,如果配置的緩存是默認(rèn)類型伴逸,則mybatis會根據(jù)配置自動追加一系列裝飾器。

Cache對象之間的引用順序?yàn)椋?/p>

SynchronizedCache–>LoggingCache–>SerializedCache–>ScheduledCache–>LruCache–>PerpetualCache

9膘壶、迭代器模式

迭代器(Iterator)模式错蝴,又叫做游標(biāo)(Cursor)模式。GOF給出的定義為:提供一種方法訪問一個容器(container)對象中各個元素颓芭,而又不需暴露該對象的內(nèi)部細(xì)節(jié)顷锰。

Java的Iterator就是迭代器模式的接口,只要實(shí)現(xiàn)了該接口亡问,就相當(dāng)于應(yīng)用了迭代器模式:

比如Mybatis的PropertyTokenizer是property包中的重量級類官紫,該類會被reflection包中其他的類頻繁的引用到。這個類實(shí)現(xiàn)了Iterator接口州藕,在使用時經(jīng)常被用到的是Iterator接口中的hasNext這個函數(shù)束世。

Java

public?class?PropertyTokenizer?implements?Iterator {

private?String?name;

private?String?indexedName;

private?String?index;

private?String?children;

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.");

}

}

可以看到晓勇,這個類傳入一個字符串到構(gòu)造函數(shù)反镇,然后提供了iterator方法對解析后的子串進(jìn)行遍歷,是一個很常用的方法類屡萤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末锈死,一起剝皮案震驚了整個濱河市贫堰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌待牵,老刑警劉巖其屏,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異缨该,居然都是意外死亡偎行,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門压彭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來睦优,“玉大人,你說我怎么就攤上這事壮不『古蹋” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵询一,是天一觀的道長隐孽。 經(jīng)常有香客問我癌椿,道長,這世上最難降的妖魔是什么菱阵? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任踢俄,我火速辦了婚禮,結(jié)果婚禮上晴及,老公的妹妹穿的比我還像新娘都办。我一直安慰自己,他們只是感情好虑稼,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布琳钉。 她就那樣靜靜地躺著,像睡著了一般蛛倦。 火紅的嫁衣襯著肌膚如雪歌懒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天溯壶,我揣著相機(jī)與錄音及皂,去河邊找鬼。 笑死且改,一個胖子當(dāng)著我的面吹牛验烧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播钾虐,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼噪窘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了效扫?” 一聲冷哼從身側(cè)響起倔监,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎菌仁,沒想到半個月后浩习,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡济丘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年谱秽,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片摹迷。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡疟赊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出峡碉,到底是詐尸還是另有隱情近哟,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布鲫寄,位于F島的核電站吉执,受9級特大地震影響疯淫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜戳玫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一熙掺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咕宿,春花似錦币绩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽载城。三九已至肌似,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诉瓦,已是汗流浹背川队。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留睬澡,地道東北人固额。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像煞聪,于是被迫代替她去往敵國和親斗躏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容