MyBatis的Configuration配置中有一个Plugin配置,根据其名可以解释为“插件”,这个插件实质可以理解为“拦截器”。“拦截器”这个名词不陌生,在众多框架中均有“拦截器”。这个Plugin有什么用呢?活着说拦截器有什么用呢?可以想想拦截器是怎么实现的。Plugin用到了Java中很重要的一个特性——动态代理。所以这个Plugin可以理解为,在调用一个方法时,我“拦截”其方法做一些我想让它做的事。它可以拦截以下方法:
在官方文档中有这么一句话:If you attempt to modify or override the behaviour of a given method, you’re likely to break the core of MyBatis. 谨慎使用自定义Plugin拦截器,因为它可能修改Mybatis核心的东西。实现自定义Plugin我们需要实现 Interceptor接口。并未这个类注解@Intercepts。
1 package day_8_mybatis.util; 2 3 import java.util.Iterator; 4 import java.util.Map; 5 import java.util.Properties; 6 7 import org.apache.ibatis.plugin.Interceptor; 8 import org.apache.ibatis.plugin.Intercepts; 9 import org.apache.ibatis.plugin.Invocation; 10 import org.apache.ibatis.plugin.Plugin; 11 import org.apache.ibatis.plugin.Signature; 12 13 /** 14 * @author turbo 15 * 16 * 2016年10月25日 17 */ 18 @Intercepts({ 19 @Signature( 20 type = Map.class, 21 method = "get", 22 args = {Object.class} 23 )}) 24 public class ExamplePlugin implements Interceptor { 25 26 /* 此方法用于实现拦截逻辑 27 * @see org.apache.ibatis.plugin.Interceptor#intercept(org.apache.ibatis.plugin.Invocation) 28 */ 29 @Override 30 public Object intercept(Invocation invocation) throws Throwable { 31 32 return "ExamplePlugin"; 33 } 34 35 /* 使用当前的这个拦截器实现对目标对象的代理(内部实现时Java的动态代理) 36 * @see org.apache.ibatis.plugin.Interceptor#plugin(java.lang.Object) 37 */ 38 @Override 39 public Object plugin(Object target) { 40 return Plugin.wrap(target, this); 41 } 42 43 /* 此方法和上一节所讲的自定义对象工厂中的setProperties一样,初始化Configuration时通过配置文件配置property传递参数给此方法并调用。 44 * @see org.apache.ibatis.plugin.Interceptor#setProperties(java.util.Properties) 45 */ 46 @Override 47 public void setProperties(Properties properties) { 48 Iterator iterator = properties.keySet().iterator(); 49 while (iterator.hasNext()){ 50 String keyValue = String.valueOf(iterator.next()); 51 System.out.println(properties.getProperty(keyValue)); 52 } 53 } 54 55 }
别忘了在mybatis-config.xml的配置文件中注册自定义Plugin。(下面的配置中有一些遗留代码,是在上两节中的配置,可以选择性的忽略。)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 6 <configuration> 7 <!-- 注意configuration中各个属性配置的顺序应为:properties,settings,typeAliases,typeHandlers,objectFactory,objectWrapperFactory,reflectorFactory,plugins,environments,databaseIdProvider,mappers)--> 8 <properties> 9 <property name="driver" value="com.mysql.jdbc.Driver"/> 10 <property name="url" value="jdbc:mysql://localhost:3306/test"/> 11 <property name="username" value="root"/> 12 <property name="password" value="0000"/> 13 </properties> 14 <!-- 15 <typeHandlers> 16 <typeHandler handler="day_8_mybatis.util.ExampleTypeHandler" javaType="java.util.Date" jdbcType="VARCHAR"/> 17 </typeHandlers> 18 <objectFactory type="day_8_mybatis.util.ExampleObjectFactory"> 19 <property name="someProperty" value="100"/> 20 </objectFactory> 21 --> 22 <plugins> 23 <plugin interceptor="day_8_mybatis.util.ExamplePlugin"> 24 <property name="someProperty" value="100"/> 25 </plugin> 26 </plugins> 27 <environments default="development"> 28 <environment id="development"> 29 <transactionManager type="JDBC" /> 30 <dataSource type="POOLED"> 31 <property name="driver" value="${driver}"/> 32 <property name="url" value="${url}"/> 33 <property name="username" value="${username}"/> 34 <property name="password" value="${password}"/> 35 </dataSource> 36 </environment> 37 </environments> 38 <mappers> 39 <mapper resource="day_8_mybatis/mapper/UserMapper.xml"/> 40 <mapper resource="day_8_mybatis/mapper/NoteMapper.xml"/> 41 </mappers> 42 43 </configuration> 44 45
客户端测试代码:
1 package day_8_mybatis; 2 3 import java.io.IOException; 4 import java.util.HashMap; 5 import java.util.Map; 6 7 import org.apache.ibatis.session.SqlSession; 8 9 import day_8_mybatis.util.ExamplePlugin; 10 import day_8_mybatis.util.SessionFactory; 11 12 /** 13 * 客户端 14 * @author turbo 15 * 16 * 2016年10月25日 17 */ 18 public class Main { 19 20 /** 21 * @param args 22 * @throws IOException 23 */ 24 public static void main(String[] args) throws Exception { 25 String resource = "day_8_mybatis/mybatis-config.xml"; //获取mybatis配置文件路径 26 SqlSession sqlSession = SessionFactory.getSqlSession(resource); //通过SessionFactory工具类(此工具类为自己构造即util包中的SessionFactory)构造SqlSession 27 28 Map map = new HashMap(); 29 map = (Map)new ExamplePlugin().plugin(map); 30 System.out.println(map.get("")); 31 32 } 33 34 }
至此,我们就简单的了解了MyBatis中的Plugin。有兴趣的可以看看我们在客户端测试代码中的第29行所调用的plugin方法。即调用了Plugin类的静态方法wrap(Object target, Interceptor intercpetor),追踪该方法会发现,此方法即是Java的动态代理。
1 public static Object wrap(Object target, Interceptor interceptor) 2 { 3 Map signatureMap = getSignatureMap(interceptor); 4 Class type = target.getClass(); 5 Class interfaces[] = getAllInterfaces(type, signatureMap); 6 if(interfaces.length > 0) 7 return Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)); //返回代理类实例 8 else 9 return target; 10 }
动态代理很重要,反射很重要。一定要反复理解领会动态代理以及反射,这对我们读懂很多框架源代码有很大帮助。这篇仅仅简单了解,不做过多的深入。