Spring集成Mybatis源码分析(二)

一. 初始化SqlSessionFactory

使用过mybatis的人都知道,mybatis中要操作需要开启SqlSession,而mybatis采用了工厂模式来生成SqlSession,要将mybatis继承到spring中,首先我们需要初始化SqlSessionFactory

// 数据源配置,采用dbcp2连接池
@Bean
public BasicDataSource dataSource(){
	BasicDataSource basicDataSource = new BasicDataSource();
	basicDataSource.setUrl("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=192.268.252.20)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=abc)))");
	basicDataSource.setPassword("123456");
	basicDataSource.setUsername("demouser");
	basicDataSource.setDriverClassName(OracleDriver.class.getName());
	return basicDataSource;
}

// 此处会一次调用getObject -> afterPropertiesSet() -> buildSqlSessionFactory()创建sqlSessionFactory
// 如果需要指定配置文件可以通过以下方法指定:
// sqlSessionFactoryBean.setConfigLocation(new ClassPathResource(""));
// sqlSessionFactoryBean.setConfiguration(new org.apache.ibatis.session.Configuration());
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
	SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
	sqlSessionFactoryBean.setDataSource(dataSource());
	return sqlSessionFactoryBean.getObject();
}

SqlSession采用工厂模式产生,而SqlSessionFactory也可以通过SqlSessionFactoryBean来产生,SqlSessionFactoryBean采用了类似建造者模式的方式产生SqlSessionFactory,让我们看看他类图。在这里插入图片描述
这里我们new出SqlSessionFactoryBean,然后通过getObject获取SqlSessionFactory,该方法先构建解析配置类,然后利用sqlSessionFactoryBuilder建造SqlSessionFactory。

protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

    final Configuration targetConfiguration;
	// 如果是采用java方式注入的Configuration
    XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configuration != null) {
      targetConfiguration = this.configuration;
      if (targetConfiguration.getVariables() == null) {
        targetConfiguration.setVariables(this.configurationProperties);
      } else if (this.configurationProperties != null) {
        targetConfiguration.getVariables().putAll(this.configurationProperties);
      }
    }
    // 如果指定的是xml配置文件路径 
    else if (this.configLocation != null) {
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      targetConfiguration = xmlConfigBuilder.getConfiguration();
    }
    // 如果没有指定任何配置文件 
	else {
      LOGGER.debug(
          () -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
      targetConfiguration = new Configuration();
      Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
    }
	// ..... 其余代码省略
	
    if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();
        LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
      } catch (Exception ex) {
        throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }

    targetConfiguration.setEnvironment(new Environment(this.environment,
        this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
        this.dataSource));

    if (this.mapperLocations != null) {
      if (this.mapperLocations.length == 0) {
        LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
      } else {
        for (Resource mapperLocation : this.mapperLocations) {
          if (mapperLocation == null) {
            continue;
          }
          try {
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
            xmlMapperBuilder.parse();
          } catch (Exception e) {
            throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
          } finally {
            ErrorContext.instance().reset();
          }
          LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
    }

    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
  }

利用解析好的配置文件创建DefaultSqlSessionFactory

public SqlSessionFactory build(Configuration config) {
   return new DefaultSqlSessionFactory(config);
}

二. Mapper实例化

上篇讲到,spring会将所有标注有@Mapper的接口注册成BeanDefinition,而这些BeanDefinition在Spring初始化过程中会被实例化,代码入口为AbstractApplicationContext类中的finishBeanFactoryInitialization方法。
spring会循环遍历所有已经有的BeanDefinition,然后依次做实例化

for (String beanName : beanNames) {
	// 获取合并后的beanDefinition,此时已经有合并后的beanDefinition了,因为在执行bean工厂后置处理器时,会调用一个getBeanNamesForType,为了得到一个bean是否时属于某一个type,必须拿到合并后的beanDefinition
	RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
	// 判断是否不是抽象的,是否是单例的,是否是非懒加载的
	if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
		// 判断是否是工厂bean,我们这里的MapperFactoryBean就是继承自spring工厂bean
		if (isFactoryBean(beanName)) {
			// 获取工厂bean,FACTORY_BEAN_PREFIX=&,工厂beanName特有的标识
			Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
			if (bean instanceof FactoryBean) {
				final FactoryBean<?> factory = (FactoryBean<?>) bean;
				boolean isEagerInit;
				if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
					isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
									((SmartFactoryBean<?>) factory)::isEagerInit,
							getAccessControlContext());
				}
				else {
					isEagerInit = (factory instanceof SmartFactoryBean &&
							((SmartFactoryBean<?>) factory).isEagerInit());
				}
				if (isEagerInit) {
					getBean(beanName);
				}
			}
		}
		else {
			getBean(beanName);
		}
	}
}

由于在定义beanDefinition时添加了PropertyValue,因此实例化过程中会进行属性注入,分两个步骤,实例化时注入mapperInterface,属性注入时注入sqlSessionTemplate,addToConfig

2.1. 实例化,bean后置处理器推断构造方法

因为MapperFactoryBean有两个构造函数,一个为无参构造函数,一个为有参构造函数,首先会通过bean后置处理器推断构造函数

