前言
在解析代码之前,我们先回顾一下spring容器是怎么管理bean的
对bean(xml配置,注解标识)进行解析生成BeanDefinition对象,注册到BeanDefinitionRegistry容器中
实例化容器中所有实现了BeanFactoryPostProcessor接口的bean,执行postProcessBeanFactory方法
实例化容器中所有的单例bean,并填充bean的属性
执行所有实现了BeanPostProcessor接口bean的postProcessBeforeInitialization方法
执行bean的初始化方法(定义bean时配置的init-method方法,或实现InitializingBean接口重写的afterPropertiesSet方法)
执行所有实现了BeanPostProcessor接口bean的postProcessAfterInitialization方法
Mapper文件是通过MapperScan注解将Mapper扫描放到Spring容器中的,我们就从MapperScan注解开始分析
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class)
public @interface MapperScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends Annotation> annotationClass() default Annotation.class;
Class<?> markerInterface() default Class.class;
String sqlSessionTemplateRef() default "";
String sqlSessionFactoryRef() default "";
Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
String lazyInitialization() default "";
String defaultScope() default "";
MapperScans注解导入了MapperScannerRegistrar类,MapperScannerRegistrar类是ImportBeanDefinitionRegistrar接口的实现类,重写了registerBeanDefinitions方法
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
public MapperScannerRegistrar() {
}
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
this.registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
}
}
registerBeanDefinitions中通过AnnotationAttributes工具类获取了MapperScan注解的属性,然后调用重载的registerBeanDefinitions方法
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
private boolean addToConfig = true;
private SqlSessionFactory sqlSessionFactory;
private SqlSessionTemplate sqlSessionTemplate;
private String sqlSessionTemplateBeanName;
private String sqlSessionFactoryBeanName;
private Class<? extends Annotation> annotationClass;
private Class<?> markerInterface;
private MapperFactoryBean<?> mapperFactoryBean = new MapperFactoryBean();
public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
super(registry, false);
}
这个是mybatis继承spring的ClassPathBeanDefinitionScanner类扫描器,加了一些mybatis需要用到的属性
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
List<String> basePackages = new ArrayList();
String[] var10 = annoAttrs.getStringArray("value");
int var11 = var10.length;
int var12;
String pkg;
for(var12 = 0; var12 < var11; ++var12) {
pkg = var10[var12];
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
var10 = annoAttrs.getStringArray("basePackages");
var11 = var10.length;
for(var12 = 0; var12 < var11; ++var12) {
pkg = var10[var12];
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
调用的doScan方法
scanner.doScan(StringUtils.toStringArray(basePackages));
在registerBeanDefinitions中获取注解中的属性信息,然后设置给ClassPathBeanDefinitionScanner的对象。遍历注解中设置的包路径放到集合中
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
调用父类的扫描包方法
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
this.processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
Iterator var3 = beanDefinitions.iterator();
while(var3.hasNext()) {
BeanDefinitionHolder holder = (BeanDefinitionHolder)var3.next();
从holder里获取到BeanDefinition
GenericBeanDefinition definition = (GenericBeanDefinition)holder.getBeanDefinition();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface");
}
设置构造函数的参数为当前类的类名,也就是mapper接口
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
设置beanClass为Mapper的工厂类
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;
}
上面的代码首先调用了父类的doscan方法,获取到了BeanDefinitionHolder,BeanDefinitionHolder里面有BeanDefinition和beanNmae以及别名。然后从BeanDefinitionHolder里面获取到BeanDefinition。设置它的beanClass为this.mapperFactoryBean.getClass(),spring容器在实例化bean的时候根据BeanDefinition的beanClass属性创建,所以会创建MapperFactoryBean对象。又给BeanDefinition的属性增加了几个PropertyValue配置。在创建MapperFactoryBean的时候会自动填充这些属性。
MapperFactoryBean是继承了FactoryBean的工厂类。工厂类返回的对象是getObject对象返回的,所以我们根据指定的类型获取Mapper的时候,是从容器中拿到MapperFactoryBean的getObject()方法返回的对象
public T getObject() throws Exception {
return this.getSqlSession().getMapper(this.mapperInterface);
}
getObject方法是从SqlSession里面的configuration属性中获取Mapper类,我们的Mapper通过动态代理创建好后会放在configuration主配置文件类的mapperRegistry中
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
总结
整个流程就是,MapperScan注解中引入了MapperScannerRegistrar类。这个类创建了继承ClassPathBeanDefinitionScanner的ClassPathMapperScanner类。获取注解里面的包路径放到集合里面,调用父类的doscan方法获取BeanDefinitionHolder对象,给后面需要实例化的BeanDefinition对象设置构造参数为当前mapper文件的类名,设置BeanClass为当前对象的mapperFactoryBean属性。当我们根据Mapper类型从Spring容器中获取FactoryBean时,获取到的是FactoryBean的getObject()方法返回的对象。之前看到apollo的@EnableApolloConfig注解,也是使用了类似的方式去管理对象,有兴趣的可以去研究一下