mybatis-插件详解
基本原理
使用了JDK动态代理,基于 interceptor
实现
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
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) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
//为目标对象target创建代理
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;
}
}
看下Interceptor
接口
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
//创建代理对象并返回
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
}
mybatis是如何使用的
创建 MybatisAutoConfiguration
的时候进行注入
@Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean {
private final MybatisProperties properties;
private final Interceptor[] interceptors;
private final TypeHandler[] typeHandlers;
private final List<ConfigurationCustomizer> configurationCustomizers;
private final List<SqlSessionFactoryBeanCustomizer> sqlSessionFactoryBeanCustomizers;
public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,
ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider,
ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,
ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider,
ObjectProvider<List<SqlSessionFactoryBeanCustomizer>> sqlSessionFactoryBeanCustomizers) {
this.properties = properties;
//通过构造器进行注入
this.interceptors = interceptorsProvider.getIfAvailable();
this.typeHandlers = typeHandlersProvider.getIfAvailable();
this.languageDrivers = languageDriversProvider.getIfAvailable();
this.resourceLoader = resourceLoader;
this.databaseIdProvider = databaseIdProvider.getIfAvailable();
this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
this.sqlSessionFactoryBeanCustomizers = sqlSessionFactoryBeanCustomizers.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
...............
//赋值到sqlSessionFactory中,当开启会话时,便可以进行拦截
if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}
//获取SqlSessionFactory
return factory.getObject();
}
}
获取SqlSessionFactory时,Configuration
初始化 interceptorChain
public class Configuration {
//实例化
protected final InterceptorChain interceptorChain = new InterceptorChain();
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
}
可见 ,插件作用于 ParameterHandler、ResultSetHandler、StatementHandler、Executor
这四种类型
拦截器责任链 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;
}
}
最后,开启会话session
的时候,就会进行增强
自定义插件
实现Interceptor接口,根据方法签名@Signature声明需要拦截的方法
@Intercepts({@Signature(
type= Executor.class, //上面说的四种类型之一
method = "query", //方法名称
args = {MappedStatement.class ,Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}), //方法参数
@Signature(
type= Executor.class,
method = "query",
args = {MappedStatement.class ,Object.class, RowBounds.class, ResultHandler.class})})
public class MyIntercepter implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 增强..................
return invocation.proceed();
}
}
注册到容器中
@Configuration
public class MybatisConfig {
@Bean
public Interceptor myInterceptor() {
return new MyIntercepter();
}
}
使用场景:分页,字段加密、自动赋值,监控等等。