目录
前言
一、启动配置类
二、扫描Mapper接口,注册Bean定义
1.@MapperScan
2.MapperScannerConfigurer
三、实例化MapperFactoryBean
总结
在上一篇文章中分析了mybatis的启动流程,主要包括SqlSessionFactory的创建和configure配置文件的解析,然后是sqlSession的创建,最后完成了mapper动态代理对象的创建,本文主要分析在有spring参与的情况下,mybatis的启动流程。
在mybatis-spring程序中,需要引入SqlSessionFactory这个bean,用于后面SqlSession的创建。
@Configuration
@ComponentScan("com.mybatis.spring.service")
@MapperScan(basePackages = "com.mybatis.spring.mapper")
@EnableTransactionManagement
public class MyBatisConfig {
@Bean
public DataSource dataSource(){
Properties properties = new Properties();
InputStream is = MyBatisConfig.class.getClassLoader().getResourceAsStream("jdbc.properties");
try {
// 加载配置文件
properties.load(is);
} catch (IOException e) {
e.printStackTrace();
}
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(properties.getProperty("jdbc.driver"));
dataSource.setUrl(properties.getProperty("jdbc.url"));
dataSource.setUsername(properties.getProperty("jdbc.username"));
dataSource.setPassword(properties.getProperty("jdbc.password"));
return dataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource());
// factoryBean.setMapperLocations("classpath:mapper/*.xml");
return factoryBean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
首先从MapperScan注解分析,可以看到这个注解引入了MapperScannerRegistrar类,最终注册了MapperScannerConfigurer类。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {}
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
//将MapperScannerConfigurer注册到容器中,负责mapper接口的扫描
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
MapperScannerConfigurer主要实现了对mapper接口扫描,一般情况下,此时的SqlSessionFactory和SqlSessionTemplate都为空,如果在mapperScan注解中设置这些变量的值,就可以提前赋值。
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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
}
scanner.registerFilters();
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
doScan方法首先通过父类方法将所有符合要求的类取出,此处重写了父类的isCandidateComponent方法,只会扫描包中的接口。
public Set doScan(String... basePackages) {
//将componentScan包下的接口都扫描出来,不需要添加@mapper注解,继承了isCandidateComponent方法,只扫描其中的接口
Set beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
+ "' package. Please check your configuration.");
} else {
//处理扫描出来的接口,创建MapperFactoryBean,加入到容器中
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
然后调用processBeanDefinitions方法,处理扫描出来的类,注册为MapperFactoryBean,并设置根据类型自动赋值,此处为SqlSession创建的关键。
private void processBeanDefinitions(Set beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
+ "' mapperInterface");
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
//直接设置beandDefinition的类型是mapperFactoryBean类型,实例化时调用getObject方法,创建动态代理对象。
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
if (!explicitFactoryUsed) {
//设置属性注入为根据类型注入,在populateBean时会对属性自动赋值
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
definition.setLazyInit(lazyInitialization);
}
}
在配置类中引入了SqlSessionFactory,会创建一个DefaultSqlSessionFactory。
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource());
// factoryBean.setMapperLocations("classpath:mapper/*.xml");
return factoryBean.getObject();
}
前面提到,MapperFactoryBean设置为根据类型自动注入,继承了SqlSessionDaoSupport,其中有一个属性为SqlSessionFactory,在populateBean的过程中,会调用该属性的set方法,却完成了对SqlSessionTemplate的赋值?????
在加载MapperFactoryBean的过程中,将自动装配类型设置为按照类型装配,从而能够实现各种set方法的调用,如setSqlSessionFactory
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
}
}
protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
MapperFactoryBean继承了DaoSupport,而DaoSupport又实现了InitializingBean接口,在bean实例化过程中,调用DaoSupport的afterPropertiesSet方法,然后调用MapperFactoryBean的checkDaoConfig方法,然后又调用了addMapper方法,实现了将接口中和对应xml文件中的方法及sql加入到mapperStatement中。
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接口对应的mapperStatement
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();
}
}
}
MapperFactoryBean本身是FactoryBean,在调用getBean方法时会调用getObject方法,最终得到一个mapper的代理对象,关于代理对象的展开将在以后的文章中讲述。
public T getObject() throws Exception {
//调用的时候已经完成了整体configure,sqlSessionFactory,sqlsession的创建,然后获取mapper,构建动态代理
return getSqlSession().getMapper(this.mapperInterface);
}
本文主要讲述了在有spring参与的情况下,MapperFactoryBean的注册,属性注入和初始化过程中的一些重要节点,主要是SqlSessionFactory和SqlSession的创建,mapperStatement的生成,最后是mapper代理对象的创建,如有不足之处,还请大家指出。。