• Mybatis Annotation使用小结


    Mybatis Annotation使用小结

    之前一直有看过mybatis的注解使用方式,但没有去看过它的原理。今天看springboot-mybatis-annotation使用的时候,debug了下看了它的加载过程。

    1. 先编写一个示例接口

      @Mapper // 标志为 Mybatis 的 Mapper
      public interface CityDao {
      
          /**
           * 根据城市名称,查询城市信息
           *
           * @param cityName 城市名
           */
          @Select("SELECT * FROM city")
          // 返回 Map 结果集
          @Results({
                  @Result(property = "id", column = "id"),
                  @Result(property = "provinceId", column = "province_id"),
                  @Result(property = "cityName", column = "city_name"),
                  @Result(property = "description", column = "description"),
          })
          City findByName(@Param("cityName") String cityName);
      }
      
    2. springboot引入mybatis-spring-boot-starter依赖

      <dependency>
      			<groupId>org.mybatis.spring.boot</groupId>
      			<artifactId>mybatis-spring-boot-starter</artifactId>
      			<version>${mybatis-spring-boot}</version>
      </dependency>
      

      它会自动注入MybatisAutoConfiguration这个bean然后解析执行AutoConfiguredMapperScannerRegistrar这个ImportBeanDefinitionRegistrar,它的registerBeanDefinitions方法会扫描包把带有Mapper注解的接口注入到spring容器,并且在内部修改BeanDefinition的class为MapperFactoryBean,并且设置它的自动注入模式为AUTOWIRE_BY_TYPE,实现了SqlSessionFactory的自动注入。

      @Override
          public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      
            logger.debug("Searching for mappers annotated with @Mapper");
      
            ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
      
            try {
              if (this.resourceLoader != null) {
                scanner.setResourceLoader(this.resourceLoader);
              }
      
              List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
              if (logger.isDebugEnabled()) {
                for (String pkg : packages) {
                  logger.debug("Using auto-configuration base package '{}'", pkg);
                }
              }
      
              scanner.setAnnotationClass(Mapper.class);
              scanner.registerFilters();
              scanner.doScan(StringUtils.toStringArray(packages));
            } catch (IllegalStateException ex) {
              logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
            }
          }
      
    3. MapperFactoryBean的实例化

      MapperFactoryBean继承了SqlSessionDaoSupport,SqlSessionDaoSupport继承了DaoSupport,DaoSupport它实现了InitializingBean,所以实例化MapperFactoryBean的时候会调用afterPropertiesSet方法。
      
      	   @Override
      		public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
      			// Let abstract subclasses check their configuration.
      			checkDaoConfig();
      	
      			// Let concrete implementations initialize themselves.
      			try {
      				initDao();
      			}
      			catch (Exception ex) {
      				throw new BeanInitializationException("Initialization of DAO failed", ex);
      			}
      		}
      

      这里会调用MapperFactoryBean的重写方法checkDaoConfig

        protected void checkDaoConfig() {
          super.checkDaoConfig();
      
          notNull(this.mapperInterface, "Property 'mapperInterface' is required");
      
          Configuration configuration = getSqlSession().getConfiguration();
          if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
            try {
              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();
            }
          }
        }
      
    	  因为配置了方法注解所以我们在不配置xml的时候configuration里并没有对应的mapperRegistry,所以会调用configuration的addMapper方法,内部会调用mapperRegistry的addMapper方法
    	  
    	  
    	```
    	  public <T> void addMapper(Class<T> type) {
    	    if (type.isInterface()) {
    	      if (hasMapper(type)) {
    	        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
    	      }
    	      boolean loadCompleted = false;
    	      try {
    	        knownMappers.put(type, new MapperProxyFactory<T>(type));
    	        // It's important that the type is added before the parser is run
    	        // otherwise the binding may automatically be attempted by the
    	        // mapper parser. If the type is already known, it won't try.
    	        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
    	        parser.parse();
    	        loadCompleted = true;
    	      } finally {
    	        if (!loadCompleted) {
    	          knownMappers.remove(type);
    	        }
    	      }
    	    }
    	  }
    	```
    		
    	从以上代码可知通过MapperAnnotationBuilder这个类去解析当前接口的方法上的配置信息转化成MappedStatement并设置到全局的Configuration里面。这样就和直接解析xml配置达到了一样的效果。
    	
    4.	MapperFactoryBean生成的动态代理bean的调用
    	
    	MapperFactoryBean生成的动态代理bean,调用方法的过程最终会委派给MapperProxy的invoke方法,后续的调用过程就和xml解析出来的配置一致了,也就是说不管是注解配置还是xml配置只是解析配置的方式不同,最终都会设置到Configuration里面。然而调用过程一致。
    	
    ---
    代码下载参考[springboot-learning-example](https://github.com/yaojf/springboot-learning-example)
  • 相关阅读:
    从零到一k8s(五)网络模型讲解(cilium,calico,flannel)
    从零到一k8s(六)集群管理深入
    从零到一k8s(九)devops&&cicd
    1kvm理论
    类和方法
    方法定义和调用
    2kvm安装
    Redis6.2.6源码CLion编译调试
    ubuntu16.04 nginx创建自签名SSL证书
    MySQL性能优化之参数配置
  • 原文地址:https://www.cnblogs.com/yaojf/p/7598560.html
Copyright © 2020-2023  润新知