MyBatis整合MyBatis源码设计很多Spring IOC的内容,要想明白MyBatis如何整合进Spring需要对Spring IOC的加载流程和扩展点很熟悉,详情可以看SpringIOC容器加载流程和源码剖析
Demo
首先引入spring-mybatis依赖包
| MyBatis-Spring | MyBatis | Spring Framework | Spring Batch | Java |
|---|---|---|---|---|
| 2.0 | 3.5+ | 5.0+ | 4.0+ | Java 8+ |
| 1.3 | 3.4+ | 3.2.2+ | 2.1+ | Java 6+ |
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
配置类
@Configuration
@MapperScan(basePackages = {"com.dm.mapper"})
@ComponentScan(basePackages = {"com.dm"})
@Repository
public class MyBatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws IOException {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource());
// MyBatis配置文件路径
factoryBean.setConfigLocation(new ClassPathResource("mybatis/mybatis-config.xml"));
// Mapper映射文件路径
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/*.xml"));
return factoryBean;
}
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername("root");
dataSource.setPassword("xxxxxxx");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://xx.xx.xx.xx:3306/test");
return dataSource;
}
}
User实体类
@Data
@Builder
@AllArgsConstructor
@ToString
public class User implements Serializable {
private Integer id;
private String name;
private Integer age;
}
UserMapper.java
public interface UserMapper {
User selectById(@Param(value = "id") Integer id);
List<User> selectAllUser();
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dm.mapper.UserMapper">
<resultMap id="result" type="com.dm.entity.User">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="name" jdbcType="VARCHAR" property="name"/>
<result column="age" jdbcType="INTEGER" property="age"/>
</resultMap>
<select id="selectById" resultMap="result">
select * from user
<where>
<if test="id > 0">
and id=#{id}
</if>
</where>
</select>
<select id="selectAllUser" resultType="com.dm.entity.User">
select * from user
</select>
</mapper>
测试类
public class MainStarter {
public static void main(String[] args) {
// 加载spring上下文
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MyBatisConfig.class);
UserMapper bean = (UserMapper) ioc.getBean("userMapper");
System.out.println(bean.selectById(1));
}
}
我们发现这和我们普通spring bean加载有2个区别,配置类上加了@MapperScan,和注入了一个SqlSessionFactoryBean(其实注入的bean不叫sqlSessionFactoryBean而是sqlSessionFactory)
注入SqlSessionFactoryBean源码
这里我们进入SqlSessionFactoryBean发现它实现了FactoryBean和InitializingBean
实现FactoryBean
实现了FactoryBean说明它可能更改了类型,我们来到getObject()方法
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
// 返回我们的sqlSessionFactory对象
return this.sqlSessionFactory;
}
从这也可以看出容器中容器中有sqlSessionFactory 这个bean
实现InitializingBean
InitializingBean在bean初始化之后调用,调用afterPropertiesSet
具体调用逻辑AnnotationConfigApplicationContext->refresh->finishBeanFactoryInitialization->preInstantiateSingletons->getBean->doGetBean->createBean->doCreateBean->initializeBean->invokeInitMethods
可直接跟到AbstractAutowireCapableBeanFactory#invokeInitMethods
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();
}
/**
* 构建我们的sqlSessionFactory的实例
*/
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
// 声明一个Configuration对象用于保存mybatis的所有的配置信息
final Configuration targetConfiguration;
XMLConfigBuilder xmlConfigBuilder = null;
// 判断当前的SqlSessionFactoryBean是否在配置@Bean的时候 factoryBean.setConfiguration();
if (this.configuration != null) {
// 把配置的SqlSessionFactoryBean配置的configuration 赋值给targetConfiguration
targetConfiguration = this.configuration;
if (targetConfiguration.getVariables() == null) {
targetConfiguration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
targetConfiguration.getVariables().putAll(this.configurationProperties);
}
}
else if (this.configLocation != null) {
//创建我们xml配置构建器对象,对mybatis/mybatis-config.xml配置文件进行解析 在这里以及把我们的mybaits-config.xml解析出要给document对象
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
// 因为我们在创建XMLConfigBuilder的时候已经把我们的Configuration对象创建出来了
targetConfiguration = xmlConfigBuilder.getConfiguration();
} else {
LOGGER.debug(
() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
targetConfiguration = new Configuration();
// 判断configurationProperties不为空,那么就调用targetConfiguration.set方法 把configurationProperties注入到Configuration对象中
Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
}
// objectFactory不为空,那么就调用targetConfiguration.set方法 把objectFactory注入到Configuration对象
Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
// objectWrapperFactory不为空,那么就调用targetConfiguration.set方法把 ObjectWrapperFactory注入到Configuration对象中
Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
// vfs不为空,那么就调用targetConfiguration.set方法把 vfs注入到Configuration对象中
Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);
if (hasLength(this.typeAliasesPackage)) {
//第一步:扫描我们typeAliasesPackage 包路径下的所有的实体类的class类型 第二步:进行过滤,然后注册到Configuration的别名映射器中
scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
.filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
.filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
}
/**
* 判断我们SqlSessionFactory是否配置了typeAliases(class类型) 一般typeAliasesPackage配置好了 就没有必要配置typeAliases
* 注册到Configuration的别名映射器中
*/
if (!isEmpty(this.typeAliases)) {
Stream.of(this.typeAliases).forEach(typeAlias -> {
targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
});
}
// 把我们自定义的插件注册到我们的mybatis的配置类上
if (!isEmpty(this.plugins)) {
Stream.of(this.plugins).forEach(plugin -> {
targetConfiguration.addInterceptor(plugin);
LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
});
}
// 扫描我们自定义的类型处理器(用来处理我们的java类型和数据库类型的转化) 并且注册到我们的 targetConfiguration(批量注册)
if (hasLength(this.typeHandlersPackage)) {
scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
.filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
.forEach(targetConfiguration.getTypeHandlerRegistry()::register);
}
// 通过配置<TypeHandlers></TypeHandlers>的形式来注册我们的类型处理器对象
if (!isEmpty(this.typeHandlers)) {
Stream.of(this.typeHandlers).forEach(typeHandler -> {
targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
});
}
if (!isEmpty(this.scriptingLanguageDrivers)) {
Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
targetConfiguration.getLanguageRegistry().register(languageDriver);
LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
});
}
Optional.ofNullable(this.defaultScriptingLanguageDriver)
.ifPresent(targetConfiguration::setDefaultScriptingLanguage);
// 设置数据库厂商
if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls
try {
targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
// 若二级缓存不为空,注册二级缓存
Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);
if (xmlConfigBuilder != null) {
try {
// 真正的解析我们的配置(mybatis-config.xml)的document对象
xmlConfigBuilder.parse();
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();
}
}
// 为我们的configuration设置一个环境变量
targetConfiguration.setEnvironment(new Environment(this.environment,
this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
this.dataSource));
// 循环我们的mapper.xml文件
if (this.mapperLocations != null) {
if (this.mapperLocations.length == 0) {
LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
} else {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
// 真正的循环我们的mapper.xml文件
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
}
// 通过建造者模式构建我们的SqlSessionFactory对象 默认是DefaultSqlSessionFactory
return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}
@MapperScan源码
首先我们SqlSessionFactoryBean通过上面的@Bean注入进容器了,那么现在只差把UserMapper注入到容器中就可以了,但是问题来了,众所周知Mapper.java是接口类,而接口类是不可以解析成BeanDefinition,既然不可以解析成BeanDefinition也就不存在注入到容器,至于接口不能被注入大家可以来到ClassPathScanningCandidateComponentProvider#isCandidateComponent
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
// metadata.isIndependent()=顶级类、嵌套类、静态内部类
// metadata.isConcrete() =非接口、非抽象类
// metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName() = 抽象类并且必须方法中有@LookUp
return (metadata.isIndependent() && (metadata.isConcrete() ||
(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}
我们来看下spring-mybatis是如何解决这个问题的
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
从注解上看有个熟悉的@Import,它实现了ImportBeanDefinitionRegistrar接口,这个接口大家都知道是用来注册Bean定义的
我们来到实现了ImportBeanDefinitionRegistrar接口的registerBeanDefinitions
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 从我们传入的配置类中来解析@MapperScan注解信息,然后吧MapperScan注解的属性转化为AnnotationAttributes类型
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
// 若上一步解析出来的mapperScanAttrs不为空(说明配置类上加了@MapperScan注解)
if (mapperScanAttrs != null) {
registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
}
}
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
// 创建bean定义构造器 通过够构造器来构建出我们的bean定义<MapperScannerConfigurer> 应用到的设计模式[建造者模式]
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
//手动为我们MapperScannerConfigurer 开启processPropertyPlaceHolders属性为true
builder.addPropertyValue("processPropertyPlaceHolders", true);
// 为我们的MapperScannerConfigurer 解析我们@MapperScanner 指定扫描的的注解类型
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
// 是否配置了标记接口
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
builder.addPropertyValue("markerInterface", markerInterface);
}
// 设置MapperScannerConfigurer的beanName 生成器对象
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
}
// 解析@MapperScan注解属性MapperFactoryBean 设置到MapperScannerConfigurer 声明一个自定义的MapperFactoryBean 返回一个代理对象
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
}
// 解析@MapperScan 的sqlSessionTemplateRef到底使用是哪个sqlSessionTemplate 设置到MapperScannerConfigurer 多数据源的情况下需要指定
String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
if (StringUtils.hasText(sqlSessionTemplateRef)) {
builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
}
// 解析@MapperScan的sqlSessionFactoryRef属性 设置到 MapperScannerConfigurer 多数据情况下的话 ,需要指定使用哪个 sqlSessionFactory
String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
if (StringUtils.hasText(sqlSessionFactoryRef)) {
builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
}
// 解析@MapperScan 扫描的的包或者是class对象
List<String> basePackages = new ArrayList<>();
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
.collect(Collectors.toList()));
// 指定MapperScannerConfigurer 是否为懒加载
String lazyInitialization = annoAttrs.getString("lazyInitialization");
if (StringUtils.hasText(lazyInitialization)) {
builder.addPropertyValue("lazyInitialization", lazyInitialization);
}
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
// 为我们的容器中注册了MapperScannerConfigurer的接口
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
这里做的事情很简单-使用建造者模式构建MapperScannerConfigurer并注册到BeanDefinition,然后进入MapperScannerConfigurer类中发现它实现了BeanDefinitionRegistryPostProcessor,InitializingBean(这里并没有什么用)
大家都知道BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry会在refresh->invokeBeanFactoryPostProcessors中会实例化刚刚注册的MapperScannerConfigurer bean定义并且会调用postProcessBeanDefinitionRegistry
MapperScannerConfigurer#postProcessBeanDefinitionRegistry
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 若MapperScannerConfigurer属性的processPropertyPlaceHolders为ture的时候,就进行processPropertyPlaceHolders();
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
// 显示的new 一个ClassPathMapperScanner 包扫描器对象 这个对象是mybaits继承了spring的ClassPathBeanDefinitionScanner为我们扫描器指定@MapperScan属性
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();
// 扫描我们@MapperScan指定的路径下的bean定义信息 先会去调用ClassPathMapperScanner.scan()方法然后调用ClassPathMapperScanner#doScan方法然后会调用到父类的ClassPathBeanDefinitionScanner#doScan,在父类的doScan中会调用isCandidateComponent(AnnotatedBeanDefinition beanDefinition)方法,而这个方法在子类被重写了所以这里调到子类isCandidateComponent,而子类中的不会过滤掉接口,所以接口可以被扫描注册成beanDefinition了
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
ClassPathMapperScanner#doScan
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 调用父类ClassPathBeanDefinitionScanner 来进行扫描
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
// 若扫描后 我们mapper包下有接口类,那么扫描bean定义就不会为空
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
+ "' package. Please check your configuration.");
} else {
// 这里很重要
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
ClassPathMapperScanner#processBeanDefinitions(beanDefinitions);
Mapper生成代理对象
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
// 循环我们所有扫描出mapper的bean定义出来
for (BeanDefinitionHolder holder : beanDefinitions) {
// 获取我们的bean定义
definition = (GenericBeanDefinition) holder.getBeanDefinition();
// 获取我们的bean定义的名称
String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
+ "' mapperInterface");
// 设置ConstructorArgumentValues 会通过构造器初始化对象
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
// 设置成factoryBean
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
/**
* 为我们的Mapper对象绑定我们的sqlSessionFactory引用 说白了就是我们的UserMapper(实际上是就是为我们的MapperFactoryBean添加一个sqlSessionFactory的属性)
* 然后SpringIoc在实例话我们的MapperFactoryBean的时候会经历populate()方法为我么你的UserMapper(MapperFactoryBean)
* 的sqlSessionFactory赋值(调用set方法)
*/
boolean explicitFactoryUsed = false;
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;
}
/**
* 为我们的Mapper对象绑定我们的sqlSessionTemplate属性对象
* 说白了就是我们的UserMapper(实际上是就是为我们的MapperFactoryBean添加一个sqlSessionTemplate的属性)
* 然后SpringIoc在实例话我们的MapperFactoryBean的时候会经历populate()方法为我么你的UserMapper(MapperFactoryBean)
* 的sqlSessionTemplate赋值(调用set方法)
*/
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.");
}
// 将sqlSessionTemplate通过AUTOWIRE_BY_TYPE自动装配
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
// 设置bean定义的加载模型(是否为懒加载)
definition.setLazyInit(lazyInitialization);
}
}
此时Mapper集成进Spring中了。
总结
Spring整合Mybatis最重要的点就是怎么把代理Mapper注入到spring容器中。要解决注入问题首先解决2个问题。
- 首先Mapper是一个接口,无法生成BeanDefinition
- Mapper需要生成代理对象
1,Mybatis利用@Import注解注入一个BeanDefinitionRegistryPostProcessor,而这个BeanDefinition是包含注册功能的,在这个BeanDefinitionRegistryPostProcessor我们可以实现扫描功能并重写isCandidateComponent,使接口可以被扫描并注册成一个bean定义。
2,Mapper生成代理对象,利用FactoryBean进行改写,使只要getBean,便拿到了getObject改写的代理对象MapperProxy