SpringBoot整合Mybatis自定义拦截器不起作用
Mybatis插件生效的方式:
1. 原始的读取mybatis-config.xml文件
该方式和Spring无关,是通过反射的形式创建插件对象,此时会执行org.apache.ibatis.plugin.Interceptor#setProperties方法,以读取配置参数。
1
2
3
4
5
|
mybatis: mapper-locations: classpath*:/mapping/*.xml type-aliases-package: com.tellme.pojo #读取全局配置的地址 config-location: classpath:mybatis-config.xml |
在resource目录下配置mybatis的全局配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<? 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
@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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
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); } |
最终会执行到:
1
2
3
4
5
6
7
8
9
10
11
12
|
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 接口,并指定想要拦截的方法签名即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@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()); } } |
我的配置
1
2
3
4
5
6
7
|
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方法。
1
2
3
4
5
6
7
8
9
10
11
12
|
@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属性不能同时指定
1
2
3
4
|
mybatis: config-location: classpath:mybatis.xml type-aliases-package: me.zingon.pagehelper.model mapper-locations: classpath:mapper/*.xml |
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<? 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 注解直接初始化需要的参数。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。
原文链接:https://www.jianshu.com/p/5fddb7f67108