聊一聊MyBatis的核心概念、Spring相关的核心内容,主要结合源码理解Spring是如何整合MyBatis的。(结合右侧目录了解吧)html
建立SqlSession的工厂java
sql请求的会话,经过SqlSessionFactory获取。spring
String resource = "mybatis-config.xml"; InputStream resourceAsStream = Resources.getResourceAsStream(resource); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream); // 经过Builder获取构建SqlSessionFactory(读取mybatis-config.xml文件配置) SqlSession sqlSession = sqlSessionFactory.openSession(); // 开启Session UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.findByUserId(1);
上述代码就是单独使用MyBatis的时候的API例子。读取mybatis-config.xml构建出SqlSessionFactory,经过Factory开启SqlSession,使用SqlSession获取Mapper的代理实例。sql
public interface UserMapper { User findByUserId(Integer userId); }
<mapper namepsace = "com.deepz.mybatis.user.UserMapper"> <select id = "findByUserId" resultType="User"> ... </select> </mapper>
以上就是我们熟悉的MyBatis使用代码了,一个Mapper接口对应的就有一个XML文件。数据库
是一种特殊的SpringBean,对应的真实实例是FactoryBean接口中getObject()方法的返回值,用于自定义复杂的Bean生成。缓存
AbstractBeanFactory#doGetBeansession
Object sharedInstance = getSingleton(beanName); // 从三级缓存中根据beanName获取SpringBean if (sharedInstance != null && args == null) { // 若是SpringBean不为空则说明命中缓存,直接获取SpringBean实例便可 if (logger.isDebugEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); } } bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); // 获取SpringBean真实实例(针对FactoryBean) }
AbstractBeanFactory#getObjectForBeanInstancemybatis
// Now we have the bean instance, which may be a normal bean or a FactoryBean. // If it's a FactoryBean, we use it to create a bean instance, unless the // caller actually wants a reference to the factory. if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { // 若是不是FactoryBean类型则直接返回 return beanInstance; } // 后续代码针对FactoryBean类型的SpringBean处理 Object object = null; if (mbd == null) { object = getCachedObjectForFactoryBean(beanName); // 从缓存中获取 } if (object == null) { // Return bean instance from factory. FactoryBean<?> factory = (FactoryBean<?>) beanInstance; // Caches object obtained from FactoryBean if it is a singleton. if (mbd == null && containsBeanDefinition(beanName)) { mbd = getMergedLocalBeanDefinition(beanName); } boolean synthetic = (mbd != null && mbd.isSynthetic()); object = getObjectFromFactoryBean(factory, beanName, !synthetic); // 获取FactoryBean真实实例 } return object;
FactoryBeanRegistrySupport#doGetObjectFromFactoryBean
FactoryBeanRegistrySupport#getObjectFromFactoryBean最终会调到以下方法,经过FactoryBean的getObject()获取FactoryBean的真实实例app
Object object; try { if (System.getSecurityManager() != null) { AccessControlContext acc = getAccessControlContext(); try { object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { @Override public Object run() throws Exception { return factory.getObject(); } }, acc); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { object = factory.getObject(); // 显示调用FactoryBean#getObject } }
在SpringBean的生命周期中,Bean的初始化环节Spring会调用AbstractAutowireCapableBeanFactory#invokeInitMethods() 回调实现了InitializingBean接口的Bean的InitializingBean#afterPropertiesSet()less
关于SpringBean生命周期欢迎移步笔者的相关总结随笔《深刻源码理解SpringBean生命周期》
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable { boolean isInitializingBean = (bean instanceof InitializingBean); // Bean实现了InitializingBean接口 if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isDebugEnabled()) { logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { try { AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { @Override public Object run() throws Exception { ((InitializingBean) bean).afterPropertiesSet(); return null; } }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { ((InitializingBean) bean).afterPropertiesSet(); // 显式回调InitializingBean的afterPropertiesSet()方法 } } if (mbd != null) { String initMethodName = mbd.getInitMethodName(); if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } } }
能够看到BeanDefinitionRegistryPostProcessor继承了BeanFactoryPostProcessor,那么该接口的实现类必定会在Spring应用上下文生命周期中回调相关接口方法。
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { /** * Modify the application context's internal bean definition registry after its * standard initialization. All regular bean definitions will have been loaded, * but no beans will have been instantiated yet. This allows for adding further * bean definitions before the next post-processing phase kicks in. * @param registry the bean definition registry used by the application context * @throws org.springframework.beans.BeansException in case of errors */ void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException; }
这个接口是干什么的?它有本身的抽象方法,在Spring应用上下文生命周期的"invokeBeanFactoryPostProcessors"环节会回调相关方法。(这里不是重点不过多聊)
咱们此次关注的重点是它的子接口-BeanDefinitionRegistryPostProcessor,Spring在上述环节中对该接口作了特殊处理,回调了它的独有方法。(如上面的代码段所示)
AbstractApplicationContext#refresh()方法即为Spring应用上下文的生命周期的刷新入口,能够看到在比较前置的环节就会先处理BeanFactoryProcessor类型的Bean。
追进源码后发现最后会在PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors()方法中循环处理全部BeanFactoryProcessor,其中若是是BeanDefinitionRegistryPostProcessor则会回调BeanDefinitionRegistryPostProcessor的独有方法,值得注意的是该方法的入参是一个BeanDefinitionRegistry。
(关于BeanDefinitionRegistry有必要在单独段落作介绍,因此请先移步下一段落吧)
在了解了这几个接口后,咱们汇总一下就明白了BeanDefinitionRegistryPostProcessor的能力了。它能够在Spring应用上下文前期先被实例化且回调相关接口方法,向Spring容器注册或移除BeanDefinition甚至能够在get出一个BeanDefinition后直接修改内部属性,让Bean变成你想要的模样。
BeanDefinition是Spring实例化一个Bean的依据,它的内部维护了一个Bean的各类属性,如BeanClass、BeanName、lazyInit(是否懒加载)、primary、scope等等。
而Spring在实例化一个Bean的时候须要先从一个Map中根据beanName获取到对应的BeanDefinition才能去按需实例化SpringBean,以下。
DefaultListableBeanFactory#getBeanDefinition()
看看这个Map的定义
相信你们也猜到了,上面提到的就是维护这个Map的。
BeanDefinitionRegistry接口的方法以下:
能够看到该接口的能力就是维护Spring容器中的BeanDefinition。
有了上面的回顾后,关于Spring整合MyBatis的秘密就很容易揭晓了。
在跟进源码前,咱们先思考下若是是你,你会怎么将MyBatis整合进来?达到效果:经过@Autowired将Mapper注入进来即可以直接使用。
首先,咱们有了Spring,第一个要干掉的就是SqlSessionFactory的维护了,咱们要想办法读取”mybatis-config.xml“配置后,将SqlSessionFactory做为一个SpringBean交给SpringIOC管理。
其次,一样的,咱们不可能每次都经过sqlSession.getMapper()来获取咱们须要的Mapper代理实例,因此第二个要干掉的就是Mapper的维护,咱们一样要想办法将全部的Mapper处理成SpringBean交给SpringIOC,这样咱们就可以将SpringBean依赖注入到任何地方了。
思考事后,咱们来看看Spring是怎么作的吧。当咱们须要结合Spring使用MyBatis的时候,第一步即是添加一个mybatis-spring的Jar到项目里来,那么秘密都在这里了。
以下即是MyBatis-Spring这个Jar的项目结构了,应该能看到你们使用的时候熟悉的组件吧。如MapperScan注解。眼尖的伙伴应该能看到几个类:SqlSessionFactoryBean、MapperFactoryBean、MapperScannerConfigurer、SpringManagedTransaction,这几个类将会是接下来探讨的重点。
刚刚咱们聊到了,首先须要干掉的就是SqlSessionFactory的低端维护方式。咱们先看看SqlSessionFactory这个类的继承树。
能够看到它实现了FactoryBean,这就意味着它必定有一个getObject()方法,用于返回交给Spring管理的实例;
它还实现了InitializingBean,这就意味着在这个Bean的初始化时,Spring会回调它的afterPropertiesSet()方法。(Spring事件本次不讨论)
咱们先看看,这个SqlSessionFactoryBean交给Spring管理的对象是怎样构建的。
/** * {@inheritDoc} */ @Override public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { // 若是sqlSessionFactory为空,则显式调用afterPropertiesSet()方法 afterPropertiesSet(); } return this.sqlSessionFactory; // 返回sqlSessionFactory } @Override public Class<? extends SqlSessionFactory> getObjectType() { return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass(); }
能够看到源码中首先是判空,若是为空则显式调用afterPropertiesSet()方法,最后将sqlSessionFactory返回。那么能够猜出,afterPropertiesSet()方法大几率是构造sqlSessionFactory的了。
首先看看afterPropertiesSet()方法
/** * {@inheritDoc} */ @Override public void afterPropertiesSet() throws Exception { notNull(dataSource, "Property 'dataSource' is required"); notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null), "Property 'configuration' and 'configLocation' can not specified with together"); this.sqlSessionFactory = buildSqlSessionFactory(); // 核心逻辑在buildSqlSessionFactory()中。 }
跟进看看buildSqlSessionFactory()方法,请重点看标了注释的,其余的能够不用太关注细节,主要是一些配置的初始化工做。
protected SqlSessionFactory buildSqlSessionFactory() throws IOException { Configuration configuration; XMLConfigBuilder xmlConfigBuilder = null; // 初始化Configuration全局配置对象。 if (this.configuration != null) { configuration = this.configuration; if (configuration.getVariables() == null) { configuration.setVariables(this.configurationProperties); } else if (this.configurationProperties != null) { configuration.getVariables().putAll(this.configurationProperties); } } else if (this.configLocation != null) { // 读取指定的配置文件 xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties); configuration = xmlConfigBuilder.getConfiguration(); } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration"); } configuration = new Configuration(); if (this.configurationProperties != null) { configuration.setVariables(this.configurationProperties); } } if (this.objectFactory != null) { configuration.setObjectFactory(this.objectFactory); } if (this.objectWrapperFactory != null) { configuration.setObjectWrapperFactory(this.objectWrapperFactory); } if (this.vfs != null) { configuration.setVfsImpl(this.vfs); } // 以下将是对MyBatis的基础配置作初始化,如扫描注册别名、注册Plugins、注册TypeHandler、配置缓存、配置数据源等等。 if (hasLength(this.typeAliasesPackage)) { String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packageToScan : typeAliasPackageArray) { configuration.getTypeAliasRegistry().registerAliases(packageToScan, typeAliasesSuperType == null ? Object.class : typeAliasesSuperType); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases"); } } } if (!isEmpty(this.typeAliases)) { for (Class<?> typeAlias : this.typeAliases) { configuration.getTypeAliasRegistry().registerAlias(typeAlias); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered type alias: '" + typeAlias + "'"); } } } if (!isEmpty(this.plugins)) { for (Interceptor plugin : this.plugins) { configuration.addInterceptor(plugin); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered plugin: '" + plugin + "'"); } } } if (hasLength(this.typeHandlersPackage)) { String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packageToScan : typeHandlersPackageArray) { configuration.getTypeHandlerRegistry().register(packageToScan); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers"); } } } if (!isEmpty(this.typeHandlers)) { for (TypeHandler<?> typeHandler : this.typeHandlers) { configuration.getTypeHandlerRegistry().register(typeHandler); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered type handler: '" + typeHandler + "'"); } } } if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls try { configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource)); } catch (SQLException e) { throw new NestedIOException("Failed getting a databaseId", e); } } if (this.cache != null) { configuration.addCache(this.cache); } if (xmlConfigBuilder != null) { try { xmlConfigBuilder.parse(); if (LOGGER.isDebugEnabled()) { 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(); } } // 配置事务管理类,将再也不由MyBatis管理(以前配置为“JDBC”对应的MyBatis的JdbcTransactionFactory)。 if (this.transactionFactory == null) { this.transactionFactory = new SpringManagedTransactionFactory(); } configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource)); // 扫描Mapper.xml以及对应的Mapper接口 if (!isEmpty(this.mapperLocations)) { for (Resource mapperLocation : this.mapperLocations) { if (mapperLocation == null) { continue; } try { XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), configuration, mapperLocation.toString(), configuration.getSqlFragments()); xmlMapperBuilder.parse(); } catch (Exception e) { throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e); } finally { ErrorContext.instance().reset(); } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'"); } } } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found"); } } //前置条件准备好后,建立SqlSessionFactory对象 return this.sqlSessionFactoryBuilder.build(configuration); }
整体就是读取MyBatis的配置,初始化Configuration全局配置对象,根据整合的配置建立出SqlSessionFactory。
通过了上述步骤,SqlSessionFactory就能够被交给Spring管理了,解决了第一个问题,能够从Spring上下文中获取到SqlSessionFactory这个Bean了。
ClassPathXmlApplication applicationContext = new ClassPathXmlApplicationContext("spring.xml"); SqlSessionFactory sqlSessionFactory = applicationContext.getBean(SqlSessionFactory.class); SqlSession sqlSession = sqlSessionFactory.openSession(); // 省略后续MyBatis代码 -.- ......
接下来讨论Spring如何实现经过@Autowired将Mapper注入进来后就能直接使用的问题。
首先思考一下,如何将一个类交给Spring管理?@Component系列注解?而MyBatis是一个个Interface而不是Class,在上面加注解是没用的,咱们须要的是将MyBatis对Mapper生成的代理对象交给Spring管理。那该怎么作呢?Spring的作法是将Mapper一对一地包装成了MapperFactoryBean,而MapperFactoryBean维护了Mapper的类型,经过该类型获取Mapper代理实例。
能够看到这个MapperFactoryBean一样实现了FactoryBean接口,那么按照惯例咱们看看它的getObject()作了什么。
// Mapper接口类型 private Class<T> mapperInterface; /** * {@inheritDoc} */ @Override public T getObject() throws Exception { // 与MyBatis单独使用相似,都是经过sqlSession调用getMapper()方法获取对应的Mapper。 // 须要注意的是入参是一个接口类型,而出参是MyBatis生成的代理对象 return getSqlSession().getMapper(this.mapperInterface); } /** * {@inheritDoc} */ @Override public Class<T> getObjectType() { return this.mapperInterface; // Object的类型 }
能够看出Spring将Mapper包装成了MapperFactoryBean,其中的mapperInterface字段就是Mapper的类型,在交给Spring管理的时候依旧是经过sqlSession.getMapper(Class
那么Mapper对应的模型有了,是否是还缺点什么?是的,咱们须要扫描全部的Mapper,将他们包装成MapperFactoryBean(如UserMapper,就须要有一个MapperFactoryBean,其中mapperInterface字段是UserMapper.class)。这个重要的任务,Spring交给了咱们接下来要聊的MapperScannerConfigurer了,经过类名就能感知到关键字:[Mapper、扫描、配置]。
老规矩,看看几个核心接口的方法都作了什么。
/** * {@inheritDoc} */ @Override public void afterPropertiesSet() throws Exception { // 几乎啥也没干,就断言了个扫描包路径不为空。 下一个! notNull(this.basePackage, "Property 'basePackage' is required"); }
能够看到MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,文章开头的Spring技术回顾聊到该接口的postProcessBeanDefinitionRegistry()方法会在Spring容器启动的时候在较早的时机被回调。
private String basePackage; private boolean addToConfig = true; private SqlSessionFactory sqlSessionFactory; private SqlSessionTemplate sqlSessionTemplate; private String sqlSessionFactoryBeanName; private String sqlSessionTemplateBeanName; private Class<? extends Annotation> annotationClass; private Class<?> markerInterface; private ApplicationContext applicationContext; private String beanName; private boolean processPropertyPlaceHolders; private BeanNameGenerator nameGenerator; /** * {@inheritDoc} * * @since 1.0.2 */ @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) { // 解析并更新Spring配置文件中MapperScannerConfigurer相关的配置 processPropertyPlaceHolders(); } //建立类路径Mapper扫描器,并配置基本信息如扫描的注解(过滤条件)等。 ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); scanner.setAddToConfig(this.addToConfig); scanner.setAnnotationClass(this.annotationClass); scanner.setMarkerInterface(this.markerInterface); scanner.setSqlSessionFactory(this.sqlSessionFactory); scanner.setSqlSessionTemplate(this.sqlSessionTemplate); scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName); scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName); scanner.setResourceLoader(this.applicationContext); scanner.setBeanNameGenerator(this.nameGenerator); scanner.registerFilters(); //根据配置好的信息去扫描basePackage字段中指定的包及其子包 scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); }
咱们来看看processPropertyPlaceHolders()作了什么。[能够跳过,不重要]
/* * BeanDefinitionRegistries are called early in application startup, before * BeanFactoryPostProcessors. This means that PropertyResourceConfigurers will not have been * loaded and any property substitution of this class' properties will fail. To avoid this, find * any PropertyResourceConfigurers defined in the context and run them on this class' bean * definition. Then update the values. */ // 上面Spring官方的注释的意思以下:BeanDefinitionRegistriy在Spring启动的时候回调地太早了,在BeanFactoryPostProcessors以后(PropertyResourceConfigurer实现了BeanFactoryProcessor) // 方法调用到此处的时候,相关的配置信息还没被载入进来,都是空,会有问题。因此咱们要提早主动触发(getBeanOfType与getBean逻辑一致,都是先拿,拿不到就实例化再存入三级缓存)PropertyResourceConfigurer的实例化,这样相关的配置就可以被载入进来了。 private void processPropertyPlaceHolders() { // 先主动触发该类型的Bean的实例化。 Map<String, PropertyResourceConfigurer> prcs = applicationContext.getBeansOfType(PropertyResourceConfigurer.class); if (!prcs.isEmpty() && applicationContext instanceof ConfigurableApplicationContext) { BeanDefinition mapperScannerBean = ((ConfigurableApplicationContext) applicationContext) .getBeanFactory().getBeanDefinition(beanName); // PropertyResourceConfigurer does not expose any methods to explicitly perform // property placeholder substitution. Instead, create a BeanFactory that just // contains this mapper scanner and post process the factory. DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); factory.registerBeanDefinition(beanName, mapperScannerBean); for (PropertyResourceConfigurer prc : prcs.values()) { prc.postProcessBeanFactory(factory); } PropertyValues values = mapperScannerBean.getPropertyValues(); // 更新相关重要字段信息 this.basePackage = updatePropertyValue("basePackage", values); this.sqlSessionFactoryBeanName = updatePropertyValue("sqlSessionFactoryBeanName", values); this.sqlSessionTemplateBeanName = updatePropertyValue("sqlSessionTemplateBeanName", values); } }
回到ClassPathMapperScanner#scan(),该方法内部会继续调用doScan()方法。
/** * Calls the parent search that will search and register all the candidates. * Then the registered objects are post processed to set them as * MapperFactoryBeans */ @Override public Set<BeanDefinitionHolder> doScan(String... basePackages) { //先调用父类ClassPathBeanDefinitionScanner#doScan()方法 //扫描并将Bean信息整合成BeanDefinition注册进Spring容器,且包装成BeanDefinitionHolder返回 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 { // 重要!!! 将BeanDefinition的定义适配成MapperFactoryBean。(当前BeanDefinition的beanClass是Mapper Interface,是没法实例化的。) processBeanDefinitions(beanDefinitions); } return beanDefinitions; }
以下是processBeanDefinitions()的核心代码片断
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); // 省略代码 ...... // the mapper interface is the original class of the bean // but, the actual class of the bean is MapperFactoryBean // 添加构造函数参数值,当前Mapper的Class。 definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59 // 将Bean的类型定义修改成MapperFactoryBean,这样实例化出来的就是一个MapperFactoryBean了 definition.setBeanClass(this.mapperFactoryBean.getClass()); definition.getPropertyValues().add("addToConfig", this.addToConfig); // 省略代码 ...... } }
这段代码很是关键!首先,它对Bean的构造函数参数值作了干预,将当前的BeanClassName设置进去了(如UserMapper.class),而从第二行代码中也能知道该Bean的Class被修改为了MapperFactoryBean,因此咱们去看看MapperFactoryBean的构造函数就好了。
public MapperFactoryBean(Class<T> mapperInterface) { // Mapper Interface。(如UserMapper.class) this.mapperInterface = mapperInterface; } /** * {@inheritDoc} */ @Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); // 等同于如:sqlSession.getMapper(UserMapper.class) }
其次它将Bean的实例化类型从没法实例化的Mapper Interface修改为了能够实例化的MapperFactoryBean类型。
以上操做后,就将一个Mapper Bean包装成了MapperFactoryBean,而交给Spring管理的是Mapper对应的代理实例(经过mapperInterface字段绑定关系),因此咱们就能经过@Autowired将Mapper(MapperFactoryBean#getObject())依赖注入进来直接使用了。
到此,Spring整合MyBatis的内容就结束了。
再顺便聊一下其中的变更-事务管理器,由于Spring整合了MyBatis因此后续的事务就应该由Spring来管理了。
以前在MyBatis中若是配置的是“JDBC",则是JdbcTransactionFactory
值得一提的是,SpringManagedTransaction 中除了维护事务关联的数据库链接和数据源以外,还维护了一个 isConnectionTransactional 字段(boolean 类型)用来标识当前事务是否由 Spring 的事务管理器管理,这个标识会控制 commit() 方法和rollback() 方法是否真正提交和回滚事务,相关的代码片断以下:
public void commit() throws SQLException { if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit){ // 当事务不禁Spring事务管理器管理的时候,会当即提交事务,不然由Spring事务管理器管理事务的提交和回滚 this.connection.commit(); } }
看一眼Xml方式的Spring整合MyBatis,经过两个核心的组件就完成了Spring整合MyBatis。
<!-- 省略 --> <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="scannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- name="basePackage":(起始)包名, 从这个包开始扫描--> <property name="basePackage" value="com.deepz.mapper"/> </bean> <!-- 省略 -->
总结下本文的内容吧:
SqlSessionFactory的注入:
SqlSessionFactoryBean中的buildSqlSessionFactory()会读取MyBatis的核心配置载入内存,并构建出SqlSessionFactory经过FactoryBean的getObject()交给Spring管理。
而buildSqlSessionFactory()方法的触发时机有两个:1. 在Bean初始化的时候Spring回调InitializingBean的afterProperties();2. FactoryBean的getObject()方法会前置判断SqlSessionFactory是否为空,是空则会调用。
Mapper的注入:
MapperScannerConfigurer在Spring应用上下文启动的时候,在较早的时机回调BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法
经过ClassPathMapperScanner来扫描指定过滤条件(包路径、注解类型...)的类,包装成BeanDefinition注册进容器。
同时将这些BeanDefinition作“加工”处理,就是咱们讲的“processBeanDefinitions()”。它主要作的两件事:1. 添加构造函数参数值,将当前BeanDefinition的Class传递进去,做为后续sqlSession.getMapper();的入参。2. 将BeanDefinition中的beanClass替换成MapperFactoryBean.class,使得Spring经过BeanDefinition实例化出来的是MapperFactoryBean,上演了一出狸猫换太子。最后注入进去的又是getObject()中MyBatis根据MapperFactoryBean中的mapperInterface字段建立的代理对象, 完成了将Mapper交给Spring管理的目标。
原创不易,但愿对你有帮助。欢迎多多指导和讨论。