MyBatis和Spring整合的奥秘

本篇博客源码分析基于Spring 5.1.16.RELEASE,mybatis-spring 2.0.0,较高版本的mybatis-spring源码有较大区别。

Spring之所以是目前Java最受欢迎的框架,几乎所有的Java项目都在使用,就是因为它良好的生态,很多技术可以与之整合,为什么其他技术可以和Spring相整合,就是因为Spring拥有很多扩展点,阅读Spring源码,有一部分原因就是有必要清楚的知道Spring提供了哪些扩展点,而怎么合理的利用这些扩展点,就需要了解其他技术是如何利用这些扩展点的。

今天我就来带着大家看下,国内最流行的数据库框架MyBatis是如何利用Spring的扩展点的,从而双剑合璧,让Spring+MyBatis成为国内最流行的技术搭配。

前置知识

为了后面的故事可以顺利展开,很有必要先给大家介绍下,阅读mybatis-spring源码的前置知识,没有这些前置知识阅读mybatis-spring源码是寸步难行。

mybatis-spring使用

因为现在有了SpringBoot,所以Mybatis和Spring的整合变得非常简单,但是如果没有SpringBoot,该怎么整合呢?我翻阅了百度的前几页,不知道是不是搜索关键词问题,几乎全是用XML的方式去整合Mybatis和Spring的,零XML配置,它不香吗?

代码结构:

MyBatis和Spring整合的奥秘

具体实现:

 <dependencies>
        <dependency>
            <groupid>org.mybatis</groupid>
            <artifactid>mybatis-spring</artifactid>
            <version>2.0.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
        <groupid>org.springframework</groupid>
            <artifactid>spring-context</artifactid>
            <version>5.1.16.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupid>org.mybatis</groupid>
            <artifactid>mybatis</artifactid>
            <version>3.4.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupid>org.springframework</groupid>
            <artifactid>spring-jdbc</artifactid>
            <version>5.1.10.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupid>mysql</groupid>
            <artifactid>mysql-connector-java</artifactid>
            <version>6.0.5</version>
        </dependency>
    </dependencies>
@MapperScan("com.codebear.mapper")
@ComponentScan
public class AppConfig {
    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        return factoryBean.getObject();
    }
}
@Repository
public interface StudentMapper {
    @Select("select * from student")
    List<student> getList();
}
</student>
public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println(applicationContext.getBean(StudentMapper.class).getList());
    }
}

运行结果:

[Student{id=1, name='&#x75AB;&#x82D7;&#x738B;', age=20}, Student{id=2, name='&#x963F;&#x4FEE;&#x7F57;&#x72EC;&#x89D2;&#x4ED9;', age=18}, Student{id=3, name='&#x5730;&#x5E95;&#x738B;', age=18}]

Import注解

如果我们想把一个类注册到Spring容器中,可以采用的方法有很多,其中一种是利用Import注解,Import注解有三种用法,mybatis-spring利用的是其中一种用法,Import了ImportBeanDefinitionRegistrar类,所以我们这里只看Import ImportBeanDefinitionRegistrar。

如何使用
public class MyBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        System.out.println(annotationMetadata.getAnnotationTypes());
    }
}

写一个类实现ImportBeanDefinitionRegistrar ,重写其中的registerBeanDefinitions方法。

@Import(MyBeanDefinitionRegistrar.class)
@ComponentScan
@MapperScan("com.codebear.mapper")
public class AppConfig {
    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        return factoryBean.getObject();
    }
}

在配置上加上@Import注解,写上刚才写的MyBeanDefinitionRegistrar类。

运行结果:

[org.springframework.context.annotation.Import, org.springframework.context.annotation.ComponentScan, org.mybatis.spring.annotation.MapperScan]

从registerBeanDefinitions两个入参的命名来看,第一个参数,Spring把注解元数据给你了,而第二个参数,Spring是直接把beanDefinition的注册器给你了。

追本溯源

下面我们来看看Spring在什么时候处理@Import注解的,又是什么时候调用registerBeanDefinitions方法的,当然这里不是Spring源码分析,我不会详细一行行翻译,而是简单的找到源头。

//AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class<?>... componentClasses)
    public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
        this();
        register(componentClasses);
        refresh();
    }

进入第三行的refresh()方法。

refresh方法做了很多事情,我们只需要关心invokeBeanFactoryPostProcessors方法:

//AbstractApplicationContext#refresh
invokeBeanFactoryPostProcessors(beanFactory);

执行invokeBeanFactoryPostProcessors方法,顾名思义,这个方法是执行BeanFactoryPostProcessor的。什么,你不知道什么是BeanFactoryPostProcessor?你可以简单的理解为Spring遵循插件化式的开发,其中有一个插件叫ConfigurationClassPostProcessor,实现了BeanDefinitionRegistryPostProcessor,同时BeanDefinitionRegistryPostProcessor又实现了BeanFactoryPostProcessor,通过ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry的方法,Spring完成了扫描。

//PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

这一步传入了BeanDefinitionRegistryPostProcessor的集合,要执行BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,集合有一个元素是我们关心的,就是上面提到的ConfigurationClassPostProcessor。

//PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
            postProcessor.postProcessBeanDefinitionRegistry(registry);
}

循环传入的BeanDefinitionRegistryPostProcessor集合,调用postProcessBeanDefinitionRegistry方法,我们直接进入到ConfigurationClassPostProcessor的processConfigBeanDefinitions方法,找到关键解析代码:

//ConfigurationClassPostProcessor#processConfigBeanDefinitions
parser.parse(candidates);
//ConfigurationClassParser#parse
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
//ConfigurationClassParser#processConfigurationClass
doProcessConfigurationClass(configClass, sourceClass);
//ConfigurationClassParser#doProcessConfigurationClass
processImports(configClass, sourceClass, getImports(sourceClass), true);

重点来了,终于找到了我们的目标:处理@Import注解。

//ConfigurationClassParser#processImports
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
//ConfigurationClass#addImportBeanDefinitionRegistrar
this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);

这个importBeanDefinitionRegistrars就是一个Map:

//ConfigurationClass
private final Map<importbeandefinitionregistrar, annotationmetadata> importBeanDefinitionRegistrars = new LinkedHashMap<>();
</importbeandefinitionregistrar,>

让我们就监视下configClass:

MyBatis和Spring整合的奥秘
可以看到我们写的MyBeanDefinitionRegistrar被放入了importBeanDefinitionRegistrars ,我们需要记住这个集合,至于还有一个什么,这里不用关心,当然,聪明的小伙伴肯定知道这是什么了。

我们写的MyBeanDefinitionRegistrar只是被放入了一个Map,并没有执行,下面我们要找找它是在哪里执行的。

我们需要回到ConfigurationClassPostProcessor的processConfigBeanDefinitions方法:

//ConfigurationClassPostProcessor#processConfigBeanDefinitions
this.reader.loadBeanDefinitions(configClasses);
//ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
//ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());

这个集合是不是有点眼熟,就是我在上面让大家记住的集合,这个集合就存放着我们的写的MyBeanDefinitionRegistrar类,让我们继续点进去:

//ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars
    private void loadBeanDefinitionsFromRegistrars(Map<importbeandefinitionregistrar, annotationmetadata> registrars) {
        registrars.forEach((registrar, metadata) ->
                registrar.registerBeanDefinitions(metadata, this.registry));
    }
</importbeandefinitionregistrar,>

循环传入的ImportBeanDefinitionRegistrar集合,调用registerBeanDefinitions方法,我的天,终于找到执行方法了。

FactoryBean

Spring就像是一个魔术师的袋子,而FactoryBean就是被魔术师装进袋子的香蕉,当魔术师打开袋子,发现香蕉变成鸽子了。

如何使用
@Component
public class MyFactoryBean implements FactoryBean<teacher> {
    public Teacher getObject() {
        Teacher teacher = new Teacher();
        teacher.setName("&#x7426;&#x7389;&#x8001;&#x5E08;");
        return teacher;
    }

