1.配置applicationContext.xml
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://122.152.131.141:3306/school" />
<property name="username" value="zw121" />
<property name="password" value="122334" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis.xml" />
<property name="mapperLocations">
<array>
<!-- 配置单个映射文件 -->
<!-- <value>classpath:mappers/UserMapper.xml</value> -->
<!-- 配置多个映射文件使用 * 通配符 -->
<value>classpath:mappers/*Mapper.xml</value>
</array>
</property>
<!-- 配置别名,使用包扫描 -->
<property name="typeAliasesPackage" value="com.qhf.pojo"></property>
</bean>
<!-- 扫描接口文件,其2选1 -->
<!-- 1.扫描接口文件,只能单个文件 -->
<!--
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.qhf.dao.UserMapper"></property>
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
-->
<!-- 2.扫描包下的接口文件,此方法简便 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.qhf.dao"></property>
</bean>
- 主要就是配置 SqlSessionFactoryBean 的初始化 bean 对象
- 还有 MapperFactoryBean 的初始化 bean 对象,此 bean 针对一个 mapper 接口
- MapperScannerConfigurer 的初始化 bean 对象,扫描包下的全部接口文件
2.SqlSessionFactoryBean
- 实现接口 FactoryBean 接口,getBean() 方法返回 sqlSessionFactory 工厂类对象。
- 实现 InitializingBean 接口,则 bean 初始化时会调用此类的 afterPropertiesSet() 方法。
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
//...
@Override
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
this.sqlSessionFactory = buildSqlSessionFactory();
}
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
}
- sqlSessionFactory 的初始化实际上就是调用 buildSqlSessionFactory() 方法时初始化的。
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration;
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
configuration.setVariables(this.configurationProperties);
}
if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
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 (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();
}
}
if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}
configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
if (this.databaseIdProvider != null) {
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
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");
}
}
return this.sqlSessionFactoryBuilder.build(configuration);
}
- 总体来说:
- 生成一个 Configuration 对象,此对象是 mybatis 的核心。
- 把配置的 configLocation(mybatis原始配置文件,整合之后基本不用配置),objectFactory,objectWrapperFactory,typeAliasesPackage(别名,省了configLocation 里别名的配置),typeAliases,plugins,typeHandlersPackage,typeHandlers,transactionFactory,databaseIdProvider,mapperLocations(扫描 xml 映射文件) 初始化和解析。
- 最后通过 sqlSessionFactoryBuilder.build(configuration) 方法把此 configuration 对象传给了 DefaultSqlSessionFactory 实例对象,其中 DefaultSqlSessionFactory 实现了 SqlSessionFactory 接口,是生产 SqlSession 的工厂。
public class SqlSessionFactoryBuilder {
//...
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
//...
}
- 当单独使用 mybatis 时通过 sqlSessionFactory.openSession() 来获取 sqlSession,才能操作数据库。
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//...
- 当交给 Spring 管理,可以通过下面方式获取 userMapper 对象
UserMapper userMapper = (UserMapper)context.getBean("userMapper");
或
@Autowired
UserMapper userMapper;
- 先看此配置,是对单个 mapper 接口的 bean 对象的创建。
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.qhf.dao.UserMapper"></property>
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
3.MapperFactoryBean
- 配置 MapperFactoryBean 的 bean 时的属性是 mapperInterface,sqlSessionFactory
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.qhf.dao.UserMapper"></property>
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
- <property name="sqlSessionFactory" ref="sqlSessionFactory"/>属性初始化时会调用 MapperFactoryBean 从父类 SqlSessionDaoSupport 中继承下来的 setSqlSessionFactory() 方法,并使用 SqlSessionTemplate 对 sqlSessionFactory 进行加工生产 sqlSession(通过动态代理)
-
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { if (!this.externalSqlSession) { this.sqlSession = new SqlSessionTemplate(sqlSessionFactory); } }
- MapperFactoryBean 实现了 InitializingBean 接口,则 bean 初始化时会调用此类的 afterPropertiesSet() 方法,此方法在其父类中。
public abstract class DaoSupport implements InitializingBean {
@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() 方法在 DaoSupport 的子类 MapperFactoryBean 中实现。
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
@Override
protected void checkDaoConfig() {
super.checkDaoConfig(); // notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
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();
}
}
}
}
- getSession() 方法返回 this.sqlSession,getConfiguration() 返回 Configuration 对象
- 再检查 Configuration 对象中是否已配置了 <property name="mapperInterface" value="com.qhf.dao.UserMapper"/> 此mapper 接口(因为手动配置,所以还是要检查。重点是 mybatis 中读取映射文件时解析到<mapper namespace="xxx.UserMapper">时会自动进行类型的注册)
- 如果没有的话便会通过 configuration.addMapper(this.mapperInterface) 方法进行 mapper 接口的注册
- 回到1中最后的问题:UserMapper userMapper = (UserMapper)context.getBean("userMapper"); 为什么能自动返回了 userMapper 对象?MapperFactoryBean 实现 FactoryBean 接口,重写了其接口方法来获取 mapper 对象。
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
}
4.MapperScannerConfigurer
- 同样是继承 InitializingBean,但是此方法没作用;重点在父接口 BeanDefinitionRegistryPostProcessor,BeanDefinitionRegistryPostProcessor实现了BeanFactoryPostProcessor接口,是Spring框架的BeanDefinitionRegistry的后处理器,用来注册额外的BeanDefinition。postProcessBeanDefinitionRegistry方法会在所有的BeanDefinition已经被加载了,但所有Bean还没有被创建前调用。BeanDefinitionRegistryPostProcessor经常被用来注册BeanFactoryPostProcessor的BeanDefinition。MapperScannerConfigurer 重写了其 postProcessBeanDefinitionRegistry() 方法。
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
@Override
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));
}
}
- 后续加上