今天就大概讲一下mybatis-spring的主要执行流程;
mybatis-spring的官网http://mybatis.org/spring/zh/index.html;
作用,简单说,就是通过配置,生成mapper代理类,交给spring管理,
今天这篇文章主要讲:扫描mapper接口,注入spring容器,生成代理类的入口,具体怎么生成没有讲;
举个例子:xml配置需要配置SqlSessionFactoryBean,和MapperScannerConfigurer,MapperScannerConfigurer就是对路径进行解析的
<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:mapping/*.xml"></property>
</bean>
<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.txn.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
MapperScannerConfigurer是实现了BeanDefinitionRegistryPostProcessor,通过postProcessBeanDefinitionRegistry方法获取到BeanDefinitionRegistry,然后还是通过ClassPathMapperScanner的scan方法进行注册bean,可以先看下面在回过头来看MapperScannerConfigurer类,是一样的…
@MapperScan(value = {"com.study.springbootplus.**.mapper"})
@Bean
public SqlSessionFactory sqlSessionFactory() {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource());
return factoryBean.getObject();
}
通过MapperScan注解将通过注解引入MapperScannerConfigurer类,然后通过实现ImportBeanDefinitionRegistrar,获取MapperScan注解属性,然后new ClassPathMapperScanner来对mapper进行注册的,可以先看后面,然后在回过头来看;
不管怎么配置,SqlSessionFactoryBean都是要单独配置的;
SqlSessionFactoryBean封装了mybatis的配置文件,数据源,解析mapperxml文件等等;
在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。
而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建。
通常,在 MyBatis-Spring 中,你不需要直接使用 SqlSessionFactoryBean 或对应的 SqlSessionFactory。相反,session 的工厂 bean 将会被注入到 MapperFactoryBean 或其它继承于 SqlSessionDaoSupport 的 DAO(Data Access Object,数据访问对象)中。
在官网中专门讲了SqlSessionFactoryBean,http://mybatis.org/spring/zh/factorybean.html
我的简单分析:https://yidajava.blog.csdn.net/article/details/108184170
@MapperScan(value = {"com.study.springbootplus.**.mapper"})
一般是通过mapperscan来扫描mapper接口,然后对接口生成对应的对应的代理类,注入spring容器中;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
注解使用了@Import(MapperScannerRegistrar.class),主要功能就看MapperScannerRegistrar类,MapperScannerRegistrar实现了两个接口,一个ImportBeanDefinitionRegistrar,顾名思义就是动态往spring容器注入bean,ResourceLoaderAware就是资源加载器,就是用来处理包扫描的,可以获取,工程的资源或者外部资源等等…
主要方法registerBeanDefinitions,能够拿到所有注解,和bean注册器.
这里新建了ClassPathMapperScanner ,不管是xml配置还是注解,解析都是靠这个类.
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//获取MapperScan注解的属性值
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
//初始化ClassPathMapperScanner,主要用来通过这个类来生成对应的mapper代理类,实际是MapperFactoryBean,是一个fatoryBean,
//当spring使用的时候,会调用对应的getObject,来生成代理类
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// this check is needed in Spring 3.1
if (resourceLoader != null) {
scanner.setResourceLoader(resourceLoader);
}
//下面是获取MapperScan注解配置的值,获取之后加到ClassPathMapperScanner中
/**
* annotationClass : 这个是基于包下面扫描所有的接口类并注册
* markInterface : 基于包下面扫描所有接口类并注册
* nameGenerator : 在Spring的容器中,将使用BeanNameGenerator去命名检测到的组件。
* factoryBean: 提前指定MapperFactoryBean
* sqlSessionTemplateRef: 指定使用sqlSessionTemplateRef
* sqlSessionFactoryRef : 指定使用sqlSessionFactoryRef
* value: 就是扫描包下所有的接口
* basepackages : 基于包下面的扫描MyBatis的接口。注意是,只有是接口的将会被扫描注册,如果是具体的类将会被忽略。
* basePackageClasses : 这是一个安全替代basePackages()作为指定组件的扫描包。包下面的所有接口都将会被扫描。
*/
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));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
}
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
List<String> basePackages = new ArrayList<String>();
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));
}
//对配置annotationClass,markInterface标记提前进行注册,用来过滤
scanner.registerFilters();
// 主要解析工作了
scanner.doScan(StringUtils.toStringArray(basePackages));
}
看看scan方法,
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//通过父类进行解析,将对应的包下类解析成BeanDefinition,BeanDefinitionHolder内部维护了BeanDefinition,
// BeanDefinition就是spring的bean定义可以简单看做就是spring,bean的属性
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 {
//对bean定义进行处理
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
父类的scan方法就不讲了,是spring的方法,就是扫描包下所有类生成BeanDefinition;并注入spring容器中,
我们看对bean定义做的一些处理:
主要就是设置注入类的类型为mapperFactoryBean,
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
// 取到BeanDefinitionHolder维护的Bean定义,具体的实现GenericBeanDefinition
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接口是原始类,但是我们放在spring容器内部的实际是MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
definition.setBeanClass(this.mapperFactoryBean.getClass());
// addToConfig属性好像没啥用...
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
// 前面有设置sqlSessionFactoryRef,如果设置了引用就用设置的
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
// 前面有设置sqlSessionTemplateRef,如果设置了引用就用设置的
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
//如果都没有设置,就设置为类型注入
if (!explicitFactoryUsed) {
if (logger.isDebugEnabled()) {
logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
MapperFactoryBean类继承了SqlSessionDaoSupport,实现FactoryBean,继承SqlSessionDaoSupport重写了checkDaoConfig方法为了验证验证sqlSession,mapperInterface不为空,然后getObject方法就直接使用sqlSession来获取dao:
@Override
protected void checkDaoConfig() {
//验证sqlSession,mapperInterface不为空
super.checkDaoConfig();
notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
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();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public T getObject() throws Exception {
//获取SqlSession来获取mapper
return getSqlSession().getMapper(this.mapperInterface);
}
当获取对应的mapper的时候,实际是执行MapperFactoryBean的getObject,
而getSqlSession返回的就是mybatis配置的sqlsessionFactory;然后生成代理类就是走的mybaits里面了,
我这篇文章主要为了分析mybatis-spring中间件的功能,生成代理就简单过一下好了,
走到sqlSessionTemplate类,然后通过类org.apache.ibatis.session.Configuration类,而Configuration维护了MapperRegistry类,然后通过MapperRegistry获取存储的MapperProxyFactory,代理工厂类生成代理类:这里,代理就生成了,我们使用的mapper代理就是这样生成的:
官网:http://mybatis.org/spring/zh/index.html
我的github源码:https://github.com/stackXu/spring
我是fork的,大家可以自己fork源码就好了,https://github.com/mybatis/spring
通过上面,mybatis-spring的功能就介绍完了,这时候回到最开始,
一种通过注解方式新建ClassPathMapperScanner,
一种通过配置方式来新建ClassPathMapperScanner…应该很明了把;
这篇文章主要讲mybatis-spring中间件,很多具体的没怎么讲,如果想要更深的了解,可以看如下文章: