Java教程

spring和mybatis整合源码解析

本文主要是介绍spring和mybatis整合源码解析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

springboot和mybatis整合源码解析

简单的流程图
在这里插入图片描述

使用的是我们springboot,所以我们先要创建一个config类

@Configuration//指定这是一个核心配置类
@MapperScan("com.demo.dao")//扫描dao层,生成动态代理
@ComponentScan("com.demo")//扫描该路径下所有类上的注解
@EnableTransactionManagement//开启事务
@EnableAspectJAutoProxy
public class ApplicationConfig {
    //配置数据源
    @Bean
    public DataSource dataSource(JdbcConfig dbcp) throws PropertyVetoException {
        //其中JdbcConfig是自定义的配置类,读取properties文件的类
        BasicDataSource cd = new BasicDataSource();
        cd.setDriverClassName(dbcp.getDriver());
        cd.setUrl(dbcp.getUrl());
        cd.setUsername(dbcp.getName());
        cd.setPassword(dbcp.getPassword());
        return cd;
    }

    //配置核心Mybatis核心工厂
    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource ds) throws IOException {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(ds);//配置数据源
        bean.setTypeAliasesPackage("com.demo.entity");//设置实体类别名
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        bean.setMapperLocations(resolver.getResources("classpath:/mapper/*.xml"));//配置Mapper映射文件的路径
        return bean;
    }
    }

在这里插入图片描述

可以看到,这是向我们的容器注册2个bean,分别是DataSource ,SqlSessionFactoryBean 。

然后我们分析注解MapperScan,点进去mapperScan注解,发现import了MapperScannerRegistrar。

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

结合spirng的源码就可以知道,在解析@Import注解的时候,把MapperScannerRegistrar放进了importBeanDefinitionRegistrars,然后再执行registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));

	private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
		registrars.forEach((registrar, metadata) ->
				registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
	}

就会进入到MapperScannerRegistrar的registerBeanDefinitions方法。

  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

    if (resourceLoader != null) { // this check is needed in Spring 3.1
      scanner.setResourceLoader(resourceLoader);
    }

    Class<? extends Annotation> 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<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
      scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
    }

    scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
    scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));

    List<String> basePackages = new ArrayList<String>();
    //拿到注解上面配置的value
    for (String pkg : annoAttrs.getStringArray("value")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (String pkg : annoAttrs.getStringArray("basePackages")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
    }
    scanner.registerFilters();
    scanner.doScan(StringUtils.toStringArray(basePackages));
  }

通过annoAttrs.getStringArray(“value”)拿到了MapperScan上面的注解com.demo.dao,然后再执行scanner.doScan(StringUtils.toStringArray(basePackages))。doScan里面有3个关键的代码,然后dao下面的注解就进入到了beanDefinitions。

//根据basePackages去扫描类,形成beanDefinition
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
//把注入方式设置成按类型注入,默认是no
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
//把accountDao的类型设置成MapperFactoryBean
definition.setBeanClass(MapperFactoryBean.class);

因为我们定义了2个@Bean注解,到loadBeanDefinitionsForBeanMethod(beanMethod)方法,就会去解析我们的@Bean注解,把2个bean加入到beanDefinitions中。

然后我们的容器中就有了我们的dao,SqlSessionFactoryBean
在初始化前,我们看一下现在 都有哪些bd

在这里插入图片描述在这里插入图片描述

然后看几个关键bean的初始化
初始化我们的dataSource就会调用ApplicationConfig 的dataSource,把配置文件上面的属性都给他赋值上。

下面初始化我们的accountDao,由于BeanClass被设置成了MapperFactoryBean,
注入类型变成了byType,所以我们先看MapperFactoryBean的代码,找到所有的set方法,都是需要自动装配的

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
		for (PropertyDescriptor pd : pds) {
			if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
					!BeanUtils.isSimpleProperty(pd.getPropertyType())) {
				result.add(pd.getName());
			}
		}

最后的到两个属性需要自动装配

在这里插入图片描述

调用

Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);

在这里插入图片描述
从容器中的bd去找sqlsessionFactory,有一段关键的代码,判断这个BD是否是一个FactoryBean,类型是否匹配。
在这里插入图片描述
SqlSessionFactoryBean不正是一个实现了FactoryBean,然后重写的getObject方法返回sqlSessionFactory对象吗,所以就匹配到了,下面就是进行属性的填充。

  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }

要填充首先要初始化sqlSessionFactoryBean

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {

由于sqlSessionFactoryBean实现了InitializingBean,所以被实例化后会执行他重写的afterPropertiesSet。

  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

buildSqlSessionFactory里面就会解析我们的XML文件,分别解析mapper下面的parameterMap,resultMap,sql,select|insert|update|delete

      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));

再根据解析出来的信息,形成了mappedStatements,每一个增删改查方法都是一个mappedStatement
在这里插入图片描述
完成实例化后,就执行,SqlSessionTemplate被new出来,放到了sqlSession 。

  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (!this.externalSqlSession) {
      this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
    }
  }

最后accountDao也被实例化出来了。这样我们就能从bean工厂中获取到accountDao,执行我们的sql语句了。

这篇关于spring和mybatis整合源码解析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!