    public Class<?> getObjectType() {
        return Teacher.class;
    }
}
</teacher>
public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println(applicationContext.getBean(MyFactoryBean.class));
        System.out.println(applicationContext.getBean(Teacher.class));

        System.out.println(applicationContext.getBean("&myFactoryBean"));
        System.out.println(applicationContext.getBean("myFactoryBean"));

        System.out.println(applicationContext.getBean("myFactoryBean").hashCode());
        System.out.println(applicationContext.getBean("myFactoryBean").hashCode());
    }
}

运行结果:

com.codebear.MyFactoryBean@4ee203eb
Teacher{name='&#x7426;&#x7389;&#x8001;&#x5E08;'}
com.codebear.MyFactoryBean@4ee203eb
Teacher{name='&#x7426;&#x7389;&#x8001;&#x5E08;'}
442125849
442125849

可以很清楚的看到从FactoryBean里面又生产出了一个Bean,生产出来的Bean就是FactoryBean中getObject方法返回的。

追本溯源

和上面一样,我们也要看看FactoryBean中的getObject是在哪里执行的,我们先来做个试验:

我们在getObject里面加上一句打印的代码:

@Component
public class MyFactoryBean implements FactoryBean<teacher> {
    public Teacher getObject() {
        System.out.println("getObject");
        Teacher teacher = new Teacher();
        teacher.setName("&#x7426;&#x7389;&#x8001;&#x5E08;");
        return teacher;
    }

    public Class<?> getObjectType() {
        return Teacher.class;
    }
}
</teacher>

然后只保留main方法中的创建ApplicationContext方法:

public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    }
}

运行后,你会发现,控制台没有任何输出,我们大胆的猜想,FactoryBean生产出来的Bean并不是预先加载的,而是采用懒加载的机制,也就是只有需要,才会去加载。

我们继续改下main方法:

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println(applicationContext.getBean(Teacher.class));
    }

运行结果:

getObject
Teacher{name='&#x7426;&#x7389;&#x8001;&#x5E08;'}

所以我们的猜想是正确的,这次入口是getBean。

下面还是枯燥无味的寻找,这次的寻找之旅更复杂:

//org.springframework.beans.factory.support.DefaultListableBeanFactory#getBean(java.lang.Class<t>)
public <t> T getBean(Class<t> requiredType) throws BeansException {
        return getBean(requiredType, (Object[]) null);
}
</t></t></t>
// org.springframework.beans.factory.support.DefaultListableBeanFactory#getBean(java.lang.Class<t>, java.lang.Object...)
Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);
</t>
//org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveBean
NamedBeanHolder<t> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
</t>
//org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveNamedBean(org.springframework.core.ResolvableType, java.lang.Object[], boolean)
String[] candidateNames = getBeanNamesForType(requiredType);
//org.springframework.beans.factory.support.DefaultListableBeanFactory#getBeanNamesForType(org.springframework.core.ResolvableType)
return getBeanNamesForType(resolved, true, true);
//org.springframework.beans.factory.support.DefaultListableBeanFactory#getBeanNamesForType(java.lang.Class<?>, boolean, boolean)
resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);

这个方法里面有一步是循环beanDefinitionNames,当循环到myFactoryBean的时候,判断这是一个FactoryBean:

boolean isFactoryBean = isFactoryBean(beanName, mbd);

随后执行isTypeMatch(beanName, type)方法:

//org.springframework.beans.factory.support.AbstractBeanFactory#isTypeMatch(java.lang.String, org.springframework.core.ResolvableType)
Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);
//org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getTypeForFactoryBean
return factoryBean.getObjectType();

当执行到这里,我们写的MyFactoryBean的getObjectType方法被调用了,返回Teacher.class,而我们现在要找的也是Teacher.class,所以匹配。

随后回到DefaultListableBeanFactory#doGetBeanNamesForType,把beanName放入一个集合中:

if (matchFound) {
    result.add(beanName);
}

随后返回集合。

再回到DefaultListableBeanFactory#resolveNamedBean,会判断返回出来的集合的元素的个数,显然只返回一个,执行

//org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveNamedBean
        if (candidateNames.length == 1) {
            String beanName = candidateNames[0];
            return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
        }

继续点开getBean方法:

