1.建造者模式
Builder Pattern使用多個(gè)簡單的對象一步一步構(gòu)建成一個(gè)復(fù)雜的對象。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。
- Builder:給出一個(gè)抽象接口,以規(guī)范產(chǎn)品對象的各個(gè)組成成分的建造。這個(gè)接口規(guī)定要實(shí)現(xiàn)復(fù)雜對象的哪些部分的創(chuàng)建输瓜,并不涉及具體的對象部件的創(chuàng)建;
ConcreteBuilder:實(shí)現(xiàn)Builder接口芬萍,針對不同的商業(yè)邏輯尤揣,具體化復(fù)雜對象的各部分的創(chuàng)建。在建造過程完成后柬祠,提供產(chǎn)品的實(shí)例北戏;
Director:調(diào)用具體建造者來創(chuàng)建復(fù)雜對象的各個(gè)部分,在指導(dǎo)者中不涉及具體產(chǎn)品的信息漫蛔,只負(fù)責(zé)保證對象各部分完整創(chuàng)建或按某種順序創(chuàng)建嗜愈;
Product:要?jiǎng)?chuàng)建的復(fù)雜對象。
建造者模式使用場景:
- 需要生成的對象具有復(fù)雜的內(nèi)部結(jié)構(gòu)莽龟,實(shí)例化對象時(shí)要屏蔽掉對象內(nèi)部細(xì)節(jié)蠕嫁,讓上層代碼與復(fù)雜對象的實(shí)例化過程解耦,可以使用建造者模式毯盈。簡而言之剃毒,如果遇到多個(gè)構(gòu)造器參數(shù)時(shí)要考慮用建造者模式。
- 一個(gè)對象的實(shí)例化是依賴各個(gè)組件的產(chǎn)生以及裝配順序,關(guān)注的是一步一步地組裝出目標(biāo)對象赘阀,可以使用建造者模式益缠。
與工廠模式區(qū)別:
- 對象復(fù)雜度
建造者建造的對象更加負(fù)責(zé),是一個(gè)復(fù)合產(chǎn)品基公,它由各個(gè)部件復(fù)合而成幅慌,部件不同產(chǎn)品對象不同,生成的產(chǎn)品粒度細(xì)酌媒;
在工廠方法模式里欠痴,關(guān)注的是一個(gè)產(chǎn)品整體迄靠,無須關(guān)心產(chǎn)品的各部分是如何創(chuàng)建出來的秒咨。 - 客戶端參與程度
建造者模式,導(dǎo)演對象參與了產(chǎn)品的創(chuàng)建掌挚,決定了產(chǎn)品的類型和內(nèi)容雨席,參與度高,適合實(shí)例化對象時(shí)屬性變化頻繁的場景吠式;
工廠模式陡厘,客戶端對產(chǎn)品的創(chuàng)建過程參與度低,對象實(shí)例化時(shí)屬性值相對比較固定特占。
2.建造者模式實(shí)例
2.1 Builder抽象接口
public interface RedPacketBuilder {
RedPacketBuilder setPublisherName(String publishName);
RedPacketBuilder setAcceptName(String acceptName);
RedPacketBuilder setPacketAmount(BigDecimal packetAmount);
RedPacketBuilder setPacketType(int packetType);
RedPacketBuilder setPulishPacketTime(Date pushlishPacketTime);
RedPacketBuilder setOpenPacketTime(Date openPacketTime);
RedPacket build();
}
2.2 實(shí)際的Builder
注意點(diǎn):
1)RedPacketBuilderImpl包含RedPacket的所有域
2)set方法設(shè)置一個(gè)域糙置,并且返回this,這是流式編程的關(guān)鍵
3)最后build調(diào)用一個(gè)完整包含所有域的RedPacket構(gòu)造函數(shù)
public class RedPacketBuilderImpl implements RedPacketBuilder {
private String publisherName;
private String acceptName;
private BigDecimal packetAmount;
private int packetType;
private Date pulishPacketTime;
private Date openPacketTime;
public static RedPacketBuilderImpl getBulider(){
return new RedPacketBuilderImpl();
}
@Override
public RedPacketBuilder setPublisherName(String publishName) {
this.publisherName = publishName;
return this;
}
@Override
public RedPacketBuilder setAcceptName(String acceptName) {
this.acceptName = acceptName;
return this;
}
@Override
public RedPacketBuilder setPacketAmount(BigDecimal packetAmount) {
this.packetAmount = packetAmount;
return this;
}
@Override
public RedPacketBuilder setPacketType(int packetType) {
this.packetType = packetType;
return this;
}
@Override
public RedPacketBuilder setPulishPacketTime(Date pushlishPacketTime) {
this.pulishPacketTime = pushlishPacketTime;
return this;
}
@Override
public RedPacketBuilder setOpenPacketTime(Date openPacketTime) {
this.openPacketTime = openPacketTime;
return this;
}
public RedPacket build() {
return new RedPacket(publisherName,acceptName,packetAmount,packetType,pulishPacketTime,openPacketTime);
}
}
2.3 具體的對象類
public class RedPacket {
private String publisherName; //發(fā)包人
private String acceptName; //手包人
private BigDecimal packetAmount; //紅包金額
private int packetType; //紅包類型
private Date pulishPacketTime; //發(fā)包時(shí)間
private Date openPacketTime; //搶包時(shí)間
public RedPacket(String publisherName, String acceptName, BigDecimal packetAmount, int packetType, Date pulishPacketTime, Date openPacketTime) {
this.publisherName = publisherName;
this.acceptName = acceptName;
this.packetAmount = packetAmount;
this.packetType = packetType;
this.pulishPacketTime = pulishPacketTime;
this.openPacketTime = openPacketTime;
}
public String getPublisherName() {
return publisherName;
}
public void setPublisherName(String publisherName) {
this.publisherName = publisherName;
}
public String getAcceptName() {
return acceptName;
}
public void setAcceptName(String acceptName) {
this.acceptName = acceptName;
}
public BigDecimal getPacketAmount() {
return packetAmount;
}
public void setPacketAmount(BigDecimal packetAmount) {
this.packetAmount = packetAmount;
}
public int getPacketType() {
return packetType;
}
public void setPacketType(int packetType) {
this.packetType = packetType;
}
public Date getPulishPacketTime() {
return pulishPacketTime;
}
public void setPulishPacketTime(Date pulishPacketTime) {
this.pulishPacketTime = pulishPacketTime;
}
public Date getOpenPacketTime() {
return openPacketTime;
}
public void setOpenPacketTime(Date openPacketTime) {
this.openPacketTime = openPacketTime;
}
@Override
public String toString() {
return "RedPacket [publisherName=" + publisherName + ", acceptName="
+ acceptName + ", packetAmount=" + packetAmount
+ ", packetType=" + packetType + ", pulishPacketTime="
+ pulishPacketTime + ", openPacketTime=" + openPacketTime + "]";
}
}
2.4 導(dǎo)演
public class Director {
public static void main(String[] args) {
RedPacket redPacket = RedPacketBuilderImpl.getBulider().setPublisherName("lison")
.setAcceptName("vip群")
.setPacketAmount(new BigDecimal("888"))
.setPacketType(1)
.setOpenPacketTime(new Date())
.setPulishPacketTime(new Date()).build();
System.out.println(redPacket);
}
}
結(jié)果:
RedPacket [publisherName=lison, acceptName=vip群, packetAmount=888, packetType=1, pulishPacketTime=Thu Sep 27 16:44:44 CST 2018, openPacketTime=Thu Sep 27 16:44:44 CST 2018]
3.MyBatis的初始化
- XMLConfigBuilder:主要負(fù)責(zé)解釋mybatis-config.xml
XMLMapperBuilder:負(fù)責(zé)解析映射配置文件
XMLStatementBuilder:負(fù)責(zé)解析映射配置文件中的SQL結(jié)點(diǎn)
3.1 映射器的關(guān)鍵類
- Configuration:Mybatis啟動(dòng)初始化的核心就是將所有xml配置文件信息加載到Configuration對象中是目,Configuration是單例的谤饭,生命周期是應(yīng)用級的
- MapperRegistry:mapper接口動(dòng)態(tài)代理工廠類的注冊中心。在Mybatis中懊纳,通過mapperProxy實(shí)現(xiàn)InvocationHandler接口揉抵,MapperProxyFactory用于生成動(dòng)態(tài)代理的實(shí)例對象
- ResultMap:用于解析mapper.xml文件中的resultMap節(jié)點(diǎn),使用ResultMapping來封裝id嗤疯、result等子元素
- MappedStatement:用于存儲mapper.xml文件中的select冤今、insert、update和delete節(jié)點(diǎn)茂缚,同時(shí)還包含了這些節(jié)點(diǎn)很多重要屬性
- SqlSource:mapper.xml文件中sql語句會被解析成SqlSource對象戏罢,經(jīng)過解析SqlSource包含的語句最終僅僅包含?占位符,可以直接交給數(shù)據(jù)庫執(zhí)行脚囊。
3.2 Configuration的域?qū)?yīng)mybatis-config.xml中相應(yīng)的配置項(xiàng)
public class Configuration {
protected Environment environment;
/* 是否啟用數(shù)據(jù)組A_column自動(dòng)映射到Java類中的駝峰命名的屬性**/
protected boolean mapUnderscoreToCamelCase;
/*當(dāng)對象使用延遲加載時(shí) 屬性的加載取決于能被引用到的那些延遲屬性,否則,按需加載(需要的是時(shí)候才去加載)**/
protected boolean aggressiveLazyLoading;
/*是否允許單條sql 返回多個(gè)數(shù)據(jù)集 (取決于驅(qū)動(dòng)的兼容性) default:true **/
protected boolean multipleResultSetsEnabled = true;
/*-允許JDBC 生成主鍵龟糕。需要驅(qū)動(dòng)器支持。如果設(shè)為了true凑术,這個(gè)設(shè)置將強(qiáng)制使用被生成的主鍵翩蘸,有一些驅(qū)動(dòng)器不兼容不過仍然可以執(zhí)行。 default:false**/
protected boolean useGeneratedKeys;
/*配置全局性的cache開關(guān)淮逊,默認(rèn)為true**/
protected boolean cacheEnabled = true;
/*指定 MyBatis 應(yīng)如何自動(dòng)映射列到字段或?qū)傩?/
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
/*MyBatis每次創(chuàng)建結(jié)果對象的新實(shí)例時(shí)催首,它都會使用對象工廠(ObjectFactory)去構(gòu)建POJO*/
protected ObjectFactory objectFactory = new DefaultObjectFactory();
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
/*延遲加載的全局開關(guān)*/
protected boolean lazyLoadingEnabled = false;
/*指定 Mybatis 創(chuàng)建具有延遲加載能力的對象所用到的代理工具*/
protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
/*插件集合*/
protected final InterceptorChain interceptorChain = new InterceptorChain();
/*TypeHandler注冊中心*/
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
/*TypeAlias注冊中心*/
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
//-------------------------------------------------------------
/*mapper接口的動(dòng)態(tài)代理注冊中心*/
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
/*mapper文件中增刪改查操作的注冊中心*/
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<>("Mapped Statements collection");
/*mapper文件中配置的所有resultMap對象 key為命名空間+ID*/
protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
/*加載到的所有*mapper.xml文件*/
protected final Set<String> loadedResources = new HashSet<>();
3.3 XMLConfigBuilder解析mybatis-config.xml扶踊,將解析出的相關(guān)的值加入到Configuration對象中
@Before
public void init() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 1.讀取mybatis配置文件創(chuàng)SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
inputStream.close();
}
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
具體的解析方法:
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
//解析<properties>節(jié)點(diǎn)
propertiesElement(root.evalNode("properties"));
//解析<settings>節(jié)點(diǎn)
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
//解析<typeAliases>節(jié)點(diǎn)
typeAliasesElement(root.evalNode("typeAliases"));
//解析<plugins>節(jié)點(diǎn)
pluginElement(root.evalNode("plugins"));
//解析<objectFactory>節(jié)點(diǎn)
objectFactoryElement(root.evalNode("objectFactory"));
//解析<objectWrapperFactory>節(jié)點(diǎn)
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//解析<reflectorFactory>節(jié)點(diǎn)
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);//將settings填充到configuration
// read it after objectFactory and objectWrapperFactory issue #631
//解析<environments>節(jié)點(diǎn)
environmentsElement(root.evalNode("environments"));
//解析<databaseIdProvider>節(jié)點(diǎn)
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//解析<typeHandlers>節(jié)點(diǎn)
typeHandlerElement(root.evalNode("typeHandlers"));
//解析<mappers>節(jié)點(diǎn)
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
3.4 XMLMapperBuilder解析mapper.xml映射文件
<!-- 映射文件,mapper的配置文件 -->
<mappers>
<!--直接映射到相應(yīng)的mapper文件 -->
<mapper resource="sqlmapper/TUserMapper.xml" />
<mapper resource="sqlmapper/TJobHistoryMapper.xml" />
<mapper resource="sqlmapper/TPositionMapper.xml" />
<mapper resource="sqlmapper/THealthReportFemaleMapper.xml" />
<mapper resource="sqlmapper/THealthReportMaleMapper.xml" />
<mapper class="com.enjoylearning.mybatis.mapper.TJobHistoryAnnoMapper"/>
</mappers>
解析方法:
//解析<mappers>節(jié)點(diǎn)
mapperElement(root.evalNode("mappers"));
XMLMapperBuilder解析mapper映射文件:
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {//處理mapper子節(jié)點(diǎn)
if ("package".equals(child.getName())) {//package子節(jié)點(diǎn)
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {//獲取<mapper>節(jié)點(diǎn)的resource郎任、url或mClass屬性這三個(gè)屬性互斥
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {//如果resource不為空
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);//加載mapper文件
//實(shí)例化XMLMapperBuilder解析mapper映射文件
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {//如果url不為空
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);//加載mapper文件
//實(shí)例化XMLMapperBuilder解析mapper映射文件
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {//如果class不為空
Class<?> mapperInterface = Resources.classForName(mapperClass);//加載class對象
configuration.addMapper(mapperInterface);//向代理中心注冊mapper
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
public void parse() {
//判斷是否已經(jīng)加載該配置文件
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));//處理mapper節(jié)點(diǎn)
configuration.addLoadedResource(resource);//將mapper文件添加到configuration.loadedResources中
bindMapperForNamespace();//注冊mapper接口
}
//處理解析失敗的ResultMap節(jié)點(diǎn)
parsePendingResultMaps();
//處理解析失敗的CacheRef節(jié)點(diǎn)
parsePendingCacheRefs();
//處理解析失敗的Sql語句節(jié)點(diǎn)
parsePendingStatements();
}
private void configurationElement(XNode context) {
try {
//獲取mapper節(jié)點(diǎn)的namespace屬性
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
//設(shè)置builderAssistant的namespace屬性
builderAssistant.setCurrentNamespace(namespace);
//解析cache-ref節(jié)點(diǎn)
cacheRefElement(context.evalNode("cache-ref"));
//重點(diǎn)分析 :解析cache節(jié)點(diǎn)----------------1-------------------
cacheElement(context.evalNode("cache"));
//解析parameterMap節(jié)點(diǎn)(已廢棄)
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//重點(diǎn)分析 :解析resultMap節(jié)點(diǎn)(基于數(shù)據(jù)結(jié)果去理解)----------------2-------------------
resultMapElements(context.evalNodes("/mapper/resultMap"));
//解析sql節(jié)點(diǎn)
sqlElement(context.evalNodes("/mapper/sql"));
//重點(diǎn)分析 :解析select秧耗、insert、update舶治、delete節(jié)點(diǎn) ----------------3-------------------
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
解析mapper.xml文件分井,例如TUserMapper.xml:
<mapper namespace="com.enjoylearning.mybatis.mapper.TUserMapper">
<cache></cache>
<resultMap id="BaseResultMap" type="TUser">
<!-- <constructor> <idArg column="id" javaType="int"/> <arg column="user_name"
javaType="String"/> </constructor> -->
<id column="id" property="id" />
<result column="user_name" property="userName" />
<result column="real_name" property="realName" />
<result column="sex" property="sex" />
<result column="mobile" property="mobile" />
<result column="email" property="email" />
<result column="note" property="note" />
</resultMap>
<resultMap id="userAndPosition1" extends="BaseResultMap" type="TUser">
<association property="position" javaType="TPosition" columnPrefix="post_">
<id column="id" property="id"/>
<result column="name" property="postName"/>
<result column="note" property="note"/>
</association>
</resultMap>
<resultMap id="userAndPosition2" extends="BaseResultMap" type="TUser">
<!--<association property="position" column="position_id" select="com.enjoylearning.mybatis.mapper.TPositionMapper.selectByPrimaryKey" />-->
<association property="position" fetchType="lazy" column="position_id" select="com.enjoylearning.mybatis.mapper.TPositionMapper.selectByPrimaryKey" />
</resultMap>
<select id="selectUserPosition1" resultMap="userAndPosition1">
select
a.id,
user_name,
real_name,
sex,
mobile,
email,
a.note,
b.id post_id,
b.post_name,
b.note post_note
from t_user a,
t_position b
where a.position_id = b.id
</select>
3.4.1 解析緩存節(jié)點(diǎn)cache
//重點(diǎn)分析 :解析cache節(jié)點(diǎn)----------------1-------------------
cacheElement(context.evalNode("cache"));
private void cacheElement(XNode context) throws Exception {
if (context != null) {
//獲取cache節(jié)點(diǎn)的type屬性,默認(rèn)為PERPETUAL
String type = context.getStringAttribute("type", "PERPETUAL");
//找到type對應(yīng)的cache接口的實(shí)現(xiàn)
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
//讀取eviction屬性霉猛,既緩存的淘汰策略尺锚,默認(rèn)LRU
String eviction = context.getStringAttribute("eviction", "LRU");
//根據(jù)eviction屬性,找到裝飾器
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
//讀取flushInterval屬性惜浅,既緩存的刷新周期
Long flushInterval = context.getLongAttribute("flushInterval");
//讀取size屬性瘫辩,既緩存的容量大小
Integer size = context.getIntAttribute("size");
//讀取readOnly屬性,既緩存的是否只讀
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
//讀取blocking屬性坛悉,既緩存的是否阻塞
boolean blocking = context.getBooleanAttribute("blocking", false);
Properties props = context.getChildrenAsProperties();
//通過builderAssistant創(chuàng)建緩存對象伐厌,并添加至configuration
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
}
}
//通過builderAssistant創(chuàng)建緩存對象,并添加至configuration
public Cache useNewCache(Class<? extends Cache> typeClass,
Class<? extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
boolean blocking,
Properties props) {
//經(jīng)典的建造起模式裸影,創(chuàng)建一個(gè)cache對象
Cache cache = new CacheBuilder(currentNamespace)
.implementation(valueOrDefault(typeClass, PerpetualCache.class))
.addDecorator(valueOrDefault(evictionClass, LruCache.class))
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
//將緩存添加至configuration挣轨,注意二級緩存以命名空間為單位進(jìn)行劃分
configuration.addCache(cache);
currentCache = cache;
return cache;
}
3.4.2 解析resultMap(整體思路都是一樣的)
//重點(diǎn)分析 :解析resultMap節(jié)點(diǎn)(基于數(shù)據(jù)結(jié)果去理解)----------------2-------------------
resultMapElements(context.evalNodes("/mapper/resultMap"));
//解析resultMap節(jié)點(diǎn),實(shí)際就是解析sql查詢的字段與pojo屬性之間的轉(zhuǎn)化規(guī)則
private void resultMapElements(List<XNode> list) throws Exception {
//遍歷所有的resultmap節(jié)點(diǎn)
for (XNode resultMapNode : list) {
try {
//解析具體某一個(gè)resultMap節(jié)點(diǎn)
resultMapElement(resultMapNode);
} catch (IncompleteElementException e) {
// ignore, it will be retried
}
}
}
用來存儲resultMap節(jié)點(diǎn)的ResultMap對象:
public class Configuration {
/*mapper文件中配置的所有resultMap對象 key為命名空間+ID*/
protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
public class ResultMap {
private Configuration configuration;//configuration對象
private String id;//resultMap的id屬性
private Class<?> type;//resultMap的type屬性
private List<ResultMapping> resultMappings;//除discriminator節(jié)點(diǎn)之外的映射關(guān)系
private List<ResultMapping> idResultMappings;//記錄ID或者<constructor>中idArg的映射關(guān)系
private List<ResultMapping> constructorResultMappings;////記錄<constructor>標(biāo)志的映射關(guān)系
private List<ResultMapping> propertyResultMappings;//記錄非<constructor>標(biāo)志的映射關(guān)系
private Set<String> mappedColumns;//記錄所有有映射關(guān)系的columns字段
private Set<String> mappedProperties;//記錄所有有映射關(guān)系的property字段
private Discriminator discriminator;//鑒別器,對應(yīng)discriminator節(jié)點(diǎn)
private boolean hasNestedResultMaps;//是否有嵌套結(jié)果映射
private boolean hasNestedQueries;////是否有嵌套查詢
private Boolean autoMapping;//是否開啟了自動(dòng)映射
public class ResultMapping {
private Configuration configuration;//引用的configuration對象
private String property;//對應(yīng)節(jié)點(diǎn)的property屬性
private String column;//對應(yīng)節(jié)點(diǎn)的column屬性
private Class<?> javaType;//對應(yīng)節(jié)點(diǎn)的javaType屬性
private JdbcType jdbcType;//對應(yīng)節(jié)點(diǎn)的jdbcType屬性
private TypeHandler<?> typeHandler;//對應(yīng)節(jié)點(diǎn)的typeHandler屬性
private String nestedResultMapId;////對應(yīng)節(jié)點(diǎn)的resultMap屬性,嵌套結(jié)果時(shí)使用
private String nestedQueryId;////對應(yīng)節(jié)點(diǎn)的select屬性,嵌套查詢時(shí)使用
private Set<String> notNullColumns;//對應(yīng)節(jié)點(diǎn)的notNullColumn屬性
private String columnPrefix;//對應(yīng)節(jié)點(diǎn)的columnPrefix屬性
private List<ResultFlag> flags;//標(biāo)志,id 或者 constructor
private List<ResultMapping> composites;
private String resultSet;//對應(yīng)節(jié)點(diǎn)的resultSet屬性
private String foreignColumn;//對應(yīng)節(jié)點(diǎn)的foreignColumn屬性
private boolean lazy;//對應(yīng)節(jié)點(diǎn)的fetchType屬性,是否延遲加載
3.4.3 XMLStatementBuilder解析SQL語句
//重點(diǎn)分析 :解析select轩猩、insert卷扮、update、delete節(jié)點(diǎn) ----------------3-------------------
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
//解析select界轩、insert画饥、update、delete節(jié)點(diǎn)
private void buildStatementFromContext(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
buildStatementFromContext(list, null);
}
//處理所有的sql語句節(jié)點(diǎn)并注冊至configuration對象
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
//創(chuàng)建XMLStatementBuilder 專門用于解析sql語句節(jié)點(diǎn)
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
//解析sql語句節(jié)點(diǎn)
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}
增刪改查sql語句對應(yīng)的對象MappedStatement:
4.Configuration建造過程的總結(jié)
4.1 建造者模式的靈魂
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
//實(shí)例化XMLMapperBuilder解析mapper映射文件
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
XMLConfigBuilder浊猾,XMLMapperBuilder和XMLStatementBuilder整體層次上并沒有采用建造者的流式編程風(fēng)格抖甘,但是采用了建造者模式的靈魂,因?yàn)檫@三個(gè)建造者分別構(gòu)建了Configuration對象的不同部分葫慎。
4.2 MapperBuilderAssistant builderAssistant調(diào)用經(jīng)典的建造者模式
//通過builderAssistant創(chuàng)建緩存對象衔彻,并添加至configuration
public Cache useNewCache(Class<? extends Cache> typeClass,
Class<? extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
boolean blocking,
Properties props) {
//經(jīng)典的建造起模式,創(chuàng)建一個(gè)cache對象
Cache cache = new CacheBuilder(currentNamespace)
.implementation(valueOrDefault(typeClass, PerpetualCache.class))
.addDecorator(valueOrDefault(evictionClass, LruCache.class))
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
//將緩存添加至configuration偷办,注意二級緩存以命名空間為單位進(jìn)行劃分
configuration.addCache(cache);
currentCache = cache;
return cache;
}
經(jīng)典建造者模式CacheBuilder:
public class CacheBuilder {
public Cache build() {
//設(shè)置緩存的主實(shí)現(xiàn)類為PerpetualCache
setDefaultImplementations();
//通過反射實(shí)例化PerpetualCache對象
Cache cache = newBaseCacheInstance(implementation, id);
setCacheProperties(cache);//根據(jù)cache節(jié)點(diǎn)下的<property>信息凑耻,初始化cache
// issue #352, do not apply decorators to custom caches
if (PerpetualCache.class.equals(cache.getClass())) {//如果cache是PerpetualCache的實(shí)現(xiàn)态蒂,則為其添加標(biāo)準(zhǔn)的裝飾器
for (Class<? extends Cache> decorator : decorators) {//為cache對象添加裝飾器配紫,這里主要處理緩存清空策略的裝飾器
cache = newCacheDecoratorInstance(decorator, cache);
setCacheProperties(cache);
}
//通過一些屬性為cache對象添加裝飾器
cache = setStandardDecorators(cache);
} else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
//如果cache不是PerpetualCache的實(shí)現(xiàn)碉就,則為其添加日志的能力
cache = new LoggingCache(cache);
}
return cache;
}
4.3 各個(gè)節(jié)點(diǎn)的構(gòu)造思路
- step1.解析XML節(jié)點(diǎn)的值
- step2.通過MapperBuilder調(diào)用相應(yīng)節(jié)點(diǎn)對象建造者
- step3.加入到Configuration對象中
4.4 源碼分析的核心思路
- step1.搞清楚XML各節(jié)點(diǎn)解析出來的對象類結(jié)構(gòu)
也即XML結(jié)點(diǎn)與相關(guān)類的對應(yīng)關(guān)系 - step2.知道了源點(diǎn)(XML節(jié)點(diǎn))和目標(biāo)(對象類),然后找到從源點(diǎn)到目標(biāo)對象之間調(diào)用的方法,搞清楚方法是怎么建造的即可
參考
- 1)享學(xué)課堂Lison老師筆記