之前用过springboot 整合mybatis 觉得非常神奇,那么mybatis 的mapper 是怎么加入spring 容器的呢。
这里我们下载jar 包看一下
我们先看注解包下面的,MapperScan 这个注解。
看一下注释里面的用法,要创建数据源,事务管理器,sqlsession 会话等。
这里我们主要看看,MapperScannerRegistrar , 和对应的basePackages
我们点进去看看MapperScannerRegistrar 的具体功能
核心就是注册registerBeanDefinition,BeanDefinition 就是bean 的元数据,定义java bean 的一些属性
源码的注释翻译也是这样的。也就说我们只要注册成功,也就可以使用了。
ClassPathMapperScanner,是类注册器,
里面也有两个方法,一个过滤,一个不过滤。过滤适用于自定义注解的,稍后给你们演示一下。 这里main还有核心的doscan , 就是对bean 做处理,返回一个bean 的集合
这里mabatis 用了动态代理重写了,所以他要初始化加载数据源,还要进行参数的绑定等。我们这里直接用父类的supper.doscan() 就行。
下面我们就按照他的模式仿写一下。首先定义一个枚举,
package com.msxf;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
/**
* Base packages to scan for MyBatis interfaces. Note that only interfaces
* with at least one method will be registered; concrete classes will be
* ignored.
*/
String[] basePackages() default {};
}
同理上面也有一个scanner 的注册器。
package com.msxf;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private ResourceLoader resourceLoader;
/**
* {@inheritDoc}
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
if (resourceLoader != null) {
scanner.setResourceLoader(resourceLoader);
}
List<String> basePackages = new ArrayList<String>();
for (String pkg : annoAttrs.getStringArray("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
scanner.addIncludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return true;
}
});
scanner.addExcludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
String className = metadataReader.getClassMetadata().getClassName();
return className.endsWith("package-info");
}
});
scanner.scan(StringUtils.toStringArray(basePackages));
}
/**
* {@inheritDoc}
*/
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
这样就结束啦,在springboot 的启动类加上
在写一个接口调用一下
成功了。