SpringBoot整合Mybatis自定义拦截器不起作用的处理方

编辑: admin 分类: java 发布时间: 2021-12-03 来源:互联网
目录
  • SpringBoot整合Mybatis自定义拦截器不起作用
    • 1. 原始的读取mybatis-config.xml文件
    • 2. 与SpringBoot容器整合
      • 2.1 mybatis的自动装载
    • 3. 在mybatis-config.xml配置又放入Spring容器
    • SpringBoot 自定义Mybatis拦截器
      • 第一种
        • 第二种
          • 第三种

          SpringBoot整合Mybatis自定义拦截器不起作用

          Mybatis插件生效的方式:

          1. 原始的读取mybatis-config.xml文件

          该方式和Spring无关,是通过反射的形式创建插件对象,此时会执行org.apache.ibatis.plugin.Interceptor#setProperties方法,以读取配置参数。

          mybatis:
            mapper-locations: classpath*:/mapping/*.xml
            type-aliases-package: com.tellme.pojo
            #读取全局配置的地址
            config-location: classpath:mybatis-config.xml

          在resource目录下配置mybatis的全局配置:

          <?xml version="1.0" encoding="UTF-8" ?>
          <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
          <configuration>
              <settings>
                  <setting name="cacheEnabled" value="true"/>
                  <setting name="lazyLoadingEnabled" value="true"/>
                  <setting name="multipleResultSetsEnabled" value="true"/>
                  <setting name="useColumnLabel" value="true"/>
                  <setting name="mapUnderscoreToCamelCase" value="true"/>
                  <setting name="useGeneratedKeys" value="true"/>
                  <setting name="defaultExecutorType" value="SIMPLE"/>
                  <setting name="defaultStatementTimeout" value="25000"/>
              </settings>
              <typeAliases>
                  <typeAlias alias="Integer" type="java.lang.Integer"/>
                  <typeAlias alias="Long" type="java.lang.Long"/>
                  <typeAlias alias="HashMap" type="java.util.HashMap"/>
                  <typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap"/>
                  <typeAlias alias="ArrayList" type="java.util.ArrayList"/>
                  <typeAlias alias="LinkedList" type="java.util.LinkedList"/>
              </typeAliases>
              <!--配置的插件名-->
              <plugins>
                  <plugin interceptor="com.xxx.yyy.plugins.PrintSqlInfoInterceptor"/>
              </plugins>
          </configuration>

          2. 与SpringBoot容器整合

          网上很多方案说:mybatis自定义拦截器上加上@Component注解便可以生效。但是我将自定义拦截器放入到Spring容器中,自定义拦截器却失效了。

          然后找到了springboot配置多数据源后mybatis拦截器失效文章,说是自定义配置了数据源导致了拦截器失效。

          2.1 mybatis的自动装载

          源码位置:org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

          @Configuration
          @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
          @ConditionalOnBean(DataSource.class)
          @EnableConfigurationProperties(MybatisProperties.class)
          @AutoConfigureAfter(DataSourceAutoConfiguration.class)
          public class MybatisAutoConfiguration {
            private static Log log = LogFactory.getLog(MybatisAutoConfiguration.class);
            @Autowired
            private MybatisProperties properties;
             //会依赖注入Spring容器中所有的mybatis的Interceptor拦截器
            @Autowired(required = false)
            private Interceptor[] interceptors;
             ...
            @Bean
            @ConditionalOnMissingBean
            public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
              SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
              factory.setDataSource(dataSource);
              factory.setVfs(SpringBootVFS.class);
              if (StringUtils.hasText(this.properties.getConfigLocation())) {
                factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
              }
              factory.setConfiguration(properties.getConfiguration());
              //手动放入到了setPlugins方法中。
              if (!ObjectUtils.isEmpty(this.interceptors)) {
                factory.setPlugins(this.interceptors);
              }
              if (this.databaseIdProvider != null) {
                factory.setDatabaseIdProvider(this.databaseIdProvider);
              }
              if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
                factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
              }
              if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
                factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
              }
              if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
                factory.setMapperLocations(this.properties.resolveMapperLocations());
              }
              return factory.getObject();
            }
             ...
          }

          上面源码中:自动注入了Interceptor[]数组(我们只需将mybatis的自定义拦截器对象放入到Spring容器中)。后续放入了sqlSessionFactory中。

          但是项目中虽然自定义配置了sqlSessionFactory类,但却未设置factory.setPlugins(this.interceptors);。导致即使将自定义拦截器放入到Spring容器,但却不生效。

          解决方法,需要手动修改自定义的sqlSessionFactory类。

          3. 在mybatis-config.xml配置又放入Spring容器

          这种情况下,mybatis自定义拦截器会被执行两次。即在mybatis-config.xml配置的拦截器会通过反射的方式创建拦截器,放入Spring容器的拦截器也会被初始化。

          源码位置:org.mybatis.spring.SqlSessionFactoryBean#buildSqlSessionFactory

          protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
              Configuration configuration;
              ...读取属性中的plugins,即org.mybatis.spring.SqlSessionFactoryBean#setPlugins设置的。
              if (!isEmpty(this.plugins)) {
                  for (Interceptor plugin: this.plugins) {
                      configuration.addInterceptor(plugin);
                      if (LOGGER.isDebugEnabled()) {
                          LOGGER.debug("Registered plugin: '" + plugin + "'");
                      }
                  }
              }
              ...解析xml配置(通过反射创建拦截器对象)
              if (xmlConfigBuilder != null) {
                  try {
                      xmlConfigBuilder.parse();
                      if (LOGGER.isDebugEnabled()) {
                          LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
                      }
                  } catch(Exception ex) {
                      throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
                  } finally {
                      ErrorContext.instance().reset();
                  }
              }
              return this.sqlSessionFactoryBuilder.build(configuration);
          }

          最终会执行到:

          private void pluginElement(XNode parent) throws Exception {
              if (parent != null) {
                  for (XNode child: parent.getChildren()) {
                      String interceptor = child.getStringAttribute("interceptor");
                      Properties properties = child.getChildrenAsProperties();
                      //反射创建mybatis的插件。
                      Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
                      interceptorInstance.setProperties(properties);
                      configuration.addInterceptor(interceptorInstance);
                  }
              }
          }

          SpringBoot 自定义Mybatis拦截器

          开发过程中经常回需要对要执行的sql加以自定义处理,比如分页,计数等。通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

          @Intercepts({@Signature(type = Executor.class,method = "query",args = {MappedStatement.class,Object.class, RowBounds.class,ResultHandler.class})})
          public class MyPageInterceptor implements Interceptor {
              private static final Logger logger= LoggerFactory.getLogger(MyPageInterceptor.class);
              @Override
              public Object intercept(Invocation invocation) throws Throwable {
                  logger.warn(invocation.toString());
                  return invocation.proceed();
              }
              @Override
              public Object plugin(Object o) {
                  return Plugin.wrap(o,this);
              }
              @Override
              public void setProperties(Properties properties) {
                  logger.warn(properties.toString());
              }
          }

          我的配置

          mybatis:
            type-aliases-package: me.zingon.pagehelper.model
            mapper-locations: classpath:mapper/*.xml
            configuration:
              map-underscore-to-camel-case: true
              default-fetch-size: 100
              default-statement-timeout: 30

          在springboot中要给mybatis加上这个拦截器,有三种方法,前两种方法在启动项目时不会自动调用自定义拦截器的setProperties方法。

          第一种

          直接给自定义拦截器添加一个@Component注解,当调用sql时结果如下,可以看到拦截器生效了,但是启动时候并没有自动调用setProperties方法。

          第二种

          在配置类里添加拦截器,这种方法结果同上,也不会自动调用setProperties方法。

          @Configuration
          public class MybatisConfig {
              @Bean
              ConfigurationCustomizer mybatisConfigurationCustomizer() {
                  return new ConfigurationCustomizer() {
                      @Override
                      public void customize(org.apache.ibatis.session.Configuration configuration) {
                          configuration.addInterceptor(new MyPageInterceptor());
                      }
                  };
              }
          }

          第三种

          这种方法就是跟以前的配置方法类似,在yml配置文件中指定mybatis的xml配置文件,注意config-location属性和configuration属性不能同时指定

          mybatis:
            config-location: classpath:mybatis.xml
            type-aliases-package: me.zingon.pagehelper.model
            mapper-locations: classpath:mapper/*.xml
          <?xml version="1.0" encoding="UTF-8" ?>
          <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
                  "http://mybatis.org/dtd/mybatis-3-config.dtd">
          <configuration>
              <typeAliases>
                  <package name="me.zingon.pacargle.model"/>
              </typeAliases>
              <plugins>
                  <plugin interceptor="me.zingon.pagehelper.interceptor.MyPageInterceptor"> 
                      <property name="dialect" value="oracle"/>
                  </plugin>
              </plugins>
          </configuration>

          可以看到,在启动项目的时候setProperties被自动调用了

          前两种方法可以在初始化自定义拦截器的时候通过 @Value 注解直接初始化需要的参数。

          以上为个人经验,希望能给大家一个参考,也希望大家多多支持自由互联。

          【源文URL、http://www.yidunidc.com/hk.html 转载请保留出处】