spring和mybatis整合源码解析
作者:互联网
springboot和mybatis整合源码解析
简单的流程图
使用的是我们springboot,所以我们先要创建一个config类
@Configuration//指定这是一个核心配置类
@MapperScan("com.demo.dao")//扫描dao层,生成动态代理
@ComponentScan("com.demo")//扫描该路径下所有类上的注解
@EnableTransactionManagement//开启事务
@EnableAspectJAutoProxy
public class ApplicationConfig {
//配置数据源
@Bean
public DataSource dataSource(JdbcConfig dbcp) throws PropertyVetoException {
//其中JdbcConfig是自定义的配置类,读取properties文件的类
BasicDataSource cd = new BasicDataSource();
cd.setDriverClassName(dbcp.getDriver());
cd.setUrl(dbcp.getUrl());
cd.setUsername(dbcp.getName());
cd.setPassword(dbcp.getPassword());
return cd;
}
//配置核心Mybatis核心工厂
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource ds) throws IOException {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(ds);//配置数据源
bean.setTypeAliasesPackage("com.demo.entity");//设置实体类别名
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
bean.setMapperLocations(resolver.getResources("classpath:/mapper/*.xml"));//配置Mapper映射文件的路径
return bean;
}
}
可以看到,这是向我们的容器注册2个bean,分别是DataSource ,SqlSessionFactoryBean 。
然后我们分析注解MapperScan,点进去mapperScan注解,发现import了MapperScannerRegistrar。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
结合spirng的源码就可以知道,在解析@Import注解的时候,把MapperScannerRegistrar放进了importBeanDefinitionRegistrars,然后再执行registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}
就会进入到MapperScannerRegistrar的registerBeanDefinitions方法。
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
if (resourceLoader != null) { // this check is needed in Spring 3.1
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));
}
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
List<String> basePackages = new ArrayList<String>();
//拿到注解上面配置的value
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));
}
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));
}
通过annoAttrs.getStringArray(“value”)拿到了MapperScan上面的注解com.demo.dao,然后再执行scanner.doScan(StringUtils.toStringArray(basePackages))。doScan里面有3个关键的代码,然后dao下面的注解就进入到了beanDefinitions。
//根据basePackages去扫描类,形成beanDefinition
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
//把注入方式设置成按类型注入,默认是no
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
//把accountDao的类型设置成MapperFactoryBean
definition.setBeanClass(MapperFactoryBean.class);
因为我们定义了2个@Bean注解,到loadBeanDefinitionsForBeanMethod(beanMethod)方法,就会去解析我们的@Bean注解,把2个bean加入到beanDefinitions中。
然后我们的容器中就有了我们的dao,SqlSessionFactoryBean
在初始化前,我们看一下现在 都有哪些bd
然后看几个关键bean的初始化
初始化我们的dataSource就会调用ApplicationConfig 的dataSource,把配置文件上面的属性都给他赋值上。
下面初始化我们的accountDao,由于BeanClass被设置成了MapperFactoryBean,
注入类型变成了byType,所以我们先看MapperFactoryBean的代码,找到所有的set方法,都是需要自动装配的
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
for (PropertyDescriptor pd : pds) {
if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
!BeanUtils.isSimpleProperty(pd.getPropertyType())) {
result.add(pd.getName());
}
}
最后的到两个属性需要自动装配
调用
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
从容器中的bd去找sqlsessionFactory,有一段关键的代码,判断这个BD是否是一个FactoryBean,类型是否匹配。
SqlSessionFactoryBean不正是一个实现了FactoryBean,然后重写的getObject方法返回sqlSessionFactory对象吗,所以就匹配到了,下面就是进行属性的填充。
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
要填充首先要初始化sqlSessionFactoryBean
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
由于sqlSessionFactoryBean实现了InitializingBean,所以被实例化后会执行他重写的afterPropertiesSet。
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
this.sqlSessionFactory = buildSqlSessionFactory();
}
buildSqlSessionFactory里面就会解析我们的XML文件,分别解析mapper下面的parameterMap,resultMap,sql,select|insert|update|delete
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
再根据解析出来的信息,形成了mappedStatements,每一个增删改查方法都是一个mappedStatement
完成实例化后,就执行,SqlSessionTemplate被new出来,放到了sqlSession 。
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}
最后accountDao也被实例化出来了。这样我们就能从bean工厂中获取到accountDao,执行我们的sql语句了。
标签:basePackages,scanner,spring,class,bean,源码,annoAttrs,mybatis,public 来源: https://blog.csdn.net/a429095958/article/details/117172090