网站源码全局文件是什么(网站源码全局文件是什么格式)〔完整的网站源码包括什么〕

  MyBatis拦截器先容

  MyBatis提供了一种插件(plugin)的功能,固然 叫做插件 ,但着实 这是拦截器功能 。那么拦截器拦截MyBatis中的哪些内容呢?

  我们进入官网看一看:

  MyBatis 答应 你在已映射语句实行 过程中的某一点举行 拦截调用。默认环境 下,MyBatis 答应 利用 插件来拦截的方法调用包罗 :

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

ParameterHandler (getParameterObject, setParameters)

ResultSetHandler (handleResultSets, handleOutputParameters)

StatementHandler (prepare, parameterize, batch, update, query)

  我们看到了可以拦截Executor接口的部分 方法,比如 update ,query,commit,rollback等方法,尚有 其他接口的一些方法等。

  总体概括为:

拦截实行 器的方法

拦截参数的处理 惩罚

拦截结果 集的处理 惩罚

拦截Sql语法构建的处理 惩罚

拦截器的利用 拦截器先容 及设置

  起首 我们看下MyBatis拦截器的接口界说 :

  1

  2

  3

  4

  5

  6

  7

网站源码全局文件是什么(网站源码全局文件是什么格式) 网站源码全局文件是什么(网站源码全局文件是什么格式)〔完整的网站源码包括什么〕 新闻资讯

  8

  9

  publicinterfaceInterceptor {

  Object intercept(Invocation invocation) throwsThrowable;

  Object plugin(Object target);

  voidsetProperties(Properties properties);

  }

  比力 简单 ,只有3个方法 。 MyBatis默认没有一个拦截器接口的实现类,开辟 者们可以实现符合本身 需求的拦截器。

  下面的MyBatis官网的一个拦截器实例:

  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  12

  13

  14

  @Intercepts({@Signature(

  type= Executor.class,

  method = "update",

  args = {MappedStatement.class,Object.class})})

  publicclassExamplePlugin implementsInterceptor {

  publicObject intercept(Invocation invocation) throwsThrowable {

  returninvocation.proceed();

  }

  publicObject plugin(Object target) {

  returnPlugin.wrap(target, this);

  }

  publicvoidsetProperties(Properties properties) {

  }

  }

  全局xml设置 :

  1

  2

  3

  plugins

  plugininterceptor="org.format.mybatis.cache.interceptor.ExamplePlugin"/plugin

  /plugins

  这个拦截器拦截Executor接口的update方法(着实 也就是SqlSession的新增,删除 ,修改操纵 ),全部 实行 executor的update方法都会被该拦截器拦截到。

  源码分析

  下面我们分析一下这段代码背后的源码。

  起首 从源头-设置 文件开始分析:

  XMLConfigBuilder分析 MyBatis全局设置 文件的pluginElement私有方法:

  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  privatevoidpluginElement(XNode parent) throwsException {

  if(parent != null) {

  for(XNode child : parent.getChildren()) {

  String interceptor = child.getStringAttribute("interceptor");

  Properties properties = child.getChildrenAsProperties();

  Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();

  interceptorInstance.setProperties(properties);

  configuration.addInterceptor(interceptorInstance);

  }

  }

  }

  具体 的分析 代码着实 比力 简单 ,就不贴了 ,重要 就是通过反射实例化plugin节点中的interceptor属性表现 的类 。然后调用全局设置 类Configuration的addInterceptor方法。

  1

  2

  3

  publicvoidaddInterceptor(Interceptor interceptor) {

  interceptorChain.addInterceptor(interceptor);

  }

  这个interceptorChain是Configuration的内部属 性,范例 为InterceptorChain,也就是一个拦截器链 ,我们来看下它的界说 :

  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  12

  13

  14

  15

  16

  17

  18

  19

  20

  publicclassInterceptorChain {

  privatefinalListInterceptor interceptors = newArrayListInterceptor();

  publicObject pluginAll(Object target) {

  for(Interceptor interceptor : interceptors) {

  target = interceptor.plugin(target);

  }

  returntarget;

  }

  publicvoidaddInterceptor(Interceptor interceptor) {

  interceptors.add(interceptor);

  }

  publicListInterceptor getInterceptors() {

  returnCollections.unmodifiableList(interceptors);

  }

  }

  如今 我们明白 了拦截器设置 的分析 以及拦截器的归属,如今 我们回过头看下为何拦截器会拦截这些方法(Executor,ParameterHandler ,ResultSetHandler,StatementHandler的部分 方法):

  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  12

  13

  14

  15

  16

  17

  18

  19

  20

  21

  22

  23

  24

  25

  26

  27

  28

  29

  30

  31

  32

  33

  34

  35

  36

  publicParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {

  ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);

  parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);

  returnparameterHandler;

  }

  publicResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,

  ResultHandler resultHandler, BoundSql boundSql) {

  ResultSetHandler resultSetHandler = newDefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);

  resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);

  returnresultSetHandler;

  }

  publicStatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

  StatementHandler statementHandler = newRoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);

  statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);

  returnstatementHandler;

  }

  publicExecutor newExecutor(Transaction transaction, ExecutorType executorType, booleanautoCommit) {

  executorType = executorType == null? defaultExecutorType : executorType;

  executorType = executorType == null? ExecutorType.SIMPLE : executorType;

  Executor executor;

  if(ExecutorType.BATCH == executorType) {

  executor = newBatchExecutor(this, transaction);

  } elseif(ExecutorType.REUSE == executorType) {

  executor = newReuseExecutor(this, transaction);

  } else{

  executor = newSimpleExecutor(this, transaction);

  }

  if(cacheEnabled) {

  executor = newCachingExecutor(executor, autoCommit);

  }

  executor = (Executor) interceptorChain.pluginAll(executor);

  returnexecutor;

  }

  以上4个方法都是Configuration的方法。这些方法在MyBatis的一个操纵 (新增,删除,修改 ,查询)中都会被实行 到,实行 的先后次序 是Executor,ParameterHandler ,ResultSetHandler,StatementHandler(此中 ParameterHandler和ResultSetHandler的创建是在创建StatementHandler[3个可用的实现类CallableStatementHandler,PreparedStatementHandler,SimpleStatementHandler]的时间 ,其构造函数调用的[这3个实现类的构造函数着实 都调用了父类BaseStatementHandler的构造函数]) 。

  这4个方法实例化了对应的对象之后 ,都会调用interceptorChain的pluginAll方法,InterceptorChain的pluginAll刚才已经先容 过了,就是遍历全部 的拦截器 ,然后调用各个拦截器的plugin方法。留意 :拦截器的plugin方法的返回值会直接被赋值给原先的对象

  由于可以拦截StatementHandler,这个接口重要 处理 惩罚 sql语法的构建,因此比如 分页的功能 ,可以用拦截器实现,只必要 在拦截器的plugin方法中处理 惩罚 StatementHandler接口实现类中的sql即可,可利用 反射实现。

  MyBatis还提供了@Intercepts和@Signature关于拦截器的注解 。官网的例子就是利用 了这2个注解,还包罗 了Plugin类的利用 :

  1

  2

  3

  4

  @Override

  publicObject plugin(Object target) {

  returnPlugin.wrap(target, this);

  }

  下面我们就分析这3个 “新组合 ” 的源码 ,起首 先看Plugin类的wrap方法:

  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  12

  publicstaticObject wrap(Object target, Interceptor interceptor) {

  MapClass?, SetMethod signatureMap = getSignatureMap(interceptor);

  Class? type = target.getClass();

  Class?[] interfaces = getAllInterfaces(type, signatureMap);

  if(interfaces.length 0) {

  returnProxy.newProxyInstance(

  type.getClassLoader(),

  interfaces,

  newPlugin(target, interceptor, signatureMap));

  }

网站源码全局文件是什么(网站源码全局文件是什么格式) 网站源码全局文件是什么(网站源码全局文件是什么格式)〔完整的网站源码包括什么〕 新闻资讯

  returntarget;

  }

  Plugin类实现了InvocationHandler接口,很显着 ,我们看到这里返回了一个JDK自身提供的动态署理 类。我们解剖 一下这个方法调用的其他方法:

  getSignatureMap方法:

  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  12

  13

  14

  15

  16

  17

  18

  19

  20

  21

  22

  privatestaticMapClass?, SetMethod getSignatureMap(Interceptor interceptor) {

  Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);

  if(interceptsAnnotation == null) { // issue #251

  thrownewPluginException("No @Intercepts annotation was found in interceptor "+ interceptor.getClass().getName());

  }

  Signature[] sigs = interceptsAnnotation.value();

  MapClass?, SetMethod signatureMap = newHashMapClass?, SetMethod();

  for(Signature sig : sigs) {

  SetMethod methods = signatureMap.get(sig.type());

  if(methods == null) {

  methods = newHashSetMethod();

  signatureMap.put(sig.type(), methods);

  }

  try{

  Method method = sig.type().getMethod(sig.method(), sig.args());

  methods.add(method);

  } catch(NoSuchMethodException e) {

  thrownewPluginException("Could not find method on "+ sig.type() + " named "+ sig.method() + ". Cause: "+ e, e);

  }

  }

  returnsignatureMap;

  }

  getSignatureMap方法表明 :起首 会拿到拦截器这个类的@Interceptors注解 ,然后拿到这个注解的属性@Signature注解聚集 ,然后遍历这个聚集 ,遍历的时间 拿出@Signature注解的type属性(Class范例 ) ,然后根据这个type得到带有method属性和args属性的Method。由于@Interceptors注解的@Signature属性是一个属性,以是 终极 会返回一个以type为key,value为SetMethod的Map 。

  1

  2

  3

  4

  @Intercepts({@Signature(

  type= Executor.class,

  method = "update",

  args = {MappedStatement.class,Object.class})})

  比如 这个@Interceptors注解会返回一个key为Executor ,value为聚集 (这个聚集 只有一个元素,也就是Method实例,这个Method实例就是Executor接口的update方法 ,且这个方法带有MappedStatement和Object范例 的参数)。这个Method实例是根据@Signature的method和args属性得到的。假如 args参数跟type范例 的method方法对应不上,那么将会抛出非常 。

  getAllInterfaces方法:

  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  12

  privatestaticClass?[] getAllInterfaces(Class? type, MapClass?, SetMethod signatureMap) {

  SetClass? interfaces = newHashSetClass?();

  while(type != null) {

  for(Class? c : type.getInterfaces()) {

  if(signatureMap.containsKey(c)) {

  interfaces.add(c);

  }

  }

  type = type.getSuperclass();

  }

  returninterfaces.toArray(newClass?[interfaces.size()]);

  }

  getAllInterfaces方法表明 :根据目标 实例target(这个target就是之前所说的MyBatis拦截器可以拦截的类,Executor,ParameterHandler,ResultSetHandler,StatementHandler)和它的父类们,返回signatureMap中含有target实现的接口数组。

  以是 Plugin这个类的作用就是根据@Interceptors注解 ,得到这个注解的属性@Signature数组,然后根据每个@Signature注解的type,method ,args属性利用 反射找到对应的Method。终极 根据调用的target对象实现的接口决定是否返回一个署理 对象更换 原先的target对象。

  比如 MyBatis官网的例子,当Configuration调用newExecutor方法的时间 ,由于Executor接口的update(MappedStatement ms, Object parameter)方法被拦截器被截获 。因此终极 返回的是一个署理 类Plugin ,而不是Executor。如许 调用方法的时间 ,假如 是个署理 类,那么会实行 :

  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  publicObject invoke(Object proxy, Method method, Object[] args) throwsThrowable {

  try{

  SetMethod methods = signatureMap.get(method.getDeclaringClass());

  if(methods != null methods.contains(method)) {

  returninterceptor.intercept(newInvocation(target, method, args));

  }

  returnmethod.invoke(target, args);

  } catch(Exception e) {

  throwExceptionUtil.unwrapThrowable(e);

  }

  }

  没错 ,假如 找到对应的方法被署理 之后,那么会实行 Interceptor接口的interceptor方法。

  这个Invocation类如下:

  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  12

  13

  14

  15

  16

  17

  18

  19

  20

  21

  22

  23

  24

  25

  26

  27

  28

  29

  publicclassInvocation {

  privateObject target;

  privateMethod method;

  privateObject[] args;

  publicInvocation(Object target, Method method, Object[] args) {

  this.target = target;

  this.method = method;

  this.args = args;

  }

  publicObject getTarget() {

  returntarget;

  }

  publicMethod getMethod() {

  returnmethod;

  }

  publicObject[] getArgs() {

  returnargs;

  }

  publicObject proceed() throwsInvocationTargetException, IllegalAccessException {

  returnmethod.invoke(target, args);

  }

  }

  它的proceed方法也就是调用原先方法(不走署理 ) 。

  总结

  MyBatis拦截器接口提供的3个方法中,plugin方法用于某些处理 惩罚 器(Handler)的构建过程。interceptor方法用于处理 惩罚 署理 类的实行 。setProperties方法用于拦截器属性的设置 。

  着实 MyBatis官网提供的利用 @Interceptors和@Signature注解以及Plugin类如许 处理 惩罚 拦截器的方法 ,我们不肯定 要直接如许 利用 。我们也可以扬弃 这3个类,直接在plugin方法内部根据target实例的范例 做相应的操纵 。

  总体来说MyBatis拦截器还是 很简单 的,拦截器本身 不必要 太多的知识点,但是学习拦截器必要 对MyBatis中的各个接口很认识 ,由于 拦截器涉及到了各个接口的知识点 。

  本文泉源 于IT技能 交换 论坛-西安java培训华为思科认证网络通讯 论坛欢迎 转载!

  毕业 后不知何去何从?想学JAVA还是 网络通讯 技能 ?测测你得当 哪种职业?进论坛一测!