千家信息网

Mybatis-spring自动注入机制原理

发表于:2025-01-31 作者:千家信息网编辑
千家信息网最后更新 2025年01月31日,这篇文章主要介绍"Mybatis-spring自动注入机制原理",在日常操作中,相信很多人在Mybatis-spring自动注入机制原理问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希
千家信息网最后更新 2025年01月31日Mybatis-spring自动注入机制原理

这篇文章主要介绍"Mybatis-spring自动注入机制原理",在日常操作中,相信很多人在Mybatis-spring自动注入机制原理问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"Mybatis-spring自动注入机制原理"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

一、基础背景

本篇文章的主要目的有两个:

  • 项目中使用mybatis,我们只定义了mapper的接口,并没有实现,那这些实现类的bean是如何生成的?

  • mapper接口的实例bean是如何注入到spring容器的?

包名版本号
spring-boot2.1.9.RELEASE
mybatis-spring2.0.0
mybatis-plus-boot-starter3.1.0
mybatis3.5.0

二、源码分析

项目中我们使用@MapperScan来配置mapper接口的路径,如下图

@EnableTransactionManagement@Configuration@MapperScan("com.ke.newhouse.agency.project.dao.mapper")public class MybatisPlusConfiguration {     @Bean    public PaginationInterceptor paginationInterceptor() {        return new PaginationInterceptor();    }}

在这些mapper路径下都是我们定义的接口,@MapperScan的作用有哪些呢,我们先看下@MapperScan的定义

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(MapperScannerRegistrar.class)@Repeatable(MapperScans.class)public @interface MapperScan {}

在MapperScan的定义中我们目前只需要关注@Import(MapperScannerRegistrar.class),其含义是将MapperScannerRegistrar.class引入到spring容器中

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {   private ResourceLoader resourceLoader;  /**   * {@inheritDoc}   */  @Override  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {    AnnotationAttributes mapperScanAttrs = AnnotationAttributes        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));    if (mapperScanAttrs != null) {      registerBeanDefinitions(mapperScanAttrs, registry);    }  }   void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry) {     ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);     // this check is needed in Spring 3.1    Optional.ofNullable(resourceLoader).ifPresent(scanner::setResourceLoader);     Class annotationClass = annoAttrs.getClass("annotationClass");    if (!Annotation.class.equals(annotationClass)) {      scanner.setAnnotationClass(annotationClass);    }     Class markerInterface = annoAttrs.getClass("markerInterface");    if (!Class.class.equals(markerInterface)) {      scanner.setMarkerInterface(markerInterface);    }     Class generatorClass = annoAttrs.getClass("nameGenerator");    if (!BeanNameGenerator.class.equals(generatorClass)) {      scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));    }     Class mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {      scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));    }     scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));    scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));     List 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()));     scanner.registerFilters();    scanner.doScan(StringUtils.toStringArray(basePackages));  }}

可以看到MapperScannerRegistrar是继承于ImportBeanDefinitionRegistrar接口,在spring中ImportBeanDefinitionRegistrar的也是创建bean的一种方式,它首先会定义好bean的一些基础信息,如beanName, class等等,然后在spring启动的时候调用其registerBeanDefinitions方法,来生成和注册bean。

我们直接看scanner.doScan(StringUtils.toStringArray(basePackages));这行的工作,最终会调用下面的方法 ClassPathMapperScanner

