Mybatis作为目前主流的数据库查询框架,他是如何集成在Spring当中的,可以给以后开发Spring扩展插件提供一个思路。
最近在学习Mybatis的源码,因此记录一下,以便以后再次学习。
首先Spring作为最流行的开发框架,所有程序员都可以去扩展,Mybatis就是利用了Spring的这些扩展点。
这里我就大致描述下Spring的执行过程
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(AppConfig.class);
ac.refresh();
UserDao app = ac.getBean(UserDao.class);
System.out.println(app.qryIntfConfig("1"));
这是spring最原始的获取bean的方式,而所有单例的bean的初始化都在前3行代码里,Spring中会有许多的BeanDefinition,顾名思义,这些BeanDefinition就是用来描述一个bean的,比如bean是否是单例,bean的类型,bean的属性,bean的注入方式,bean的初始化方法等等。Spring定义有Bean工厂,工厂就是根据这些Bean的定义来实例化bean,而bean工厂有很多的后置处理器,其中包括Spring内置的以及程序员扩展的,而mybatis的集成就利用了spring的bean工厂后置处理器BeanDefinitionRegistryPostProcessor,这个类集成自BeanFactoryPostProcessor,关于bean工厂的后置处理器这就不细讲了,其中有个最重要的bean工厂后置处理器就是ConfigurationClassPostProcessor。
@MapperScan(value = "com.wugao.dao", annotationClass = Repository.class)
@Configuration
public class DataSourceConfig {
@Bean()
public BasicDataSource dataSource(){
System.out.println("init datasource");
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=10.1.1.1)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=wbsdb)))");
dataSource.setUsername("test");
dataSource.setPassword("123456");
dataSource.setDriverClassName(OracleDriver.class.getName());
return dataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
System.out.println("init sqlSessionFactory");
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
return sqlSessionFactoryBean.getObject();
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
.....
}
要使用Mybatis首先我们需要开启扫描,本文主要讲解注解的扫描方式,@MapperScan,该注解需要指明扫描的包名,该注解中有另一个注解@Import(MapperScannerRegistrar.class),在Spring中注解被Import的类会被Spring内置的bean工厂后置处理器ConfigurationClassPostProcessor扫描到并注册成BeanDefinition,而MapperScannerRegistrar继承了ImportBeanDefinitionRegistrar类,该类的实现类的registerBeanDefinitions方法会在执行ConfigurationClassPostProcessor的后置处理器时被调用,具体方法如下,这个方法获取到MapperScanner中的属性,并且注册一个MapperScannerConfigurer的bean工程后置处理器,并把所有的MapperScan中的属性值添加到BeanDefinition的属性值中,以便后续实例化时使用。
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
}
}
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
// 创建一个BeanDefinitionBuilder用来产生BeanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
builder.addPropertyValue("markerInterface", markerInterface);
}
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);
}
String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
if (StringUtils.hasText(sqlSessionTemplateRef)) {
builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
}
String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
if (StringUtils.hasText(sqlSessionFactoryRef)) {
builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
}
List<String> basePackages = new ArrayList<>();
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
.collect(Collectors.toList()));
String lazyInitialization = annoAttrs.getString("lazyInitialization");
if (StringUtils.hasText(lazyInitialization)) {
builder.addPropertyValue("lazyInitialization", lazyInitialization);
}
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
// 注册BeanDefinition
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
注册好的Mybatis后置处理器,会在ac.refresh()方法中的invokeBeanFactoryPostProcessors被执行,该方法会执行Mybatis后置处理器的postProcessBeanDefinitionRegistry方法。
// Invoke factory processors registered as beans in the context.
// 初始化了自定义的beanFactoryPostProcessors,放进了beanDifinitionMap
invokeBeanFactoryPostProcessors(beanFactory);
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
}
scanner.registerFilters();
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
这个方法中创建了一个ClassPathMapperScanner,该类继承自Spring的ClassPathBeanDefinitionScanner类,这个类用来扫描指定条件的bean,如注解了@Component,@Configuration,@ComponentScan,@Import,@ImportResource的类,而Mybatis的ClassPathMapperScanner重写了他的部分方法registerFilters,isCandidateComponent,分别表示注册过滤器,以及过滤后扫除的bean是否满足一定条件
// 注册过滤器
public void registerFilters() {
boolean acceptAllInterfaces = true;
// 如果@MapperScan上指定了annotaionClass,那么表示注解了该注解的类会被扫描
// 比如指定为Mapper.class,那么只有注解了@Mapper的类会接口会被扫描
if (this.annotationClass != null) {
addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
acceptAllInterfaces = false;
}
// 指定了父接口的话,他的实现类会被扫描到
if (this.markerInterface != null) {
addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
@Override
protected boolean matchClassName(String className) {
return false;
}
});
acceptAllInterfaces = false;
}
// 如果注解没有指明annotaionClass和markerInterface,默认扫描所有的类,接口
if (acceptAllInterfaces) {
// default include filter that accepts all classes
addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
}
// 排除package-info.java
addExcludeFilter((metadataReader, metadataReaderFactory) -> {
String className = metadataReader.getClassMetadata().getClassName();
return className.endsWith("package-info");
});
}
// 如果扫出来的是接口并且不是内部类,则满足条件
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
扫描出来的所有类会被注册为BeanDefinition,而此处有一个非常重要的操作,就是Mybatis将bean的class设置为了它自定义的MapperFactoryBean,这个类实现了FactoryBean,后续可以通过它的getObject产生一个实例。同时如果指定了sqlSessionFactoryBeanName或者sqlSessionTemplateBeanName,为其绑定属性,否则设置bean为自动装配,这里的自动装配会在bean初始化属性注入时会使用。
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
+ "' mapperInterface");
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
// 是否指定了SqlSessionFactoryBeanName
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) {
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
// 设置懒加载,默认为true
definition.setLazyInit(lazyInitialization);
}
}
到这里,生成Mapper实例的BeanDefinition基本就生成了,后续在实例化及初始化过程中就会使用到这些BeanDefinition,里面包含了如何生成Maper的所有的信息,包括类,需要注入的SqlSessionFactory,注入方式等等,后面再写Mapper的实例化。