Hello, welcome to xx kitchen equipment co., LTD
语言选择: ∷ 

干货:MyBatis包罗这9种设计模式

Release time:2021-10-12 01:50viewed:times
本文摘要:虽然我们都知道有26个设计模式,可是大多停留在观点层面,真实开发中很少遇到,Mybatis源码中使用了大量的设计模式,阅读源码并视察设计模式在其中的应用,能够更深入的明白设计模式。

LOL全球总决赛下注

虽然我们都知道有26个设计模式,可是大多停留在观点层面,真实开发中很少遇到,Mybatis源码中使用了大量的设计模式,阅读源码并视察设计模式在其中的应用,能够更深入的明白设计模式。Mybatis至少遇到了以下的设计模式的使用:Builder模式 :例如 SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder;工厂模式 :例如SqlSessionFactory、ObjectFactory、MapperProxyFactory;单例模式 :例如ErrorContext和LogFactory;署理模式 :Mybatis实现的焦点,好比MapperProxy、ConnectionLogger,用的jdk的动态署理;另有executor.loader包使用了cglib或者javassist到达延迟加载的效果;组合模式 :例如SqlNode和各个子类ChooseSqlNode等;模板方法模式 : 例如BaseExecutor和SimpleExecutor,另有BaseTypeHandler和所有的子类例如IntegerTypeHandler;适配器模式 : 例如Log的Mybatis接口和它对jdbc、log4j等种种日志框架的适配实现;装饰者模式 : 例如cache包中的cache.decorators子包中等各个装饰者的实现;迭代器模式 : 例如迭代器模式PropertyTokenizer;接下来挨个模式举行解读,先先容模式自身的知识,然后解读在Mybatis中怎样应用了该模式。1、Builder 模式Builder模式的界说是“将一个庞大工具的构建与它的表现分散,使得同样的构建历程可以建立差别的表现。”,它属于建立类模式,一般来说,如果一个工具的构建比力庞大,超出了结构函数所能包罗的规模,就可以使用工厂模式和Builder模式,相对于工厂模式会产出一个完整的产物,Builder应用于越发庞大的工具的构建,甚至只会构建产物的一个部门。

《effective-java》中第2条也提到:遇到多个结构器参数时,思量用构建者(Builder)模式。Builder模式在Mybatis情况的初始化历程中,SqlSessionFactoryBuilder会挪用XMLConfigBuilder读取所有的MybatisMapConfig.xml和所有的*Mapper.xml文件,构建Mybatis运行的焦点工具Configuration工具,然后将该Configuration工具作为参数构建一个SqlSessionFactory工具。其中XMLConfigBuilder在构建Configuration工具时,也会挪用XMLMapperBuilder用于读取*.Mapper文件,而XMLMapperBuilder会使用XMLStatementBuilder来读取和build所有的SQL语句。

在这个历程中,有一个相似的特点,就是这些Builder会读取文件或者设置,然后做大量的XpathParser剖析、设置或语法的剖析、反射生成工具、存入效果缓存等步骤,这么多的事情都不是一个结构函数所能包罗的,因此大量接纳了Builder模式来解决。对于builder的详细类,方法都多数用build*开头,好比SqlSessionFactoryBuilder为例,它包罗以下方法:SqlSessionFactoryBuilder即凭据差别的输入参数来构建SqlSessionFactory这个工厂工具。2、工厂模式在Mybatis中好比SqlSessionFactory使用的是工厂模式,该工厂没有那么庞大的逻辑,是一个简朴工厂模式。

简朴工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类建立型模式。在简朴工厂模式中,可以凭据参数的差别返回差别类的实例。简朴工厂模式专门界说一个类来卖力建立其他类的实例,被建立的实例通常都具有配合的父类。简朴工厂模式SqlSession可以认为是一个Mybatis事情的焦点的接口,通过这个接口可以执行执行SQL语句、获取Mappers、治理事务。

