我是靠谱客的博主 大方大炮,最近开发中收集的这篇文章主要介绍MyBatis的Mapper接口自动注册原理,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

通常我们使用mapper像下面这样

// 声明一个Mapper
public interface IUserRoleMapper {
    int batchInsert(@Param("list")List<UserRolePo> list);
}

// 实现类中使用
public class UserServiceImpl implement IUserService{
    // 注入userRoleMapper
    @Autowired
    private IUserRoleMapper userRoleMapper;
    
    @Override
    BasicResponse saveUserRoles(List<UserRolePo> list) {
        // 使用userRoleMapper
        return userRoleMapper.batchInsert(list) 
    }
}

如上所示的Mybatis的mapper接口,在定义之后,可以直接在service实现类中使用,那么这些Mapper是如何被加载的呢?

这个要从@MapperScan注解说起。一般我们在引入mybitas之后在对应的启动类上都需要带上@MapperScan注解,表明需要扫描哪个路径下里面的mapper接口,将其加载到mybatis中

@SpringBootApplication
@MapperScan("com.xxx.mapper") // 扫描com.xxx.mapper路径下所有的mapper
public class XxxApplication {

    public static void main(String[] args) {
        SpringApplication.run(XxxApplication.class, args);
    }
}

@MapperScan注解提供了一种使用Java配置的方式来注入mapper接口,功能与在xml文件中通过MapperScannerRegistrar配置MapperScannerConfigurer是一样的,因为这个注解使用了@Import注解导入了MapperScannerRegistrar

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class) // 注册mapper接口
@Repeatable(MapperScans.class)
public @interface MapperScan {
  String[] value() default {};
  String[] basePackages() default {};
  Class<?>[] basePackageClasses() default {};
  Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
  Class<? extends Annotation> annotationClass() default Annotation.class;
  Class<?> markerInterface() default Class.class;
  String sqlSessionTemplateRef() default "";
  String sqlSessionFactoryRef() default "";
  Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
  String lazyInitialization() default "";

}

MapperScannerRegistrar通过实现ImportBeanDefinitionRegistrar接口来注册MapperScannerConfigurer, MapperScannerRegistrar的实现如下:

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
      @Override
      public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
          // 获取@MapperScan的注解属性
        AnnotationAttributes mapperScanAttrs = AnnotationAttributes
            .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        if (mapperScanAttrs != null) {
            // 注册bean定义
          registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
              generateBaseBeanName(importingClassMetadata, 0));
        }
      }

      void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
          BeanDefinitionRegistry registry, String beanName) {

          // 先注册MapperScannerConfigurer
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
        builder.addPropertyValue("processPropertyPlaceHolders", true);

        Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
        if (!Annotation.class.equals(annotationClass)) {
          builder.addPropertyValue("annotationClass", annotationClass);
        }

        Class<?> markerInterface = annoAttrs.getClass("markerInterface");
        if (!Class.class.equals(markerInterface)) {
          builder.addPropertyValue("markerInterface", markerInterface);
        }

        Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
        if (!BeanNameGenerator.class.equals(generatorClass)) {
          builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
        }

        Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
        if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
          builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
        }

        String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
        if (StringUtils.hasText(sqlSessionTemplateRef)) {
          builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
        }

        String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
        if (StringUtils.hasText(sqlSessionFactoryRef)) {
          builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
        }

          // 设置注册mapper的路径
        List<String> basePackages = new ArrayList<>();
        basePackages.addAll(
            Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));

        basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
            .collect(Collectors.toList()));

        basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
            .collect(Collectors.toList()));

        if (basePackages.isEmpty()) {
          basePackages.add(getDefaultBasePackage(annoMeta));
        }

        String lazyInitialization = annoAttrs.getString("lazyInitialization");
        if (StringUtils.hasText(lazyInitialization)) {
          builder.addPropertyValue("lazyInitialization", lazyInitialization);
        }

        builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));

          // 注册MapperScannerConfigurer
        registry.registerBeanDefinition(beanName, builder.getBeanDefinition());

      }

      private static String generateBaseBeanName(AnnotationMetadata importingClassMetadata, int index) {
        return importingClassMetadata.getClassName() + "#" + MapperScannerRegistrar.class.getSimpleName() + "#" + index;
      }

      private static String getDefaultBasePackage(AnnotationMetadata importingClassMetadata) {
        return ClassUtils.getPackageName(importingClassMetadata.getClassName());
      }

}

MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,在该接口的postProcessBeanDefinitionRegistry方法中会初始化类路径扫描器ClassPathMapperScanner,通过该扫描器真正加载Mapper接口

public class MapperScannerConfigurer
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
      @Override
      public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {
          processPropertyPlaceHolders();
        }
        // 类路径Mapper扫描配置
        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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
        if (StringUtils.hasText(lazyInitialization)) {
          scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
        }
        scanner.registerFilters();
          // 扫描mapper接口
        scanner.scan(
            StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
      }
}

ClassPathMapperScanner继承了ClassPathBeanDefinitionScanner,他的doSacn方法中,首先调用父类的doScan方法根据basePackages获取到mapper接口的beanDefinitions,

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        // 获取mapper接口的beanDefinitions
        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 {
            // 处理beanDefinitions,加上mybatis相关的配置
            processBeanDefinitions(beanDefinitions);
        }
        return beanDefinitions;
    }
    
    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      String beanClassName = definition.getBeanClassName();
      LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
          + "' mapperInterface");
        // mapper接口是bean的原始类,但是实际的bean类是Mapper工厂bean(MapperFactoryBean)
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); 
        // MapperFactoryBean是工厂bean,可以通过getObject获取bean实例
      definition.setBeanClass(this.mapperFactoryBeanClass);

      definition.getPropertyValues().add("addToConfig", this.addToConfig);

        // 设置sqlSessionFactory和sqlSessionTemplate属性
      boolean explicitFactoryUsed = false;
      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;
      }

      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;
      }

      if (!explicitFactoryUsed) {
        LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
      definition.setLazyInit(lazyInitialization);
    }
  }
}