// Candidate constructors for autowiring?
// 第三次调用后置处理器,判断bean的构造器
// 1.唯一非默认构造函数,返回改构造函数
// 2.多个且有一个为@Autowired(required = true),返回该构造函数
// 3.多个@Autowired(required = false), 返回多个构造函数
// 4.唯一一个默认构造函数,返回null
// 根据以上结论推断构造器返回null
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
		mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
	return autowireConstructor(beanName, mbd, ctors, args);
}

但构造BeanDefinition中添加了构造方法参数,因此上述条件成立,根据参数数量等条件推断出构造函数,实例化对象

definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59

在autowireConstructor(beanName, mbd, ctors, args)方法中会完成实例化及mapperInterface的注入

2.1. 属性注入

实例化完成后会进行属性注入

// 属性注入
populateBean(beanName, mbd, instanceWrapper);

BeanDefinition中配置了注入方式为自动byType注入,并且属性中添加过addToConfig,因此,该值将会自动注入到属性中

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

调用autowireByType注入addToConfig,并且推断出其他需要且能被注入的属性,能被注入的属性一定是已有的bean

if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
	autowireByType(beanName, mbd, bw, newPvs);
}

通过后置处理器找出需要注入的bean,封装成PropertyValue

for (BeanPostProcessor bp : getBeanPostProcessors()) {
	if (bp instanceof InstantiationAwareBeanPostProcessor) {
		InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
		// 第7次调用后置处理器,查到到需要用的PropertyValue
		PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
		if (pvsToUse == null) {
			if (filteredPds == null) {
				filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
			}
			pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
			if (pvsToUse == null) {
				return;
			}
		}
		pvs = pvsToUse;
	}
}

完成属性设置

if (pvs != null) {
	applyPropertyValues(beanName, mbd, bw, pvs);
}

至此所有属性注入完成!!

三. 初始化MapperFactoryBean

bean实例化完成后如果有需要,还要对其进行初始化,因为MapperFactoryBean实现了InitializingBean,因此此处会调用他的afterPropertiesSet方法。

// 1.执行各种Aware接口,BeanNameAware,BeanClassLoaderAware,BeanFactoryAware
// 2. 如果bean为空执行bean初始化前后置处理器
// 3. 执行初始化方法
// 4 如果bean还为空执行bean初始化后后置处理器
exposedObject = initializeBean(beanName, exposedObject, mbd);

程序入口:

try {
	// 执行实现了InitializingBean的afterPropertiesSet()
	invokeInitMethods(beanName, wrappedBean, mbd);
}

程序执行到DaoSupport的初始化方法

@Override
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
	// Let abstract subclasses check their configuration.
	checkDaoConfig();

	// Let concrete implementations initialize themselves.
	try {
		initDao();
	}
	catch (Exception ex) {
		throw new BeanInitializationException("Initialization of DAO failed", ex);
	}
}

而checkDaoConfig由MapperFactoryBean重写了,此时就与我们之前注入SqlSessionTemplate对接上了,因此这里我们需要使用SqlSession获取到Configuration,然后再用Configuration来注册Mapper。

@Override
protected void checkDaoConfig() {
  super.checkDaoConfig();

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

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

以下是注册Mapper核心代码:

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 {
       // 往缓存中添加MapperProxyFactory
        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.
        // 解析接口中的信息,把各种@Select,@Insert解析为Statement,以便后续直接调用,解析过程这里暂时不分析
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

下面我们来看看,knownMappers中添加的这个MapperProxyFactory

// 由jdk动态代理生成代理Mapper的的动态代理对象,该对象实现了我们的Mapper接口
// 其中三个参数说明以下
// mapperInterface.getClassLoader():我们Mapper接口的类加载器
// new Class[] { mapperInterface}:动态代理对象需要实现的接口
// mapperProxy: 实现了InvocationHandler的类,并重写了invoke方法,后续执行Mapper接口方法时就会被动态转调到该方法上,后面我们重点来说这个方法
@SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }
  
  // new实例,实际调用newInstance(MapperProxy<T> mapperProxy)方法
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

MapperProxy的invoke方法

@Override
// 参数说明一下:
// proxy:代理对象
// method: 调用的方法
// args: 方法参数
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
      // 如果该方法所属类是Object,那么直接调用
        return method.invoke(this, args);
      } else if (method.isDefault()) {
      // 如果是接口的default方法
        if (privateLookupInMethod == null) {
          return invokeDefaultMethodJava8(proxy, method, args);
        } else {
          return invokeDefaultMethodJava9(proxy, method, args);
        }
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    // 否则获取封装后的方法并执行
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

封装的方法,如果方法已经存在,则返回缓存的方法,如果没有,则新建并缓存

private MapperMethod cachedMapperMethod(Method method) {
    return methodCache.computeIfAbsent(method,
        k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
  }

封装执行方法,后续就可以利用该方法的execute方法调用数据库

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }

以上是我对Spring集成mybatis的总结,刚开始对Spring源码进行系统学习,不求给大家看,只求自己以后忘了能有个记录的地方,如果有理解不对或者逻辑混乱的地方,欢迎指正!!谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值