类似于毗连MySQL的Connection工具。SqlSessionFactory可以看到,该Factory的openSession()方法重载了许多个,划分支持autoCommit、Executor、Transaction 等参数的输入,来构建焦点的SqlSession工具。在DefaultSqlSessionFactory的默认工厂实现里,有一个方法可以看出工厂怎么产出一个产物: 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挪用的底层方法,该方法先从configuration读取对应的情况设置,然后初始化TransactionFactory获得一个Transaction工具,然后通过Transaction获取一个Executor工具,最后通过configuration、Executor、是否autoCommit三个参数构建了SqlSession。在这里其实也可以看到眉目,SqlSession的执行,其实是委托给对应的Executor来举行的。

而对于LogFactory,它的实现代码:public final class LogFactory { private static Constructor<? extends Log> logConstructor; private LogFactory() { // disable construction } public static Log getLog(Class<?> aClass) { return getLog(aClass.getName()); }这里有个特此外地方,是Log变量的的类型是Constructor<? extendsLog>,也就是说该工厂生产的不只是一个产物,而是具有Log公共接口的一系列产物,好比Log4jImpl、Slf4jImpl等许多详细的Log。3、单例模式单例模式(Singleton Pattern):单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局会见的方法。单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行建立这个实例;三是它必须自行向整个系统提供这个实例。

单例模式是一种工具建立型模式。单例模式又名单件模式或单态模式。

单例模式在Mybatis中有两个地方用到单例模式,ErrorContext和LogFactory,其中ErrorContext是用在每个线程规模内的单例,用于记载该线程的执行情况错误信息,而LogFactory则是提供应整个Mybatis使用的日志工厂,用于获得针对项目设置好的日志工具。ErrorContext的单例实现代码:public class ErrorContext { private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>(); private ErrorContext() { } public static ErrorContext instance() { ErrorContext context = LOCAL.get(); if (context == null) { context = new ErrorContext(); LOCAL.set(context); } return context; }结构函数是private修饰,具有一个static的局部instance变量和一个获取instance变量的方法,在获取实例的方法中,先判断是否为空如果是的话就先建立,然后返回结构好的工具。

只是这里有个有趣的地方是,LOCAL的静态实例变量使用了ThreadLocal修饰,也就是说它属于每个线程各自的数据,而在instance()方法中,先获取本线程的该实例,如果没有就建立该线程独占的ErrorContext。4、署理模式署理模式可以认为是Mybatis的焦点使用的模式,正是由于这个模式,我们只需要编写Mapper.java接口,不需要实现,由Mybatis后台帮我们完成详细SQL的执行。署理模式(Proxy Pattern) :给某一个工具提供一个代 理,并由署理工具控制对原工具的引用。

署理模式的英 文叫做Proxy或Surrogate,它是一种工具结构型模式。署理模式包罗如下角色:Subject: 抽象主题角色Proxy: 署理主题角色RealSubject: 真实主题角色署理模式这里有两个步骤,第一个是提前建立一个Proxy,第二个是使用的时候会自动请求Proxy,然后由Proxy来执行详细事务;当我们使用Configuration的getMapper方法时,会挪用mapperRegistry.getMapper方法,而该方法又会挪用mapperProxyFactory.newInstance(sqlSession)来生成一个详细的署理:/** * @author Lasse Voss */public class MapperProxyFactory<T> { private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); public MapperProxyFactory(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } public Class<T> 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<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }}在这里,先通过T newInstance(SqlSession sqlSession)方法会获得一个MapperProxy工具,然后挪用T newInstance(MapperProxy<T> mapperProxy)生成署理工具然后返回。而检察MapperProxy的代码,可以看到如下内容:public class MapperProxy<T> 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类实现了InvocationHandler接口,而且实现了该接口的invoke方法。通过这种方式,我们只需要编写Mapper.java接口类,认真正执行一个Mapper接口的时候,就会转发给MapperProxy.invoke方法,而该方规则会挪用后续的sqlSession.cud>executor.execute>prepareStatement等一系列方法,完成SQL的执行和返回。

5、组合模式组合模式组合多个工具形成树形结构以表现“整体-部门”的结构条理。组合模式对单个工具(叶子工具)和组合工具(组合工具)具有一致性,它将工具组织到树结构中,可以用来形貌整体与部门的关系。

同时它也模糊了简朴元素(叶子工具)和庞大元素(容器工具)的观点,使得客户能够像处置惩罚简朴元素一样来处置惩罚庞大元素,从而使客户法式能够与庞大元素的内部结构解耦。在使用组合模式中需要注意一点也是组合模式最关键的地方:叶子工具和组合工具实现相同的接口。这就是组合模式能够将叶子节点和工具节点举行一致处置惩罚的原因。

S11竞猜

组合模式Mybatis支持动态SQL的强大功效,好比下面的这个SQL:<update id="update" parameterType="org.format.dynamicproxy.mybatis.bean.User"> UPDATE users <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>在这内里使用到了trim、if等动态元素,可以凭据条件来生成差别情况下的SQL;在DynamicSqlSource.getBoundSql方法里,挪用了rootSqlNode.apply(context)方法,apply方法是所有的动态节点都实现的接口:public interface SqlNode { boolean apply(DynamicContext context);}对于实现该SqlSource接口的所有节点,就是整个组合模式树的各个节点:SqlNode组合模式的简朴之处在于,所有的子节点都是同一类节点,可以递归的向下执行,好比对于TextSqlNode,因为它是最底层的叶子节点,所以直接将对应的内容append到SQL语句中: @Override public boolean apply(DynamicContext context) { GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter)); context.appendSql(parser.parse(text)); return true; }可是对于IfSqlNode,就需要先做判断,如果判断通过,仍然会挪用子元素的SqlNode,即contents.apply方法,实现递归的剖析。@Overridepublic boolean apply(DynamicContext context) { if (evaluator.evaluateBoolean(test, context.getBindings())) { contents.apply(context); return true; } return false;} 6、模板方法模式模板方法模式是所有模式中最为常见的几个模式之一,是基于继续的代码复用的基本技术。模板方法模式需要开发抽象类和详细子类的设计师之间的协作。一个设计师卖力给出一个算法的轮廓和骨架,另一些设计师则卖力给出这个算法的各个逻辑步骤。

