zoukankan      html  css  js  c++  java
  • 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)
  • 相关阅读:
    poj 1579(动态规划初探之记忆化搜索)
    hdu 1133(卡特兰数变形)
    CodeForces 625A Guest From the Past
    CodeForces 625D Finals in arithmetic
    CDOJ 1268 Open the lightings
    HDU 4008 Parent and son
    HDU 4044 GeoDefense
    HDU 4169 UVALive 5741 Wealthy Family
    HDU 3452 Bonsai
    HDU 3586 Information Disturbing
  • 原文地址:https://www.cnblogs.com/yaojf/p/7598560.html
Copyright © 2011-2022 走看看