private void processBeanDefinitions(Set beanDefinitions) {    GenericBeanDefinition definition;    for (BeanDefinitionHolder holder : beanDefinitions) {      definition = (GenericBeanDefinition) holder.getBeanDefinition();      //这里的beanClassName是mapper接口的接口名(第一个字母小写)      String beanClassName = definition.getBeanClassName();      LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName()          + "' and '" + beanClassName + "' mapperInterface");       // the mapper interface is the original class of the bean      // but, the actual class of the bean is MapperFactoryBean      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59      //这行很重要,表示bean的实际class为MapperFactoryBean      definition.setBeanClass(this.mapperFactoryBean.getClass());       definition.getPropertyValues().add("addToConfig", this.addToConfig);       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() + "'.");        //将该bean设置成按类型自动装配,目的在该bean中如果有依赖其他bean,会按类型自动注入到该bean中        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);      }    }  }

我们再看MapperFactoryBean类

public class MapperFactoryBean extends SqlSessionDaoSupport implements FactoryBean { private Class mapperInterface; private boolean addToConfig = true; public MapperFactoryBean() {   //intentionally empty }   public MapperFactoryBean(Class mapperInterface) {   this.mapperInterface = mapperInterface; } /**  * {@inheritDoc}  */ @Override protected void checkDaoConfig() {   super.checkDaoConfig();   notNull(this.mapperInterface, "Property 'mapperInterface' is required");   //父类中的属性SqlSessionTemplate,该字段是在上面讲的自动装配中注入的   Configuration configuration = getSqlSession().getConfiguration();   if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {     try {       //将该mapper接口添加到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();     }   } } /**  * {@inheritDoc}  */ @Override public T getObject() throws Exception {   return getSqlSession().getMapper(this.mapperInterface); }··········}

MapperFactoryBean的父类是SqlSessionDaoSupport,同时实现了接口FactoryBean

我们先分析SqlSessionDaoSupport。

使得MapperFactoryBean拥有了SqlSessionTemplate属性,而同时SqlSessionDaoSupport继承于DaoSupport,DaoSupport是InitializingBean的一个抽象类并实现了afterPropertiesSet方法,该方法使得在bean生成时执行一些操作checkDaoConfig。在checkDaoConfig方法中会将该mapper接口添加到MapperRegistry中去,MapperRegistry可以看作是一个mapper的注册中心

MapperRegistry

public class MapperRegistry {   private final Configuration config;  //保存mapper接口与代理工厂类的映射  private final Map, MapperProxyFactory> knownMappers = new HashMap<>();   public MapperRegistry(Configuration config) {    this.config = config;  }   @SuppressWarnings("unchecked")  public  T getMapper(Class type, SqlSession sqlSession) {    final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);    if (mapperProxyFactory == null) {      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");    }    try {      return mapperProxyFactory.newInstance(sqlSession);    } catch (Exception e) {      throw new BindingException("Error getting mapper instance. Cause: " + e, e);    }  }   public  boolean hasMapper(Class type) {    return knownMappers.containsKey(type);  }   public  void addMapper(Class type) {    if (type.isInterface()) {      if (hasMapper(type)) {        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");      }      boolean loadCompleted = false;      try {        //将mapper接口与代理工厂类对应起来        knownMappers.put(type, new MapperProxyFactory<>(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);        }      }    }  }   /**   * @since 3.2.2   */  public Collection> getMappers() {    return Collections.unmodifiableCollection(knownMappers.keySet());  }   /**   * @since 3.2.2   */  public void addMappers(String packageName, Class superType) {    ResolverUtil> resolverUtil = new ResolverUtil<>();    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);    Set>> mapperSet = resolverUtil.getClasses();    for (Class mapperClass : mapperSet) {      addMapper(mapperClass);    }  }   /**   * @since 3.2.2   */  public void addMappers(String packageName) {    addMappers(packageName, Object.class);  } }

MapperProxyFactory是MapperProxy的工厂类,MapperProxy是Mapper的代理类

MapperProxy

public class MapperProxy implements InvocationHandler, Serializable {   private static final long serialVersionUID = -6424540398559729838L;  private final SqlSession sqlSession;  private final Class mapperInterface;  private final Map methodCache;   public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {    this.sqlSession = sqlSession;    this.mapperInterface = mapperInterface;    this.methodCache = methodCache;  }   @Override  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    try {      if (Object.class.equals(method.getDeclaringClass())) {        return method.invoke(this, args);      } else if (isDefaultMethod(method)) {        return invokeDefaultMethod(proxy, method, args);      }    } catch (Throwable t) {      throw ExceptionUtil.unwrapThrowable(t);    }    //使用缓存来保存MapperMethod    final MapperMethod mapperMethod = cachedMapperMethod(method);    //最终会调用这里来执行mapper中的方法    return mapperMethod.execute(sqlSession, args);  }   private MapperMethod cachedMapperMethod(Method method) {    return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));  }   private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)      throws Throwable {    final Constructor constructor = MethodHandles.Lookup.class        .getDeclaredConstructor(Class.class, int.class);    if (!constructor.isAccessible()) {      constructor.setAccessible(true);    }    final Class declaringClass = method.getDeclaringClass();    return constructor        .newInstance(declaringClass,            MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED                | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)        .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);  }  }

我们在接着分析MapperFactoryBean的另一个功能接口FactoryBean,实现FactoryBean这个接口使得MapperFactoryBean成为了一个工厂bean,它的功能是:从spring容器中获取该工厂bean的具体实例是通过getObject方法

MapperFactoryBean

public T getObject() throws Exception {    return getSqlSession().getMapper(this.mapperInterface);  }  ···· public  T getMapper(Class type) {    return getConfiguration().getMapper(type, this);  } ···public  T getMapper(Class type, SqlSession sqlSession) {    return mapperRegistry.getMapper(type, sqlSession);  } ···public  T getMapper(Class type, SqlSession sqlSession) {    final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);    if (mapperProxyFactory == null) {      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");    }    try {      return mapperProxyFactory.newInstance(sqlSession);    } catch (Exception e) {      throw new BindingException("Error getting mapper instance. Cause: " + e, e);    }  } ··· public T newInstance(SqlSession sqlSession) {    final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);    return newInstance(mapperProxy);  }

可以看到最终从spring中获取mapper的实例的时候,是获取mapper的代理类

三、类UML图

四、结论

  • 使用mybaits,其中mapper接口最终的实例是MapperProxy生成的代理类

  • 通过ImportBeanDefinitionRegistrar这种方式来注入到spring容器中

到此,关于"Mybatis-spring自动注入机制原理"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

0