代表这些详细逻辑步骤的方法称做基本方法(primitive method);而将这些基本方法汇总起来的方法叫做模板方法(template method),这个设计模式的名字就是今后而来。模板类界说一个操作中的算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重界说该算法的某些特定步骤。

S11竞猜

模板方法模式在Mybatis中,sqlSession的SQL执行,都是委托给Executor实现的,Executor包罗以下结构:Executor接口其中的BaseExecutor就接纳了模板方法模式,它实现了大部门的SQL执行逻辑,然后把以下几个方法交给子类定制化完成: @Override public boolean apply(DynamicContext context) { if (evaluator.evaluateBoolean(test, context.getBindings())) { contents.apply(context); return true; } return false; }该模板方法类有几个子类的详细实现,使用了差别的计谋:简朴SimpleExecutor:每执行一次update或select,就开启一个Statement工具,用完连忙关闭Statement工具。(可以是Statement或PrepareStatement工具)重用ReuseExecutor:执行update或select,以sql作为key查找Statement工具,存在就使用,不存在就建立,用完后,不关闭Statement工具,而是放置于Map内,供下一次使用。

(可以是Statement或PrepareStatement工具)批量BatchExecutor:执行update(没有select,JDBC批处置惩罚不支持select),将所有sql都添加到批处置惩罚中(addBatch()),等候统一执行(executeBatch()),它缓存了多个Statement工具,每个Statement工具都是addBatch()完毕后,等候逐一执行executeBatch()批处置惩罚的;BatchExecutor相当于维护了多个桶,每个桶里都装了许多属于自己的SQL,就像苹果蓝里装了许多苹果,番茄蓝里装了许多番茄,最后,再统一倒进堆栈。(可以是Statement或PrepareStatement工具)好比在SimpleExecutor中这样实现update方法: @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) :将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起事情,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为工具结构型模式。适配器模式在Mybatsi的logging包中,有一个Log接口:/** * @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接口详细由谁来实现呢?Mybatis提供了多种日志框架的实现,这些实现都匹配这个Log接口所界说的接口方法,最终实现了所有外部日志框架到Mybatis日志包的适配:Log好比对于Log4jImpl的实现来说,该实现持有了org.apache.log4j.Logger的实例,然后所有的日志方法,均委托该实例来实现。

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) :动态地给一个工具增加一些分外的职责(Responsibility),就增加工具功效来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于差别的场所。凭据翻译的差别,装饰模式也有人称之为“油漆工模式”,它是一种工具结构型模式。装饰者模式在mybatis中,缓存的功效由根接口Cache(org.apache.ibatis.cache.Cache)界说。

整个体系接纳装饰器设计模式,数据存储缓和存的基本功效由PerpetualCache(org.apache.ibatis.cache.impl.PerpetualCache)永久缓存实现,然后通过一系列的装饰器来对PerpetualCache永久缓存举行缓存计谋等利便的控制。如下图:Cache用于装饰PerpetualCache的尺度装饰器共有8个(全部在org.apache.ibatis.cache.decorators包中):FifoCache:先进先出算法,缓存接纳计谋LoggingCache:输出缓存掷中的日志信息LruCache:最近最少使用算法,缓存接纳计谋ScheduledCache:调理缓存,卖力定时清空缓存SerializedCache:缓存序列化和反序列化存储SoftCache:基于软引用实现的缓存治理计谋SynchronizedCache:同步的缓存装饰器,用于防止多线程并发会见WeakCache:基于弱引用实现的缓存治理计谋另外,另有一个特殊的装饰器TransactionalCache:事务性的缓存正如大多数持久层框架一样,mybatis缓存同样分为一级缓存和二级缓存一级缓存,又叫当地缓存,是PerpetualCache类型的永久缓存,生存在执行器中(BaseExecutor),而执行器又在SqlSession(DefaultSqlSession)中,所以一级缓存的生命周期与SqlSession是相同的。二级缓存,又叫自界说缓存,实现了Cache接口的类都可以作为二级缓存,所以可设置如encache等的第三方缓存。

二级缓存以namespace名称空间为其唯一标识,被生存在Configuration焦点设置工具中。二级缓存工具的默认类型为PerpetualCache,如果设置的缓存是默认类型,则mybatis会凭据设置自动追加一系列装饰器。

Cache工具之间的引用顺序为:SynchronizedCache–>LoggingCache–>SerializedCache–>ScheduledCache–>LruCache–>PerpetualCache9、迭代器模式迭代器(Iterator)模式,又叫做游标(Cursor)模式。GOF给出的界说为:提供一种方法会见一个容器(container)工具中各个元素,而又不需袒露该工具的内部细节。迭代器模式Java的Iterator就是迭代器模式的接口,只要实现了该接口,就相当于应用了迭代器模式:Iterator好比Mybatis的PropertyTokenizer是property包中的重量级类,该类会被reflection包中其他的类频繁的引用到。这个类实现了Iterator接口,在使用时经常被用到的是Iterator接口中的hasNext这个函数。

public class PropertyTokenizer implements Iterator<PropertyTokenizer> { 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."); }}可以看到,这个类传入一个字符串到结构函数,然后提供了iterator方法对剖析后的子串举行遍历,是一个很常用的方法类。


本文关键词:干货,MyBatis,S11竞猜,包罗,这,9种,设计模式,虽然,我们

本文来源:S11竞猜-www.huananshenghuo.com

S11竞猜-LOL全球总决赛下注Sweep WeChat yards pay attention to us

  • 24-hour hotline0737-29007558

  • The mobile phone15945484050

Copyright © 2021 Central air conditioning co. LTD All rights reserved Address:Guangzhou economic development zone, guangdong province ICP备17984871号-9