mybatis用于和数据库交互层面,我们只需定义好Mapper接口,在业务层需要的地方通过@Autowird引入对应的XxxMapper即可,很方便。为什么@Autowird可以从容器中拿到XxxMapper对象呢?其实是mybatis使用动态代理帮我们生成了XxxMapper的代理对象!下面通过源码来看一下mybatis是如何操作的
1.从@MapperScan说起
@MapperScan属于mybatis的扫描包注解,主要是扫描XxxMapper,下面看@MapperScan的源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class) //这里导入了MapperScannerRegistrar
public @interface MapperScan {
String[] value() default {};
......
}
注意@MapperScan注解上面的@Import(MapperScannerRegistrar.class)
在这里导入了MapperScannerRegistrar类,这个类在容器初始化之前会被执行,它主要通过扫描@MapperScan中指定包名,通过层层调用,来生成当前包下XxxMapper的代理对象,并放入容器中。下面来看MapperScannerRegistrar的源码。
MapperScannerRegistrar 类实现了ImportBeanDefinitionRegistrar接口,
MapperScannerRegistrar的源码
在registerBeanDefinitions()方法中,主要是扫描@MapperScan中标注的包中的内容,把MapperFactoryBean注入容器
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>();
//***取到@MapperScan里边的值(即包名)
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));
}
//***spring的框架,暂时不说
scanner.registerFilters();
//***扫描basePackages(com.luban...)里的内容,源码如下“
scanner.doScan(StringUtils.toStringArray(basePackages));
}
/**
* {@inheritDoc}
*/
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
扫描basePackages(com.luban…)里的内容使用的doScan方法
doScan源码:主要是调用了spring的doScan方法(而非mybatis的),结果不是mybatis想要的,但是拿到了对应类型的BeanDefinitionHolder(bd),再由processBeanDefinitions转成Mybatis想要的
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//注意:这里的super.doScan调用的是spring的方法,
扫描某个包(basePackages)得到对应类型的BeanDefinitionHolder(bd),但是这里的(bd)类型不对
不是我们想要的,下面的processBeanDefinitions方法才是mybatis对这个bd做的处理,做成想要的bd。
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 {
//mybatis处理上边spring扫描的BeanDefinitionHolder(bd),成为mybatis想要的!
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
怎么处理的呢 ,mybatis的processBeanDefinitions源码如下
主要作用:遍历上边spring扫描的每一个XxxMapper,给每一个XxxMapper生成对应的MapperFactoryBean
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
//*****循环传进来的spring的doscan方法生成的bd
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 // mapper接口是Bean的原始类
// but, the actual class of the bean is MapperFactoryBean //,但是,该Bean的实际类是MapperFactoryBean
//****添加构造方法,设置接口名字为bean的类型
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
//****在bd中设置mybatis中的FactoryBean == > mapperFactoryBean ,接下来看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中的FactoryBean == > MapperFactoryBean 的源码
主要作用:为上边生成的每一个MapperFactoryBean ,通过 getObject() 方法获取每一个Mapper的代理对象,放入容器
//****implements FactoryBean实现了FactoryBean,注入容器中的对象是 getObject() 中返回的对象
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
//****未指定类型的XxxMapper
private Class<T> mapperInterface;
......
//*****通过构造方法注入XxxMapper,给mapperInterface赋值
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
......
//*****调用Mybatis的getSqlSession().getMapper(this.mapperInterface)方法,生成当前mapper的代理对象
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
2.Mybatis动态代理总结:
1.mybatis定义一个注解@MapperScan
2.在@MapperScan中通过@Import导入了MapperScannerRegistrar,使这个类在启动时被加载。
3.在MapperScannerRegistrar类中,先通过spring扫描得到多个BeanDefinition(bd),后由mybatis的processBeanDefinitions()方法处理上边的多个bd
4.遍历每一个处理后的bd,并添加构造方法,设置接口名字为bean的类型,至此bean类型确定。
5.为每一个处理后的bd,设置FactoryBean(其实就是MapperFactoryBean),
6.在MapperFactoryBean中的getObject()方法中,通过动态代理获取每一个XxxMapper的代理对象,因为上文@Import导入了MapperScannerRegistrar,所以这些代理对象在启动时会被加入到容器,后续在业务层我们就可以随时调用了!
至此,MyBatis通过MapperFactoryBean为每一个XxxMapper生成代理对象结束!