【二】MyBatis-Spring最全源码详解之Mapper的自动注入

目录

1. MapperFactoryBean的实例化

1.1 Springboot自动配置与@Bean方法的扫描

1.2 sqlSessionFactorybean与SqlSessionTemplate的创建

2. Mapper的依赖注入

2.1 从MapperFactoryBean中获取对象

2.2 postProcessObjectFromFactoryBean

3. 总结

4. 附录:项目文档


上一篇文章重点介绍了@MapperScan("backage")将指定backage中XXXMapper类解析成beanDefinition,随后修改了beanDefinition中beanClass属性,将属性值从XXXMapper替换为了MapperFactoryBean类型。本节将深入分析MapperFactoryBean的实例化和Mapper的依赖注入过程。如果你对bean的实例化过程和依赖注入过程不太熟悉的话,我强烈建议你阅读之前的两篇文章:

《【六】Spring IoC 最全源码详解之bean实例化过程》《【七】Spring IoC 最全源码详解之bean的依赖注入》。如果你已经比较熟悉Spring的IoC流程了,那么我们就开始吧。本文在有基础的情况下,建议阅读时间4小时。


1. MapperFactoryBean的实例化

我们知道在创建daoService这个bean的过程中,在populateBean进行自动装配这一步骤时,会对加上了@Autowired注解的属性PersonMapper personMapper进行自动装配时会调用bean工厂的getBean(beanName)尝试获取或者创建一个目标对象名是beanName的bean注入到personMapper属性上完成自动装配。因为PersonMapper对应的bd中,beanClass已经被替换为MapperFactoryBean,所以beanName为personMapper所对应的bean其实是MapperFactoryBean类型的对象。MapperFactoryBean对象通过带参的构造函数进行实例化,构造参数是Mapper类型PersonMapper.class。当MapperFactoryBean对象实例化之后,也需要对其执行自动装配。

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
	..省略..
	if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
	    MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
	    if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
	        autowireByName(beanName, mbd, bw, newPvs);
	    }
	    if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
              //按byType方式进行自动装配
	        autowireByType(beanName, mbd, bw, newPvs);
	    }
	    pvs = newPvs;
	}
	..省略..
}

因为beanDefinition已经被设置成按AUTOWIRE_BY_TYPE类型装配,所以在populateBean方法中会执行autowireByType方法

protected void autowireByType(
        String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

    // converter的类型是org.mybatis.spring.mapper.MapperFactoryBean
    TypeConverter converter = getCustomTypeConverter();
    if (converter == null) {
        converter = bw;
    }

    Set<String> autowiredBeanNames = new LinkedHashSet<>(4);    
    // 最终得到的属性名是 "sqlSessionFactory" 和 "sqlSessionTemplate"。
    String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    for (String propertyName : propertyNames) {
        try {
            // 获得属性的描述对象
            PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
            // 根据属性的描述,如果这个对象不是Object.class类才会进入if分支。因为没办法对Object.clss进行byType的自动装配。
            if (Object.class != pd.getPropertyType()) {
                MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
                // Do not allow eager init for type matching in case of a prioritized post-processor.
                boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance());
                DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);

                Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
                if (autowiredArgument != null) {
                    pvs.add(propertyName, autowiredArgument);
                }
                for (String autowiredBeanName : autowiredBeanNames) {
                    registerDependentBean(autowiredBeanName, beanName);
                }
                autowiredBeanNames.clear();
            }
        }
        catch (BeansException ex) {
            throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
        }
    }
}

重要的方法是在String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw)。返回不满足的非简单bean属性数组。什么是非简单bean属性呢?请见下图:

unsatisfiedNonSimpleProperties方法最终得到的属性名是 "sqlSessionFactory" 和 "sqlSessionTemplate"。也就是说要MapperFactoryBean依赖于sqlSessionFactory和sqlSessionTemplate这两个对象。这两个对象在哪里呢? 在org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration中通过@Bean标注。

1.1 Springboot自动配置与@Bean方法的扫描

@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource);
    factory.setVfs(SpringBootVFS.class);
    if (StringUtils.hasText(this.properties.getConfigLocation())) {
      factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
    }
    Configuration configuration = this.properties.getConfiguration();
    if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
      configuration = new Configuration();
    }
    if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
      for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
        customizer.customize(configuration);
      }
    }
    factory.setConfiguration(configuration);
    if (this.properties.getConfigurationProperties() != null) {
      factory.setConfigurationProperties(this.properties.getConfigurationProperties());
    }
    if (!ObjectUtils.isEmpty(this.interceptors)) {
      factory.setPlugins(this.interceptors);
    }
    if (this.databaseIdProvider != null) {
      factory.setDatabaseIdProvider(this.databaseIdProvider);
    }
    if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
      factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
    }
    if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
      factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
    }
    if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
      factory.setMapperLocations(this.properties.resolveMapperLocations());
    }
    return factory.getObject();
}
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    ExecutorType executorType = this.properties.getExecutorType();
    if (executorType != null) {
        return new SqlSessionTemplate(sqlSessionFactory, executorType);
    } else {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

Springboot项目的启动类上往往有@SpringBootApplication注解,点进去看会发现它含有一个@EnableAutoConfiguration注解。再点进去看发现有一个@Import(AutoConfigurationImportSelector.class)注解。

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration

进入AutoConfigurationImportSelector.class找到getCandidateConfigurations方法。该方法的作用是委托SpringFactoriesLoader.lodaFactoryNames方法从各个依赖包的META-INF/spring.factories路径下找到自动配置类的类名。找到类名后就可以通过反射实例化该对象。理解了该机制,就理解了Springboot自动配置的精髓。

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
        AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
            getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    Assert.notEmpty(configurations,
            "No auto configuration classes found in META-INF/spring.factories. If you "
                    + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}

回到本项目中。在MapperFactoryBean实例化过程中,会首先根据mybatis-spring-boot-autoconfigure中META-INF/spring.factories中的内容获取到MybatisAutoConfiguration这个配置类类名。在后续的bean创建过程中,MybatisAutoConfiguration类的对象会被创

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值