spring集成Mybatis的原理分析
下载地址:https://github.com/mybatis/spring
1. SqlSessionFactoryBean源码分析
2. MapperFactoryBean源码分析
3. MapperScannerConfigurer源码分析
SqlSessionFactoryBean
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> { @Override //在spring容器中创建全局唯一的sqlSessionFactory public void afterPropertiesSet() throws Exception { notNull(dataSource, "Property 'dataSource' is required"); notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null), "Property 'configuration' and 'configLocation' can not specified with together"); this.sqlSessionFactory = buildSqlSessionFactory(); } protected SqlSessionFactory buildSqlSessionFactory() throws IOException { Configuration configuration; XMLConfigBuilder xmlConfigBuilder = null; if (this.configuration != null) {//如果configuration不为空,则使用该对象,并对其进行配置 configuration = this.configuration; if (configuration.getVariables() == null) { configuration.setVariables(this.configurationProperties); } else if (this.configurationProperties != null) { configuration.getVariables().putAll(this.configurationProperties); } } else if (this.configLocation != null) {//创建xmlConfigBuilder,读取mybatis的核心配置文件 xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties); configuration = xmlConfigBuilder.getConfiguration(); } else {//如果configuration为空,实例化一个configuration对象 if (LOGGER.isDebugEnabled()) { LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration"); } configuration = new Configuration(); if (this.configurationProperties != null) { configuration.setVariables(this.configurationProperties); } } //设置objectFactory if (this.objectFactory != null) { configuration.setObjectFactory(this.objectFactory); } //设置objectWrapperFactory if (this.objectWrapperFactory != null) { configuration.setObjectWrapperFactory(this.objectWrapperFactory); } //设置vfs if (this.vfs != null) { configuration.setVfsImpl(this.vfs); } //扫描指定的包typeAliasesPackage,注册别名 if (hasLength(this.typeAliasesPackage)) { String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packageToScan : typeAliasPackageArray) { configuration.getTypeAliasRegistry().registerAliases(packageToScan, typeAliasesSuperType == null ? Object.class : typeAliasesSuperType); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases"); } } } //为typeAliases指定的类注册别名 if (!isEmpty(this.typeAliases)) { for (Class<?> typeAlias : this.typeAliases) { configuration.getTypeAliasRegistry().registerAlias(typeAlias); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered type alias: '" + typeAlias + "'"); } } } //注册插件 if (!isEmpty(this.plugins)) { for (Interceptor plugin : this.plugins) { configuration.addInterceptor(plugin); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered plugin: '" + plugin + "'"); } } } //扫描指定的包typeHandlersPackage,注册类型解析器 if (hasLength(this.typeHandlersPackage)) { String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packageToScan : typeHandlersPackageArray) { configuration.getTypeHandlerRegistry().register(packageToScan); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers"); } } } //为typeHandlers指定的类注册类型解析器 if (!isEmpty(this.typeHandlers)) { for (TypeHandler<?> typeHandler : this.typeHandlers) { configuration.getTypeHandlerRegistry().register(typeHandler); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered type handler: '" + typeHandler + "'"); } } } //配置databaseIdProvider if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls try { configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource)); } catch (SQLException e) { throw new NestedIOException("Failed getting a databaseId", e); } } //配置缓存 if (this.cache != null) { configuration.addCache(this.cache); } //使用xmlConfigBuilder读取mybatis的核心配置文件 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(); } } //默认使用SpringManagedTransactionFactory作为事务管理器 if (this.transactionFactory == null) { this.transactionFactory = new SpringManagedTransactionFactory(); } //设置Environment configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource)); //根据mapperLocations的配置,处理映射配置文件以及相应的mapper接口 if (!isEmpty(this.mapperLocations)) { for (Resource mapperLocation : this.mapperLocations) { if (mapperLocation == null) { continue; } try { XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), configuration, mapperLocation.toString(), configuration.getSqlFragments()); xmlMapperBuilder.parse(); } catch (Exception e) { throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e); } finally { ErrorContext.instance().reset(); } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'"); } } } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found"); } } //最终使用sqlSessionFactoryBuilder创建sqlSessionFactory return this.sqlSessionFactoryBuilder.build(configuration); } }
MapperFactoryBean
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { private Class<T> mapperInterface; private boolean addToConfig = true; public MapperFactoryBean() { //intentionally empty } public MapperFactoryBean(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } /** * {@inheritDoc} */ @Override //MapperFactoryBean在容器初始化时,要确保mapper接口被注册到mapperRegistry protected void checkDaoConfig() { super.checkDaoConfig(); notNull(this.mapperInterface, "Property 'mapperInterface' is required"); // Configuration configuration = getSqlSession().getConfiguration();//通过SqlSession从容器中拿到configuration if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) { try { //如果mapperRegistry中不包含当前接口的动态代理工厂,则添加一个 configuration.addMapper(this.mapperInterface); } catch (Exception e) { logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e); throw new IllegalArgumentException(e); } finally { ErrorContext.instance().reset(); } } } /** * 通过在容器中的mapperRegistry,返回当前mapper接口的动态代理 * * {@inheritDoc} */ @Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); } }
MapperScannerConfigurer
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware { private String basePackage;//用于指定要扫描的包 private boolean addToConfig = true; private SqlSessionFactory sqlSessionFactory; private SqlSessionTemplate sqlSessionTemplate; private String sqlSessionFactoryBeanName; private String sqlSessionTemplateBeanName; private Class<? extends Annotation> annotationClass;//mapper接口上有指定的annotation才会被扫描 private Class<?> markerInterface;//mapper接口继承与指定的接口才会被扫描 private ApplicationContext applicationContext;//容器上下文 private String beanName; private boolean processPropertyPlaceHolders; private BeanNameGenerator nameGenerator; @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) {//占位符处理 processPropertyPlaceHolders(); } //实例化ClassPathMapperScanner,并对scanner相关属性进行配置 ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); scanner.setAddToConfig(this.addToConfig); scanner.setAnnotationClass(this.annotationClass); scanner.setMarkerInterface(this.markerInterface); scanner.setSqlSessionFactory(this.sqlSessionFactory); scanner.setSqlSessionTemplate(this.sqlSessionTemplate); scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName); scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName); scanner.setResourceLoader(this.applicationContext); scanner.setBeanNameGenerator(this.nameGenerator); scanner.registerFilters();//根据上述配置,生成过滤器,只扫描合条件的class //扫描指定的包以及其子包 scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); } }
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner { private boolean addToConfig = true; private SqlSessionFactory sqlSessionFactory; private SqlSessionTemplate sqlSessionTemplate; private String sqlSessionTemplateBeanName; private String sqlSessionFactoryBeanName; private Class<? extends Annotation> annotationClass; private Class<?> markerInterface; private MapperFactoryBean<?> mapperFactoryBean = new MapperFactoryBean<Object>(); public ClassPathMapperScanner(BeanDefinitionRegistry registry) { super(registry, false); } @Override public Set<BeanDefinitionHolder> doScan(String... basePackages) { //通过父类的扫描,获取所有复合条件的BeanDefinitionHolder对象 Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration."); } else { //处理扫描得到的BeanDefinitionHolder集合,将集合中的每一个mapper接口转换成MapperFactoryBean后,注册至spring容器 processBeanDefinitions(beanDefinitions); } return beanDefinitions; } //处理扫描得到的BeanDefinitionHolder集合,将集合中的每一个mapper接口转换成MapperFactoryBean后,注册至spring容器 private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; //遍历集合 for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); if (logger.isDebugEnabled()) { logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface"); } // the mapper interface is the original class of the bean // but, the actual class of the bean is MapperFactoryBean //将添加扫描到的接口类型作为构造函数的入参 definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59 //讲bean的类型转换成mapperFactoryBean definition.setBeanClass(this.mapperFactoryBean.getClass()); //增加addToConfig属性 definition.getPropertyValues().add("addToConfig", this.addToConfig); boolean explicitFactoryUsed = false; //增加sqlSessionFactory属性 if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) { definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionFactory != null) { definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory); explicitFactoryUsed = true; } //增加sqlSessionTemplate属性 if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) { if (explicitFactoryUsed) { logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionTemplate != null) { if (explicitFactoryUsed) { logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); explicitFactoryUsed = true; } //修改自动注入的方式 bytype if (!explicitFactoryUsed) { if (logger.isDebugEnabled()) { logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."); } definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } } } }
插件开发
概述
插件是用来改变或者扩展mybatis的原有的功能,mybaits的插件就是通过继承Interceptor拦截器实现的;在没 有完全理解插件之前禁止使用插件对mybaits进行扩展,又可能会导致严重的问题;
mybatis中能使用插件进行拦截的接口和方法如下:
1、Executor(update、query 、 flushStatment 、 commit 、 rollback 、 getTransaction 、 close 、 isClose)
2、StatementHandler(prepare 、 paramterize 、 batch 、 update 、 query)
3、ParameterHandler( getParameterObject 、 setParameters )
4、ResultSetHandler( handleResultSets 、 handleCursorResultSets 、 handleOutputParameters )
插件开发快速入门
定义一个阈值,当查询操作运行时间超过这个阈值记录日志供运维人员定位慢查询,插件实现步骤:
1. 实现Interceptor接口方法
2. 确定拦截的签名
3. 在配置文件中配置插件
4. 运行测试用例
ThresholdInterceptor:
import java.sql.Statement; import java.util.Properties; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.resultset.ResultSetHandler; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.logging.jdbc.PreparedStatementLogger; import org.apache.ibatis.logging.jdbc.StatementLogger; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Plugin; import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; @Intercepts({ //拦截StatementHandler的query方法,并且将参数类型配置在args中 @Signature(type=StatementHandler.class,method="query",args={Statement.class, ResultHandler.class}) // @Signature(type=StatementHandler.class,method="query",args={MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class}) }) public class ThresholdInterceptor implements Interceptor { private long threshold; @Override public Object intercept(Invocation invocation) throws Throwable { long begin = System.currentTimeMillis(); Object ret = invocation.proceed(); long end=System.currentTimeMillis(); long runTime = end - begin; if(runTime>=threshold){ Object[] args = invocation.getArgs(); Statement stat = (Statement) args[0]; MetaObject metaObjectStat = SystemMetaObject.forObject(stat); PreparedStatementLogger statementLogger = (PreparedStatementLogger)metaObjectStat.getValue("h"); Statement statement = statementLogger.getPreparedStatement(); System.out.println("sql语句:“"+statement.toString()+"”执行时间为:"+runTime+"毫秒,已经超过阈值!"); } return ret; } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { this.threshold = Long.valueOf(properties.getProperty("threshold")); } }
mybatis-config.xml
<plugins> <plugin interceptor="com.enjoylearning.mybatis.Interceptors.ThresholdInterceptor"> <property name="threshold" value="10"/> </plugin> </plugins>
Intercepor接口:
public interface Interceptor { //执行拦截逻辑的方法 Object intercept(Invocation invocation) throws Throwable; //target是被拦截的对象,它的作用就是给被拦截的对象生成一个代理对象 Object plugin(Object target); //读取在plugin中设置的参数 void setProperties(Properties properties); }
责任链模式
责任链模式:就是把一件工作分别经过链上的各个节点,让这些节点依次处理这个工作;和装饰器模式不同, 每个节点都知道后继者是谁;适合为完成同一个请求需要多个处理类的场景;
要素分析
Handler:定义了一个处理请求的标准接口;
ConcreteHandler:具体的处理者,处理它负 责的部分,根据业务可以结束处理流程,也可 以将请求转发给它的后继者;
client :发送者,发起请求的客户端;
责任链模式优点
- 降低耦合度。它将请求的发送者和接收者解耦。
- 简化了对象。使得对象不需要知道链的结构。
- 增强给对象指派职责的灵活性。通过改变链内 的成员或者调动它们的次序,允许动态地新增 或者删除责任。
- 增加新的请求处理类很方便。