Mybatis拦截器

可拦截的接口

可以拦截的Mybatis四大核心组件

  • ExecutorMyBatis 执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护

  • StatementHandler 封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合

  • ParameterHandler 负责对用户传递的参数转换成JDBC Statement 所需要的参数

  • ResultSetHandler 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;

拦截器加载与执行

  1. springIOC初始化SqlSessionFactoryBean的时候执行afterPropertiesSet()方法去构建SqlSessionFactory,这个过程会加载所有的拦截器到一个List中
	public void afterPropertiesSet() throws Exception {
    this.sqlSessionFactory = buildSqlSessionFactory();
  }
    //读取配置的拦截器
	protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
	   final Configuration targetConfiguration;
	   //省略...
	   //添加所有拦截器
	   if (!isEmpty(this.plugins)) {
	     Stream.of(this.plugins).forEach(plugin -> {
	       targetConfiguration.addInterceptor(plugin);
	       LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
	     });
	     //省略...
	 	return this.sqlSessionFactoryBuilder.build(targetConfiguration);
	   }
    
 	// InterceptorChain,持有所有拦截器
	public class InterceptorChain {
	
		  private final List<Interceptor> interceptors = new ArrayList<>();
		
		  public Object pluginAll(Object target) {
		    for (Interceptor interceptor : interceptors) {
		      target = interceptor.plugin(target);
		    }
		    return target;
		  }
		  
		  public void addInterceptor(Interceptor interceptor) {
		    interceptors.add(interceptor);
		  }
		
		  public List<Interceptor> getInterceptors() {
		    return Collections.unmodifiableList(interceptors);
		  }
	}

  1. 在实例化Executor、ParameterHandler、ResultSetHandler、StatementHandler四大接口对象的时候分别调用interceptorChain.pluginAll()方法。基于责任链模式循环执行拦截器链所有的拦截器的plugin() 方法。
    Executor为例
	// 构造Executor对象
	public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
	   //省略...
	   //调用pluginAll方法,executor在这里已经被修改成一个代理对象。
	    executor = (Executor) interceptorChain.pluginAll(executor);
	    return executor;
	}

拦截器使用

主键赋值插件

mybatis常用于分页查询,比如开源的分页插件PageHelper就是基于拦截器实现,动态修改sql语句实现分页。本人项目中写过一个insert主键自动赋值拦截器,示例只是简单演示原理。

实现mybatis提供接口Interceptor实现它的三个方法:

  1. plugin 方法是拦截器用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理。当返回的是代理的时候我们可以对其中的方法进行拦截来调用intercept方法,这就是intercept的拦截原理

  2. setProperties 方法是用于在Mybatis配置文件中指定一些属性的。

  3. intercept 拦截目标对象的方法执行

//Signature签名注解
public @interface Signature {
	 Class<?> type(); //拦截类型
	
	 String method(); //拦截的方法
	
	 Class<?>[] args(); //拦截的参数
}


@Intercepts(@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }))
public class GenerateKeyInterceptor implements Interceptor {

    // 自定义属性
	private String idType;
	
    /**
     * 拦截目标对象的方法执行
     *
     * @param invocation
     * @return
     * @throws Throwable
     */
    public Object intercept(Invocation invocation) throws Throwable {
		//获取xml中的一个select|update|insert|delete节点
       MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
		Object paramObj = invocation.getArgs()[1]; // 获取参数
		if (SqlCommandType.INSERT.equals(mappedStatement.getSqlCommandType())) { // 获取 SQL类型
			// MetaObject是mybatis提供的反射工具类
			MetaObject metaObject = SystemMetaObject.forObject(paramObj);
			if (metaObject.getValue("id") == null && "String".equals(metaObject.getSetterType("id").getSimpleName()) {
				metaObject.setValue("id", getUUID());
			}
		}
		// 执行目标方法
		return invocation.proceed();
    }

    /**
     * 为目标对象创建代理对象
     *
     * @param target
     * @return
     */
    public Object plugin(Object target) {
        //利用mybatis工具类创建代理对象
        if (target instanceof Executor) {
			return Plugin.wrap(target, this);
		}
		return target;
    }

    /**
     * 自定义配置信息
     *
     * @param properties
     */
    public void setProperties(Properties properties) {
   		this.idType = properties.getProperty("idType");
    }
	
	private String getUUID() {
		return UUID.randomUUID().toString().replace("-", "");
	}
}

配置插件

	@Configuration
	@MapperScan("com.**.mapper")
	public class MybatisConfig {
		//省略...
		// SqlSessionFactoryBean 
	    @Bean
	    public SqlSessionFactoryBean sqlSessionFactory() throws Exception {
	        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
	        //数据源
	        sqlSessionFactoryBean.setDataSource(dataSource());
	        // mapper文件路径
	        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml"));
	        // 别名,让*Mpper.xml实体类映射可以不加上具体包名
	        sqlSessionFactoryBean.setTypeAliasesPackage("com.**.entity");
	        //自定义插件
	        sqlSessionFactoryBean.setPlugins(generateKeyInterceptor());
	        //驼峰命名
	        sqlSessionFactoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
	        return sqlSessionFactoryBean;
	    }
	
	    /**
	     * 自定义全局主键拦截器
	     *
	     * @return
	     */
	    @Bean
	    public GenerateKeyInterceptor generateKeyInterceptor() {
	        GenerateKeyInterceptor generateKeyPlugin = new GenerateKeyInterceptor();
	        Properties properties = new Properties();
	        properties.put("idType", "自定义属性");
	        generateKeyPlugin.setProperties(properties);
	        return generateKeyPlugin;
	    }
	}

执行顺序

  • 不同类型 Executor→StatementHandler→ParameterHandler→ResultSetHandler

  • 同类型,配置在前的后执行,即最后的先执行,原因是拦截器通过了层层代理嵌套执行,最外层先执行

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值