zoukankan      html  css  js  c++  java
  • mybatis专题(五)-----mybatis源码学习

    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 :发送者,发起请求的客户端;

    责任链模式优点

    • 降低耦合度。它将请求的发送者和接收者解耦。
    • 简化了对象。使得对象不需要知道链的结构。
    • 增强给对象指派职责的灵活性。通过改变链内 的成员或者调动它们的次序,允许动态地新增 或者删除责任。
    • 增加新的请求处理类很方便。

    mybatis整体流程总结

    初始化sqlsessionFactory

    openSession

    getMapper返回接口的代理对象 包含了SqlSession对象

    查询流程 

  • 相关阅读:
    eclipse下jsp文件报错解决方法
    使用vscode搭建本地的websocket
    tomcat的首次登录配置
    tomcat配置报错解决方法 The jre_home environment variable is not defined correctly
    cento升级openssl依旧显示老版本
    Centos6安装mysql5.7最新版
    Neutron服务组件
    网络OSI 7层模型
    Kubernetes的核心技术概念和API对象
    Xen 虚拟化技术
  • 原文地址:https://www.cnblogs.com/alimayun/p/12317987.html
Copyright © 2011-2022 走看看