在xml文件解析完成之后,通过sqlSessionFactoryBuilder构造SqlSessionFactory对象
this.sqlSessionFactoryBuilder.build(configuration);
public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
这样就完成了SqlSessionFactory创建。
四、MapperScannerConfigurer介绍
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.rsms.iot.mapper"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean>
MapperScannerConfigure的postProcessBeanDefinitionRegistry方法中,会通过ClassPathMapperScanner扫描basePackage下的所有接口。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) { processPropertyPlaceHolders(); } 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(); scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); }
ClassPathMapperScanner中通过doScan将原接口类型改造成MapperFactoryBean。为每个接口类创建动态代理。可以从下面源码看出
public Set<BeanDefinitionHolder> doScan(String... basePackages) { 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; } private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); if (logger.isDebugEnabled()) { logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface"); } // the mapper interface is the original class of the bean // but, the actual class of the bean is MapperFactoryBean // mapper接口是原bean类型,但是实际类型是MapperFactoryBean definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName()); definition.setBeanClass(this.mapperFactoryBean.getClass()); definition.getPropertyValues().add("addToConfig", this.addToConfig); ...... } }
上面代码可以看出MapperScannerConfigure是为了避免一个一个定义MapperFactoryBean而添加的批量处理mapper接口的方法,根据basepackages路径,将mapper接口批量改造成MapperFactoryBean,但是元数据中依然保存了原接口类型信息,可以从下图看出。
3.png
五、MapperFactoryBean的装配
上一节中我们知道mapper接口最后都被改造成MapperFactoryBean,而MapperFactoryBean继承SqlSessionDaoSupport,而SqlSessionDaoSupport又继承InitializingBean,所以所有的MapperFactoryBean最后都会执行afterPropertiesSet完成自动装配。(DaoSupport->afterPropertiesSet()方法)
之后通过MapperFactoryBean的getObject获取代理对象(MapperProxy)注入到spring容器中。我们看看spring是如何获取代理对象的。
MapperFactoryBean类的getObject方法: public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); } 调用SqlSessionTemplate的getMapper方法: public <T> T getMapper(Class<T> type) { return getConfiguration().getMapper(type, this); } 接着调用Configuration的getMapper方法: public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } 再调用MapperRegistry的getMapper方法: public <T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } 通过MapperProxyFactory实例化一个代理对象 public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } @SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } Proxy.newProxyInstance就是jdk自带的动态代理完成MapperProxy的构造。
完成每个Mapper接口的代理对象的构造之后就会注入到spring容器管理。
六、mapper的调用过程
根据第三节我们知道,一系列mapper结构改造成MapperFactoryBean后进一步构造出其代理对象MapperProxy,所以调用mapper接口方法时,实际是调用MapperProxy的invoke方法,而该方法中又会调用MapperMethod的execute方法。如下代码
MapperProxy的invoke方法 @Override 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); }
下面以列表查询为例,看下mybatis的执行过程,当然不同的执行语句调用的Executor是不同的,这里仅供参考。DefaultSqlSession之前,即调用mapper接口时,实际是调用MapperProxy的invoke方法,MapperProxy再调用MapperMethod的execute方法,MapperMethod才会调用DefaultSqlSession。
查询过程.png