//org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String, java.lang.Class<t>, java.lang.Object...)
return doGetBean(name, requiredType, args, false);
</t>
//org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
//org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
//org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getObjectFromFactoryBean
doGetObjectFromFactoryBean(factory, beanName);
//org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBean
object = factory.getObject();

直到这里,才执行了我们写的MyFactoryBean的getObject方法,拿到了我们返回的Teacher对象后。

因为有缓存机制如果我们再去拿,就不会再次调用getObject方法了,这个缓存机制就不再继续分析了,比较复杂,就算不了解也不影响我们今天的主题。

JDK动态代理

我以前写过JDK动态代理的博客,大家可以找来看一看 ,这里就不阐述了。

mybatis-spring源码分析

前置知识介绍完成,有了上面的前置知识,我们就可以一探MyBatis和Spring整合的奥秘。

Mybatis和Spring整合的入口很好找,就是我们再配置上添加的@MapperScan注解,当我们点开@MapperScan:

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

你会发现一个很熟悉的注解,就是我们上面讲的Import注解,Import了MapperScannerRegistrar。

通过上面的源码分析明白,Spring会执行到registerBeanDefinitions方法:

@Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
   // &#x62FF;&#x5230;&#x6211;&#x4EEC;&#x5199;&#x7684;MapperScan&#x6CE8;&#x89E3;&#x4E0A;&#x5E26;&#x7684;&#x4E1C;&#x897F;&#xFF0C;&#x6211;&#x4EEC;&#x5199;&#x7684;&#xFF0C;&#x53EA;&#x6709;&#x4E00;&#x4E2A;Value&#x5B57;&#x6BB5;&#x3002;
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    if (mapperScanAttrs != null) {
      registerBeanDefinitions(mapperScanAttrs, registry);
    }
  }

继续深入registerBeanDefinitions方法:

// &#x521B;&#x5EFA;&#x4E86;&#x4E00;&#x4E2A;&#x626B;&#x63CF;&#x5668;&#xFF0C;&#x8FD9;&#x4E2A;&#x626B;&#x63CF;&#x5668;&#x7EE7;&#x627F;&#x4E86;Spring&#x5B9A;&#x4E49;&#x7684;&#x626B;&#x63CF;&#x5668;&#xFF1A;ClassPathBeanDefinitionScanner&#xFF0C;
// &#x626B;&#x63CF;&#x7684;&#x4E3B;&#x8981;&#x662F;&#x4E3B;&#x8981;&#x4F5C;&#x7528;&#x5C31;&#x662F;&#x626B;&#x63CF;&#xFF0C;&#x628A;bean&#x653E;&#x5230;map&#x4E2D;&#x53BB;
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
//&#x7701;&#x7565;
    basePackages.addAll(
        Arrays.stream(annoAttrs.getStringArray("value"))
            .filter(StringUtils::hasText)
            .collect(Collectors.toList()));
//&#x7701;&#x7565;
    scanner.registerFilters();
    scanner.doScan(StringUtils.toStringArray(basePackages));

这里主要是创建了一个扫描器,传入了一些规则。

scanner.registerFilters()中有一行代码,比较重要:

// mybatis&#x5B9A;&#x4E49;&#x7684;&#x626B;&#x63CF;&#x5668;&#x6700;&#x7EC8;&#x7684;&#x626B;&#x63CF;&#x4EFB;&#x52A1;&#x662F;&#x4EA4;&#x7ED9;Spring&#x7684;&#x626B;&#x63CF;&#x5668;&#x6267;&#x884C;&#x7684;&#xFF0C;
// Spring&#x7684;&#x626B;&#x63CF;&#x5668;&#x4E2D;&#x5B9A;&#x4E49;&#x4E86;includeFilters&#xFF0C;&#x53EA;&#x6709;&#x7B26;&#x5408;&#x89C4;&#x5219;&#x7684;&#x6700;&#x7EC8;&#x624D;&#x53EF;&#x4EE5;&#x88AB;&#x626B;&#x63CF;&#x51FA;&#x6765;&#xFF0C;
// &#x8FD9;&#x91CC;&#x610F;&#x5473;&#x7740;mybatis&#x544A;&#x8BC9;spring&#xFF0C;&#x4EFB;&#x4F55;&#x4E1C;&#x897F;&#x4F60;&#x90FD;&#x8981;&#x7ED9;&#x6211;&#x626B;&#x63CF;&#x51FA;&#x6765;&#x3002;
  addIncludeFilter((metadataReader, metadataReaderFactory) -> true);

