Mybatis加载Mapper
注册方式 | 注册时机 | 特点 |
---|---|---|
@MapperScan |
Bean定义阶段注册接口定义 | 批量注册,推荐方式 |
@Mapper (接口注解) |
同 @MapperScan |
需每个接口单独标注 |
XML 配置 <mapper> |
MyBatis 初始化时 | 传统方式,不依赖 Spring 容器 |
SqlSessionTemplate 直接获取 |
调用时 | 编程式获取,不自动注册为 Bean |
@MapperScan注册mapper
// 使用:在启动类上添加注解
@MapperScan("com.zy.**.mapper")
@Import(MapperScannerRegistrar.class) // 此处使用了@Import注解,用于导入其他配置
@Repeatable(MapperScans.class)
public @interface MapperScan {
...;
}
/**
1.指示要导入的一个或多个组件,通常是@Configuration类。
2.提供与Spring XML中的<import/>元素等效的功能。
3.允许导入@Configuration类,ImportSelector和ImportBeanDefinitionRegistrar实现,以及常规组件类(从4.2开始,类似于AnnotationConfigApplicationContext.register)。
4.导入的@Configuration类中声明的@Bean定义应该使用@Autowired注入来访问。也可以自动连接声明bean的配置类实例。
5.如果需要导入XML或其他非@Configuration bean定义资源,请改用@ImportResource注解。
*/
public @interface Import {
/**
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
}
// 实现了ImportBeanDefinitionRegistrar接口,此处涉及到Spring加载BeanDefinition的逻辑
public class MapperScannerRegistrar
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private ResourceLoader resourceLoader;
// 重写ImportBeanDefinitionRegistrar 接口中的registerBeanDefinitions 方法
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
// 1. 获取@MapperScan注解属性
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
// 2. 实际注册逻辑
if (mapperScanAttrs != null) {
registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
generateBaseBeanName(importingClassMetadata, 0));
}
}
/**
annoMeta 被注解类的元数据
annoAttrs @MapperScan 注解的属性值
registry : Spring bean 定义注册器
beanName :要注册的 Bean 名称
*/
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
// 创建MapperScannerConfigurer 的通用Bean定义构建器。此处还只是构造builder。
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
// 启用属性占位符处理(如 ${jdbc.url})
builder.addPropertyValue("processPropertyPlaceHolders", true);
// 注解类过滤:如果设置了annotationClass(如@Mapper),则只扫描带该注解的接口
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
// 标记接口过滤:如果设置了 markerInterface, 则只扫描实现该接口的Mapper
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
builder.addPropertyValue("markerInterface", markerInterface);
}
// Bean 名称生成器
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
}
// 工厂类覆盖
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
}
// SQL 会话模板引用:支持指定特定的 SqlSessionTemplate Bean
String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
if (StringUtils.hasText(sqlSessionTemplateRef)) {
builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
}
// SQL 会话工厂引用
String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
if (StringUtils.hasText(sqlSessionFactoryRef)) {
builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
}
List<String> basePackages = new ArrayList<>();
// 添加 value 属性值(包路径)
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
// 添加 basePackages 属性值
basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
.collect(Collectors.toList()));
// 添加basePackageClasses的包路径
basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
.collect(Collectors.toList()));
// 使用默认配置类所在包
if (basePackages.isEmpty()) {
basePackages.add(getDefaultBasePackage(annoMeta));
}
// 延迟初始化
String lazyInitialization = annoAttrs.getString("lazyInitialization");
if (StringUtils.hasText(lazyInitialization)) {
builder.addPropertyValue("lazyInitialization", lazyInitialization);
}
// Bean 作用域
String defaultScope = annoAttrs.getString("defaultScope");
if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {
builder.addPropertyValue("defaultScope", defaultScope);
}
// 设置扫描包路径(逗号分割)
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
// for spring-native
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册Bean 定义。上述都是在配置builder, 此处才开始注册bean definition
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
}
MapperScannerConfigurer 作为BeanDefinitionRegistryPostProcessor执行, 中重写了 postProcessBeanDefinitionRegistry 方法,最终调用doScan(basePackages);扫描Mapper
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 1. 调用父类扫描方法获取Bean定义
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
logger.warn("No MyBatis mapper was found...");
} else {
// 2. 处理扫描到的Bean定义
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
// 省略部分代码
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
for (BeanDefinitionHolder holder : beanDefinitions) {
GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
// 3. 获取Mapper接口名
String mapperClassName = definition.getBeanClassName();
// 4. BeanClass 转换为 MapperFactoryBean
// 关键步骤1:添加构造参数(原始Mapper接口)
definition.getConstructorArgumentValues().addGenericArgumentValue(mapperClassName);
try {
// 关键步骤2:设置 MapperInterface 属性(Spring Native 兼容)
definition.getPropertyValues().add("mapperInterface", Resources.classForName(beanClassName));
} catch (ClassNotFoundException ignore) {
// ignore
}
// 关键步骤3:替换 Bean 类为 MapperFactoryBean
definition.setBeanClass(this.mapperFactoryBeanClass);
// 省略部分SqlSessionFactory 、sqlSessionTemplate相关内容。优先级:SqlSessionTemplate > SqlSessionFactory
// 5. 设置自动装配模式:当未显示配置工厂/模板时,启用按类型自动装配
if (!explicitFactoryUsed) {
LOGGER.debug("Enabling autowire by type...");
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
}
转换前Bean定义:
BeanDefinition {
beanClass = com.example.UserMapper // 原始接口
scope = singleton
...
}
转换后 Bean 定义
BeanDefinition {
beanClass = MapperFactoryBean // 替换为工厂类
constructorArgs = [com.example.UserMapper] // 原始接口作为构造参数
properties = {
sqlSessionFactory = ref("sqlSessionFactory") // 自动装配
addToConfig = true
}
...
}
实际效果:
// 用户代码
@Autowired
UserMapper userMapper;
// 实际发生
MapperFactoryBean factoryBean = new MapperFactoryBean(UserMapper.class);
factoryBean.setSqlSessionFactory(sqlSessionFactory);
UserMapper proxy = factoryBean.getObject(); // 返回 MyBatis 代理
// MapperFactoryBean 的 getObject()
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface); // 此处就是获取的代理类对象
}
// 从 @Autowired 到factoryBean.getObject(); 的过程:
@Autowired
-> AutowiredAnnotationBeanPostProcessor.postProcessProperties()
-> InjectionMetadata.inject()
-> DefaultListableBeanFactory.doGetBean()
-> AbstractBeanFactory.getObjectForBeanInstance(),发现 MapperFacoryBean 是 UserMapper 类型的工厂
-> AbstractBeanFactory..getObjectFromFactoryBean()
-> FactoryBeanRegistrySupport.doGetObjectFromFactoryBean()
-> FactoryBean<?>.getObject()