MapperFactoryBean是获取Mapper的关键工厂bean,他继承了SqlSessionDaoSupport,覆写了它的checkDaoConfig方法

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
      @Override
      protected void checkDaoConfig() {
          // 调用父类SqlSessionDaoSupport#checkDaoConfig
        super.checkDaoConfig();

        notNull(this.mapperInterface, "Property 'mapperInterface' is required");

          // 获取Mybatis的配置Configuration
        Configuration configuration = getSqlSession().getConfiguration();
        if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
          try {
              // 在配置中加入Mapper
            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();
          }
        }
      }
    
    // Spring启动时初始化bean实例,会调用这里
    @Override
    public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
    }
}

Configuration#addMapper是调用了MapperRegistry的addMapper方法

public class Configuration {
    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
    public <T> void addMapper(Class<T> type) {
        // 通过MapperRegistry#addMapper为mapper绑定MapperProxyFactory,MapperProxyFactory可以生产MapperProxy
        mapperRegistry.addMapper(type);
    }
}

MapperRegistry 中主要维护了knownMappers,他是mybatis所有mapper接口与对应MapperProxyFactory的映射器,通过这个map就可以生成MapperProxy

public class MapperRegistry {
      private final Configuration config;
      private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
    
      // 添加mapper,为mapper映射对应的MapperProxyFactory
      public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {
            // 重复加载同一个mapper时报错
          if (hasMapper(type)) {
            throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
          }
            
          boolean loadCompleted = false;
          try {
              // 为mapper绑定一个Mapper代理工厂MapperProxyFactory,他可以生产Mapper代理MapperProxy
            knownMappers.put(type, new MapperProxyFactory<>(type));
            // 为对应的mapper接口绑定XML中的SQL语句
            MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
            parser.parse();
            loadCompleted = true;
          } finally {
            if (!loadCompleted) {
              knownMappers.remove(type);
            }
          }
        }
      }
}

整个Mapper绑定MapperProxyFactory的加载过程可以归纳为:

@MapperScan注解 MapperScannerRegistrar MapperScannerConfigurer ClassPathMapperScanner MapperFactoryBean Configuration MapperRegistry 通过@Import注解导入 通过ImportBeanDefinitionRegistrar注册 注册Mapper接口 扫描basePackages注册Mapper的bean定义 转为MapperFactoryBean(Spring启动会实例化Bean) checkDaoConfig 向Configuration中addMapper addMapper addMapper addMapper addMapper为mapper绑定一个MapperProxyFactory @MapperScan注解 MapperScannerRegistrar MapperScannerConfigurer ClassPathMapperScanner MapperFactoryBean Configuration MapperRegistry

Mapper绑定MapperProxyFactory并不能直接生产Spring的Bean实例,还需要依赖Spring加载FactoryBean机制,才能生成Mapper对应的bean。由于上面在ClassPathMapperScanner#doScan这一步中将Mapper bean注册为MapperFactoryBean,它实现了FactoryBean接口,在Spring启动的时候会通过调用其getObject从而实例化对应的Bean,看一下这里的MapperFactoryBean#getObject

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    @Override
    public T getObject() throws Exception {
        // 通过this.sqlSessionTemplate.getMapper获取bean实例
        return getSqlSession().getMapper(this.mapperInterface);
    }
    
    public SqlSession getSqlSession() {
    return this.sqlSessionTemplate;
  }
}

this.sqlSessionTemplate.getMapper默认调用DefaultSqlSession#getMapper,其中的Configuration在上面已经初始化过

public class DefaultSqlSession implements SqlSession {

      private final Configuration configuration;
      @Override
      public <T> T getMapper(Class<T> type) {
        return configuration.getMapper(type, this);
      }
}

configuration.getMapper会接着调MapperRegistry#getMapper

public class Configuration {
    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
    
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
      return mapperRegistry.getMapper(type, sqlSession);
    }
}

MapperRegistry#getMapper通过MapperProxyFactory为mapper生成MapperProxy

public class MapperRegistry {
      private final Configuration config;
      private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

      // 获取mapper,通过MapperProxyFactory为mapper生成MapperProxy
      public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
         // 获取mapper对应的MapperProxyFactory
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        if (mapperProxyFactory == null) {
          throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        }
        try {
            // MapperProxyFactory生产实例
          return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
          throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
      }
}

MapperProxyFactory#newInstance主要是通过反射生成Mapper实例并为其绑定一个代理对象MapperProxy,这就是为什么调用Mapper的接口时会走到MapperProxy里面的原因

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
    
  public T newInstance(SqlSession sqlSession) {
      // 创建MapperProxy
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
      // 通过反射创建Mapper并绑定代理对象MapperProxy
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }
}

通过JDK动态代理生成Mapper实例并绑定代理MapperProxy的过程如下图

Spring MapperFactoryBean DefaultSqlSession Configuration MapperRegistry MapperProxyFactory MapperProxy FactoryBean.getObject getMapper getMapper getMapper newInstance newInstance:通过反射生成Mapper实例并绑定MapperProxy代理 创建MapperProxy代理实例 代理到Mapper Spring MapperFactoryBean DefaultSqlSession Configuration MapperRegistry MapperProxyFactory MapperProxy

最后

以上就是大方大炮为你收集整理的MyBatis的Mapper接口自动注册原理的全部内容,希望文章能够帮你解决MyBatis的Mapper接口自动注册原理所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(69)

评论列表共有 0 条评论

立即
投稿
返回
顶部