核心代码简介
MyBatis 插件原理:责任链模式 + JDK 动态代理 ( 接口、代理对象、代理类:实现 jdk 的 InvocationHandler )
Mybatis 插件核心接口:Interceptor
/** * MyBatis 插件的和心接口 */ public interface Interceptor { /** * 这个方法是插件的核心方法,插件必须实现此方法 * invocation 这个对象通过发生调用原来的对象方法 * 插件的核心是执行是拦截四个接 口的子对象,拦截以后进入 intercept() 函数执行相关的业务 * invocation 对象可以获取四个接口的具体实现类 * * @param invocation 这个对象包含了目标对象(四大接口),代理方法,参数 * @return * @throws Throwable */ Object intercept(Invocation invocation) throws Throwable; /** * 作用是: 把拦截的对象编程一个代理对象,并返回他(目标的代理对象) * * @param target 被拦截的对象 * @return */ default Object plugin(Object target) { return Plugin.wrap(target, this); } /** * 允许插件在注册的时候,配置插件需要的参数,这个参数可以下 Mybatis 的核心配置文件中祖册插件的时候一起配置 * * @param properties */ default void setProperties(Properties properties) { } }
核心注解:
@Intercepts、
@Signature 签名注解,
type:申明此插件拦截器具体需要拦截那个接口的实现类对象,(四大接口对象)
method:申明此插件拦截器具体需要拦截处理那个 方法 (type 中的方法名称)
args : 此拦截器拦截到的方法中,参数的类型,(type 中的方法对应的具体参数类型)
代码解析如下:
使用示例:
@Intercepts( { @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}), @Signature(type = StatementHandler.class, method = "getBoundSql", args = {}), @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}), } )
注解代码:@Intercepts
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Intercepts { /** * 签名注解 * * @return */ Signature[] value(); }
注解代码:@Signature
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({}) public @interface Signature { /** * 申明此插件拦截器具体需要拦截那个接口的实现类对象, * * @return */ Class<?> type(); /** * 申明此插件拦截器具体需要拦截处理那个 方法 * * @return */ String method(); /** * 拦截的方法中,参数的数据类型 * 如:Executor 中的 int update(MappedStatement ms, Object parameter); * args = {MappedStatement.class, Object.class} * * @return */ Class<?>[] args(); }
四大接口介绍:
1、org.apache.ibatis.executor.Executor
(Update、query、flushStatements、commit、rollback、getTransaction、close、isClosed)拦截执行的方法
参看: https://www.cnblogs.com/virgosnail/p/10067964.html
2、org.apache.ibatis.executor.parameter.ParameterHandler 拦截参数的处理
请参看: https://www.cnblogs.com/virgosnail/p/10068355.html
3、org.apache.ibatis.executor.resultset.ResultSetHandler 拦截结果集的处理(映射成POJO对象的处理)
请参看: https://www.cnblogs.com/virgosnail/p/10079712.html
4、org.apache.ibatis.executor.statement.StatementHandler 拦截 SQL 语句的构建处理(修改SQL脚本的处理)
请参看: https://www.cnblogs.com/virgosnail/p/10073235.html
源码简介
1、插件注册阶段:
插件初始化:插件初始化是在 Mybatis 初始化的时候完成的,如: 通过 XMLConfigBuilder 的解析方法从 xml 文件中解析,生产 Configuration 对象进行的初始化,
插件对象创建 InterceptorChain :
public class InterceptorChain { /** * 作用是: 把解析的插件进行注册和容器的收集 */ private final List<Interceptor> interceptors = new ArrayList<>(); /** * 把具体的四大接口的具体实现类生成代理对象 * * @param target 四大接口的实现类对象 * @return */ public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; } /** * 插件注册的具体方法,调佣此方法会将插件添加的集合中 * * @param interceptor */ public void addInterceptor(Interceptor interceptor) { interceptors.add(interceptor); } /** * 获取已注册的插件 * * @return */ public List<Interceptor> getInterceptors() { return Collections.unmodifiableList(interceptors); } }
XMLConfigBuilder
private void pluginElement(XNode parent) throws Exception { if (parent != null) { // 1、循环配置中的所有自定义插件 for (XNode child : parent.getChildren()) { String interceptor = child.getStringAttribute("interceptor"); // 2、如果插件中有配置属性,将属性获取,并注册到 Properties 对象中 Properties properties = child.getChildrenAsProperties(); // 3、同时获取插件注册的具体对象 Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance(); // 4、调用插件的属性赋值方法,将属性赋值给插件中使用 interceptorInstance.setProperties(properties); // 5、注册插件到集合中 configuration.addInterceptor(interceptorInstance); } } }
2、插件执行阶段:
通过动态代理组织多个插件(拦截器),通过这些插件可以改变 Mybatis 的默认行为,Mybatis 允许在已映射的语句执行过程中的某一个点进行拦截调用处理,默认情况下 Mybatis 允许拦截调用的方法包括:Update、query、flushStatements、commit、rollback、getTransaction、close、 isClosed
以 insert 为例: DefaultSqlSession 类中
@Override public int insert(String statement, Object parameter) { // 参数往下传调用 update() return update(statement, parameter); } @Override public int update(String statement) { return update(statement, null); } @Override public int update(String statement, Object parameter) { try { dirty = true; MappedStatement ms = configuration.getMappedStatement(statement); // 开始调用执行器执行方法 return executor.update(ms, wrapCollection(parameter)); } catch (Exception e) { throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
执行器创建:
org.apache.ibatis.session.defaults.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(); } }
org.apache.ibatis.session.Configuration 中进行了执行器的初始化
public Executor newExecutor(Transaction transaction, ExecutorType executorType) { // 若执行器为空。则返回一个 默认的执行器 executorType = executorType == null ? defaultExecutorType : executorType; // 如果赋值默认执行器之后还是空的,那么 赋值一个 简单执行器 executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } // 调用 InterceptorChain 实例 的 pluginAll() 方法执行到 插件的 plugin() 方法 executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
类: org.apache.ibatis.plugin.InterceptorChain, (执行update时,target是执行器 Executor 的对象)
public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }
将 target(执行update时,target是执行器 Executor)的对象进行变换为代理对象
public interface Interceptor { Object intercept(Invocation invocation) throws Throwable; default Object plugin(Object target) { return Plugin.wrap(target, this); }
org.apache.ibatis.plugin.Plugin 是一个代理工具类,最终代理执行 intercept( Invocation invocation )
/** * 这就是一个代理工具类,实现了 JDK 反射中的 InvocationHandler 动态代理 */ public class Plugin implements InvocationHandler { // 代理对象中的目标对象 private final Object target; // 插件 private final Interceptor interceptor; // @Signature 注解中生命的方法,初始化阶段会解析注解 @Intercepts 进行加载进这个 Map private final Map<Class<?>, Set<Method>> signatureMap; public static Object wrap(Object target, Interceptor interceptor) { Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor); // 目标对象的类信息 Class<?> type = target.getClass(); Class<?>[] interfaces = getAllInterfaces(type, signatureMap); if (interfaces.length > 0) { // new 了一个 代理对象(InvocationHandler)进行代理 return Proxy.newProxyInstance( // 指定当前目标对象使用的类加载器 type.getClassLoader(), // 目标对象实现的接口类型 interfaces, new Plugin(target, interceptor, signatureMap)); } return target; } /** * JDK 动态代理的执行代理的方法 * * @param proxy 代理对象 * @param method * @param args * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { // 从签名注解中获取方法 Set<Method> methods = signatureMap.get(method.getDeclaringClass()); // 如果执行的方法属于签名中的方法,那么,进行代理执行(插件执行) if (methods != null && methods.contains(method)) { // 代理对象执行方法,插件执行,Invocation 对象包含了 目标对象,被代理的方法,参数 return interceptor.intercept(new Invocation(target, method, args)); } // 如果执行的方法不属于签名中的方法,那么,不需要通过代理插件执行 return method.invoke(target, args); } catch (Exception e) { throw ExceptionUtil.unwrapThrowable(e); } } }
标签:插件,return,target,对象,Object,Mybatis,原理,class From: https://www.cnblogs.com/Alay/p/16837652.html