拦截同一个方法的多个拦截器,代理的顺序是怎样的呢?
结论:拦截同一个方法的拦截器和我们在mybatis-config.xml文件中的顺序相反,而Spring容器配置的plugin最后执行方法。
mybatis自定义拦截器(一)基本使用
mybatis自定义拦截器(二)对象详解
SpringBoot整合Mybatis自定义拦截器不起作用解决方案!!!
1. plugin生效的两种方式
源码位置:mybatis的自动加载:org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration#sqlSessionFactory
public class MybatisAutoConfiguration {
private static Log log = LogFactory.getLog(MybatisAutoConfiguration.class);
//读取Spring容器中的所有的plugin插件
@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())) {
//读取mybatis-config.xml的地址
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
factory.setConfiguration(properties.getConfiguration());
//放入插件
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();
}
}
注意:SqlSessionFactoryBean
实现了InitializingBean
接口,在afterPropertiesSet()
方法中将执行org.mybatis.spring.SqlSessionFactoryBean#buildSqlSessionFactory
方法去读取ConfigLocation的xml文件,解析plugin并放入集合中。
结论:spring容器的plugin先放入到集合中,后续将
mybatis-config.xml
的plugin按先后顺序放入到集合中。
2. plugin切面执行的顺序
源码位置:org.apache.ibatis.plugin.InterceptorChain
该方法会对target代理,并且对代理类在进行代理。一层一层的增强target类,故越靠后的Interceptor越先执行。
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
public Object pluginAll(Object target) {
//将项目启动时注册的interceptors拿出来,执行plugin方法。
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);
}
}
而plugin方法:
@Slf4j
@Intercepts(
@Signature(type = Executor.class, method = "query", args = {
MappedStatement.class, Object.class, RowBounds.class,
ResultHandler.class }))
public class A1Interceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
log.info("A1 before...");
Object proceed = invocation.proceed();
log.info("A1 after...");
return proceed;
}
/**
* 将插件对象加入到拦截器链中
* @param target
* @return
*/
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
实际上会调用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);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
wrap方法会创建代理对象。

wrapper()
方法对target对象一层一层的代理。即before切面执行的顺序与放入plugins的顺序相反。
网友评论