简单的流程图
使用的是我们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语句了。