MyBatis 传统模式
public static void main(String[] args) throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过 JDK 动态代理,生成代理对象
AdminMapper mapper = sqlSession.getMapper(AdminMapper.class);
List<Admin> admins = mapper.selectAll();
for (Admin admin : admins) {
System.out.println(admin.getId() + ", " + admin.getName());
}
}
其中 AdminMapper mapper = sqlSession.getMapper(AdminMapper.class);
就是通过 JDK 动态代理生成的代理对象,我们可以往下跟源码发现
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),
new Class[] { mapperInterface }, mapperProxy);
}
MyBatis 如何和 Spring 整合的(MyBatis-Spring 1.3.X 的实现)
一、演化一,FactoryBean
FactoryBean 的模式
@Component
public class MyFactoryBean implements FactoryBean<Object> {
@Override
public UserMapper getObject() throws Exception {
Object proxyInstance = Proxy.newProxyInstance(MyFactoryBean.class.getClassLoader(), new Class<?>[]{UserMapper.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String select = method.getAnnotation(Select.class).value();
System.out.println(method.getName() + " => " + select);
return null;
}
});
return (UserMapper) proxyInstance;
}
@Override
public Class<?> getObjectType() {
return UserMapper.class;
}
}
我们可以通过 FactoryBean 的模式来给 UserMapper 生成一个代理对象,这样虽然可以成功,但是,如果在还有其他的 Mapper 怎么处理呢?难道在写一个 FactoryBean 么?肯定不是
二、演化二,ImportBeanDefinitionRegistrar
通过 ImportBeanDefinitionRegistrar 接口,我们可以想容器中注册 BeanDefinition,这也是 MyBatis 与 Spring 整合的方式
public class MyBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 这里可以用 扫描 来实现
List<Class<?>> mapperClasses = new ArrayList<>();
mapperClasses.add(UserMapper.class);
mapperClasses.add(OrderMapper.class);
for (Class<?> mapperClass : mapperClasses) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(MyFactoryBean.class);
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(mapperClass);
// 注册到容器
registry.registerBeanDefinition(mapperClass.getSimpleName(), beanDefinition);
}
}
}
public class MyFactoryBean implements FactoryBean<Object> {
private Class<?> mapperInterface;
public MyFactoryBean(Class<?> mapperInterface) {
this.mapperInterface = mapperInterface;
}
@Override
public Object getObject() throws Exception {
return Proxy.newProxyInstance(MyFactoryBean.class.getClassLoader(), new Class<?>[]{mapperInterface}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String select = method.getAnnotation(Select.class).value();
System.out.println(method.getName() + " => " + select);
return null;
}
});
}
@Override
public Class<?> getObjectType() {
return mapperInterface;
}
}
@Configuration
@ComponentScan("com.sourceflag.spring.mybatis")
@Import(MyBeanDefinitionRegistrar.class)
public static class Config {
}
MyBatis 整合 Spring 源码
MapperScan 注解
我们可以看到 MapperScan
上面也有一个 @Import(MapperScannerRegistrar.class)
注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
}
MapperScannerRegistrar 的实现
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private ResourceLoader resourceLoader;
/**
* {@inheritDoc}
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 获取 MapperScan 注解
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// this check is needed in Spring 3.1
if (resourceLoader != null) {
scanner.setResourceLoader(resourceLoader);
}
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
scanner.setAnnotationClass(annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
scanner.setMarkerInterface(markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
}
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
List<String> basePackages = new ArrayList<String>();
for (String pkg : annoAttrs.getStringArray("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : annoAttrs.getStringArray("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
// 注册 filters
scanner.registerFilters();
// 开始进行扫描
scanner.doScan(StringUtils.toStringArray(basePackages));
}
/**
* {@inheritDoc}
*/
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
看下 MyBatis 是如果定义扫描器的
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
// 我们可以看到传递的 useDefaultFilters 属性为 false
super(registry, false);
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
// 必须是接口
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
}
我们可以看到传递的 useDefaultFilters 属性为 false,表示不使用 Spring 中的默认过滤器,因为 Spring 默认不会使用接口来注册 BeanDefinition
我们继续看 MyBatis 注册的过滤器
从这里我们可以看出 MyBatis 会把所有的类(包括接口)都扫描出来
addIncludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return true;
}
});
doScan 的实现
经过 MyBatis 的过滤器和 isCandidateComponent 的判断,现在可以进行扫描了
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 经过 MyBatis 的过滤器和 isCandidateComponent 的判断,这里扫描出来的都是接口了
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
if (logger.isDebugEnabled()) {
logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
+ "' and '" + definition.getBeanClassName() + "' mapperInterface");
}
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
// 将 beanClass 作为 MapperFactoryBean 的构造方法参数
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
// 同时修改 beanClass 为 MapperFactoryBean
definition.setBeanClass(this.mapperFactoryBean.getClass());
definition.getPropertyValues().add("addToConfig", this.addToConfig);
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;
}
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.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
if (logger.isDebugEnabled()) {
logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
}
MyBatis-Spring 2.0.X 的实现
在 MyBatis-Spring-2.0.X 版本中并没有直接通过 registerBeanDefinitions
进行扫描,而是直接向容器中注册了一个 MapperScannerConfigurer
类,我们先看下这个类
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
}
我们可以看到 MapperScannerConfigurer
这类实现了 BeanDefinitionRegistryPostProcessor
接口,那么 Spring 在启动后,会找到 MapperScannerConfigurer
类,然后执行 postProcessBeanDefinitionRegistry
方法来进行扫描和注册,其他并没有变动,只是多做了一层
- 老版本:MapperScannerRegistrar => registerBeanDefinitions => scanner => 注册 BeanDefinition
- 新版本:MapperScannerRegistrar => registerBeanDefinitions => 注册 MapperScannerConfigurer => Spring 调用 BeanDefinitionRegistryPostProcessor 的后置处理器 => scanner => 注册 BeanDefinition
乍一看新版本反而还多做了一步,反而麻烦了,为什么要多注册一个 MapperScannerConfigurer,然后通过后置处理器的方式来进行扫描呢?
其实这里 MyBatis 提供了一种扩展机制,也就是说,我们可以不需要写 @MapperScan 注解来进行扫描,而通过 @Bean 的方式注册 MapperScannerConfigurer 来替代 @MapperScan 注解
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage("com.sourceflag.spring.mybatis");
return configurer;
}