5.MyBatis插件的原理?源码剖析


Mybatis插件的加载原理

1.MyBatis支持自定义插件,目的是能让开发者结合业务对框架进行增强,自定义的插件类必须实现org.apache.ibatis.plugin.Interceptor接口。切需要在核心XML配置文件中通过plugins显式声明,如:
在这里插入图片描述
2.进入XMLConfigBuilder类的pluginElement()方法,这里式解析核心配置文件plugins标签的地方,根据源码得知这里解析plugin标签根据反射生成插件实现类并保存到configuration的interceptorChain中一个Interceptor的集合。

private void pluginElement(XNode parent) throws Exception {
    
    
        if (parent != null) {
    
    
            Iterator var2 = parent.getChildren().iterator();

            while(var2.hasNext()) {
    
    
                XNode child = (XNode)var2.next();
                String interceptor = child.getStringAttribute("interceptor");
                Properties properties = child.getChildrenAsProperties();
                Interceptor interceptorInstance = (Interceptor)this.resolveClass(interceptor).newInstance();
                interceptorInstance.setProperties(properties);
                this.configuration.addInterceptor(interceptorInstance);
            }
        }

    }

总结:根据这里可以得知如果多个插件同时增强代理一个Handler那么在配置文件plugins标签中排在最后的插件拦截器最先执行。

Mybatis插件依靠动态代理生成代理类原理

用Execute举例
1.SqlSessionFactory的openSession()方法中openSessionFromDataSource()方法中会创建Execute的实现类并赋值给SqlSession

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    
    
        Transaction tx = null;

        DefaultSqlSession var8;
        try {
    
    
            Environment environment = this.configuration.getEnvironment();
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            // 在此处实力Execute
            Executor executor = this.configuration.newExecutor(tx, execType);
            var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
        } catch (Exception var12) {
    
    
            this.closeTransaction(tx);
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
        } finally {
    
    
            ErrorContext.instance().reset();
        }

        return var8;
    }

2.进入configuration.newExecutor(tx, execType);方法

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    
    
        executorType = executorType == null ? this.defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Object 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);
        }
		// 判断是否开启二级缓存,如果开启二级缓存则使用CachingExecutor
        if (this.cacheEnabled) {
    
    
            executor = new CachingExecutor((Executor)executor);
        }
		// 根据XML中配置的拦截器,为现有的Executor 进行动态代理增强
        Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
        return executor;
    }

3.进入InterceptorChain的pluginAll()方法
interceptorChain保存了所有的拦截器(interceptors),是mybatis初始化的时候创建的。调用拦截器链中的拦截器依次的对目标进行拦截或增强。interceptor.plugin(target)中的target就可以理解为原生的Executor 。返回的target是被重重代理后的对象

// 注册的拦截器插件集合
private final List<Interceptor> interceptors = new ArrayList();
public Object pluginAll(Object target) {
    
     
 	for (Interceptor interceptor : interceptors) {
    
     
 		target = interceptor.plugin(target);
 	 }
  		return target; 
 }

4.进入每个插件拦截器的interceptor.plugin(target);方法
拦截器Interceptor有三个抽象方法需要实现分别是

public interface Interceptor {
    
    
	//当代理Executor执行被拦截的方法是,对应的拦截器会监听到并执行intercept方法参数Invocation 包含
	//private final Object target; 被代理的Executor或者Executor的上一层代理类
    //private final Method method;被代理的Executor所执行的方法
    //private final Object[] args;执行方法的参数
    //三大对象,一般实现会:{
    
    
    //sysout("对方法进行了前置增强....");
    //invocation.proceed(); 执行原方法
    //sysout("对方法进行了后置增强....");
    //}
    Object intercept(Invocation var1) throws Throwable;
	// 主要是为了把这个拦截器生成一个代理放到拦截器链中
	//参数为Executor对象,返回值为Executor代理对象
	//InterceptorChain的pluginAll()方法中调用用于生成Executor的重重代理对象
	// 一般实现{return Plugin.wrap(target,this);}
    Object plugin(Object var1);
	//插件初始化的时候调用,也只调用一次,插件配置的属性从这里设置进来即获取plugin标签中的property标签内容
    void setProperties(Properties var1);
}

5.进入Plugin.wrap(target,this);方法

public static Object wrap(Object target, Interceptor interceptor) {
    
    
       // 获取插件拦截器拦截的方法
        Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
        // 获取代理前对象的类型
        Class<?> type = target.getClass();
        // 获取代理前对象所实现的全部接口
        Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
        // 如果所实现的接口大于0则使用JDK动态代理生成代理类,如果小于0则返回原对象。
        return interfaces.length > 0 ? Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)) : target;
    }

总结:
在创建Executor的实现类的过程中如果有插件拦截器拦截可Executor中的某个方法并且有再XML中显式配置则生成的Executor实现类为动态代理对象。

Mybatis代理类执行原理

用Execute中的query方法举例
1.根据Mybatis插件依靠动态代理生成代理类原理可以如果Execute为代理类,当query方法执行时,实际上执行的应该时Plugin中的invoke方法,因为Execute为JDK动态代理产生的代理类

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
     
try 
{
    
    
	/**获取被拦截方法列表,比如: * signatureMap.get(Executor.class), 可能返回 [query, update, commit] */
 	Set<Method> methods = signatureMap.get(method.getDeclaringClass());
  //检测方法列表是否包含被拦截的方法 
	 if (methods != null && methods.contains(method)) {
    
    
	 	//该方法被拦截执行插件逻辑 
   		return interceptor.intercept(new Invocation(target, method, args)); 
	 }	
    	//该方法未被拦截执行被拦截的方法	   	      
    	return method.invoke(target, args); 
 } catch(Exception e){
    
     
    } 
}

总结:插件的实现原理采用了代理模式(JDK动态代理)

猜你喜欢

转载自blog.csdn.net/yangxiaofei_java/article/details/111147354