让我们看下 scanner.doScan(StringUtils.toStringArray(basePackages))方法:

 @Override
  public Set<beandefinitionholder> doScan(String... basePackages) {
    //&#x4EA4;&#x7ED9;Spring&#x6267;&#x884C;&#x626B;&#x63CF;&#x4EFB;&#x52A1;&#xFF0C;&#x8FD4;&#x56DE;beanDefinition&#x3001;beanName&#x7684;&#x5305;&#x88C5;&#x5BF9;&#x8C61;&#xFF0C;&#x8FD9;&#x91CC;&#x5C31;&#x628A;&#x6211;&#x4EEC;
    //@MapperScan&#x6CE8;&#x89E3;&#x4E2D;&#x7ED9;&#x5B9A;&#x7684;com.codebear.mapper&#x5305;&#x4E2D;&#x6240;&#x6709;&#x7684;&#x5185;&#x5BB9;&#x90FD;&#x626B;&#x63CF;
    //&#x5E76;&#x4E14;&#x8FD4;&#x56DE;&#x51FA;&#x6765;&#x4E86;&#x3002;
    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 {
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
  }
</beandefinitionholder></beandefinitionholder>

继续看processBeanDefinitions方法:

// &#x7701;&#x7565;
 for (BeanDefinitionHolder holder : beanDefinitions) {
  definition.setBeanClass(this.mapperFactoryBean.getClass());
}
//&#x7701;&#x7565;

这个循环中,有一行代码很是重要,把扫描出来的bean的BeanClass都设置成了mapperFactoryBean,这个mapperFactoryBean是何方神圣呢?没错,它就是我们上面分析过的FactoryBean,通过实验和分析,我们知道了最终产生的bean对象是FactoryBean中的getObject返回的对象。

  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
 public SqlSession getSqlSession() {
    return this.sqlSessionTemplate;
  }
//org.mybatis.spring.SqlSessionTemplate#getMapper
  public <t> T getMapper(Class<t> type) {
    return getConfiguration().getMapper(type, this);
  }
</t></t>
//org.apache.ibatis.binding.MapperRegistry#getMapper
    public <t> T getMapper(Class<t> type, SqlSession sqlSession) {
        MapperProxyFactory<t> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }
</t></t></t>
//org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession)
//sqlSession&#x662F;SqlSessionTemplate
    public T newInstance(SqlSession sqlSession) {
// &#x8FD9;&#x91CC;&#x9700;&#x8981;&#x7528;&#x5230;JDK&#x52A8;&#x6001;&#x4EE3;&#x7406;&#x7684;&#x77E5;&#x8BC6;&#xFF0C;&#x4F20;&#x5165;&#x4E86;SqlSessionTemplate&#xFF0C;Mapper&#x7C7B;&#xFF08;&#x63A5;&#x53E3;&#xFF09;
        MapperProxy<t> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }
</t>
// &#x751F;&#x6210;&#x4E86;&#x4EE3;&#x7406;&#x5BF9;&#x8C61;
    protected T newInstance(MapperProxy<t> mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }
</t>

最终我们调用代理对象的方法,会执行到MapperProxy的invoke方法:

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

当我们点开mapperMethod.execute方法,你会觉得一切是那么的熟悉。

本篇的博客内容到这里结束了,本来还想聊聊MyBatis和Spring整合后,一级缓存为什么会失效,但是要想真正弄明白,不是几句话可以说清楚的,还是以后再开一篇博客,单独来讨论这个问题吧。

Original: https://www.cnblogs.com/CodeBear/p/13260790.html
Author: CodeBear
Title: MyBatis和Spring整合的奥秘

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/713891/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 深入理解Apollo核心机制之配置读取——轮询

    前两篇内容《深入理解Apollo核心机制之配置读取——前言》《深入理解Apollo核心机制之配置读取——ConfigService定时扫描》 概述 读取配置除了默认5分钟间隔去轮询…

    技术杂谈 2023年7月25日
    067
  • Android进阶技术之——一文吃透Android的消息机制

    前言 为什么要老药换新汤 作为Android中 至关重要 的机制之一,十多年来,分析它的文章不断,大量的内容已经被挖掘过了。所以: 已经对这一机制熟稔于心的读者,在这篇文章中,看不…

    技术杂谈 2023年7月10日
    071
  • Golang仿云盘项目-6分块上传

    分快上传和断点续传 服务器架构变迁 两个改动: 分块上传: 大文件分块上传,小文件照先前架构普通上传 Redis缓存:存储上传文件的已经上传的每一块文件的元信息。为什么用Redis…

    技术杂谈 2023年7月24日
    069
  • manim 3.0优化

    1、注意不要在物体变换之后再添加其他相关物体,这样物体的初始化会在动画部分的后面 2、动画实现过程最主要还是物体的初始化,所以可以将动画部分和查看物体初始化部分分开(即将动画部分放…

    技术杂谈 2023年7月24日
    069
  • 树莓派远程连接工具SSH使用教程

    树莓派远程连接工具SSH使用教程 树莓派 背景故事 树莓派作为一款迷你小主机,大部分的使用场景都会用到远程调试,远程调试用到最多的方式一般就是VNC和SSH,SSH就是命令行型的远…

    技术杂谈 2023年7月23日
    078
  • gauss正则找汉字

    select substring( ‘沙特阿拉伯6.20’ from ‘[\u4E00-\u9FA5]{2,5}’ )返回 ‘沙特阿拉伯’ Original: https://ww…

    技术杂谈 2023年7月24日
    060
  • 最简单的样式实例

    <ui> <view> <container> <subViews> <label value="测&…

    技术杂谈 2023年6月1日
    073
  • date_histogram,es按照时间分组统计

    日期直方图聚合(date_histogram) 与histogram相似,es中内部将日期表示为一个long值,所以有时候可以用histogram来达到相同的目的,但往往没有dat…

    技术杂谈 2023年7月24日
    079
  • linux多路转接select—服务器代码

    一、linux多路转接select—服务器代码 #include #include #include #include<string.h> #include…

    技术杂谈 2023年7月10日
    053
  • JavaCV的摄像头实战之五:推流

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本文是《JavaCV的摄…

    技术杂谈 2023年7月11日
    089
  • 平台接口建设规范

    建设目标 平台接口建设规范旨在为接口开发、测试、使用划定一个框架边界,明确技术目标与要求,并要求提供完备的接口文档说明,为自有平台与第三方平台提供数据及服务支持。 建设标准 接口规…

    技术杂谈 2023年7月25日
    080
  • eslint报“Too many blank lines at the end of file. Max of 0 allowed. (no-multiple-empty-lines)“错误的解决

    vue启动的时候报错 oo many blank lines at the end of file. Max of 0 allowed. (no-multiple-empty-li…

    技术杂谈 2023年6月1日
    082
  • 微信白名单获取

    公司在微信WIFI登录项目中,需要在BRAS设备上添加微信服务器的IP地址到白名单列表中,以实现用户连接热点后,能够使用微信中的功能(如:添加公众号),但不能使用其他应用进行上网操…

    技术杂谈 2023年7月11日
    075
  • 类的动态装载java

    类的动态装载 首先,我们要明白类加载的过程,再来区分静态加载和动态加载,类加载的过程,本质上就是将类文件,从硬盘读取到内存中的过程,而静态加载是在编译时加载,动态加载是在程序运行时…

    技术杂谈 2023年7月23日
    065
  • 九、运算符

    一、基本运算符 1.1、Java语言支持的运算符 1.&#x7B97;&#x672F;&#x8FD0;&#x7B97;&#x7B26;&am…

    技术杂谈 2023年6月21日
    076
  • ClickHouse-查询优化

    单表查询【使用的频率高】 Prewhere 和 where 语句的作用相同,用来过滤数据。不同之处在于 prewhere 只支持*MergeTree 族系列引擎的表,首先会读取指定…

    技术杂谈 2023年7月10日
    055
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球