Spring整合Mybatis源码分析
一、引言
Mybatis整合到Spring主要就是利用spring的加载bean的机制,将一个个的mapper接口注册成成bean,存入spring容器。
1、创建一个DefaultSqlSessionFactory的bean,可以通过@Bean的方式
@Bean public SqlSessionFactory sqlSessionFactory() { SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource()); try { return sessionFactoryBean.getObject(); } catch (Exception e) { e.printStackTrace(); } return null; }
还可以通过xml注解的方式,主要就是为了初始化mybatis的运行环境,核心属性配置,数据源,解析xml等等,这部分代码很简单,跟之前的mybatis源码过程差不多,就不看了。
2、利用了Sping中的扩展点,提供了一个注解@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 {}; ··· }
在我们使用spring+mybatis做开发的时候,通常会引入mybatis-spring的jar包,然后在配置类上加上@MapperScan注解,扫描我们的mapper包路径。可以看到注解上面有@Import注解,导入了MapperScannerRegistrar类,在spring扩展点一文中也讲解到了spring会执行
configClass.getImportBeanDefinitionRegistrars()方法得到所有通过import注解导入进来的实现了ImportBeanDefinitionRegistrar接口的实现类,其中就包括MapperScannerRegistrar。然后执行其registerBeanDefinitions方法,这里也就是spring整合mybatis的关键地方了。
三、MapperScannerRegistrar
在不同的版本中他的实现有所不同,在低版本中(1.3.2)
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //获取MapperScan注解的各种属性 AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); //定义一个ClassPathMapperScanner对象,用来扫描mapper 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); } //设置ClassPathMapperScanner对象可以扫描到接口,因为在Spring中是不会扫描接口的 //同时因为ClassPathMapperScanner中重写了isCandidateComponent方法,导致isCandidateComponent只会认为接口是备选者Component 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)); } //为后续设置BeanClass为MapperFactoryBean 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>(); 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)); }
直接扫描配置的包,得到对应的bd
public Set<BeanDefinitionHolder> doScan(String... basePackages) { //走的父类spring中的包扫描逻辑 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 { //对bd的后置处理 processBeanDefinitions(beanDefinitions); } return beanDefinitions; }
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); if (logger.isDebugEnabled()) { logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface"); } // 构造参数的入参设置为相应的mapper名称,beanClass统一为mapperFactoryBean definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59 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() + "'."); } //设置自动注入byType definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } } }
在高版本的mybais-spring中(2.0.5)
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //解析获取MapperScan注解的各种属性 AnnotationAttributes mapperScanAttrs = AnnotationAttributes .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); if (mapperScanAttrs != null) { //核心逻辑 registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0)); } } void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) { //生成一个MapperScannerConfigurer类型的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())); if (basePackages.isEmpty()) { basePackages.add(getDefaultBasePackage(annoMeta)); } String lazyInitialization = annoAttrs.getString("lazyInitialization"); if (StringUtils.hasText(lazyInitialization)) { builder.addPropertyValue("lazyInitialization", lazyInitialization); } String defaultScope = annoAttrs.getString("defaultScope"); if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) { builder.addPropertyValue("defaultScope", defaultScope); } builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages)); //将MapperScannerConfigurer的bd注册到BeanDefinitionRegistry中 registry.registerBeanDefinition(beanName, builder.getBeanDefinition()); }
并没有直接扫描包注册bd,而是先注册了一个MapperScannerConfigurer,实现了BeanDefinitionRegistryPostProcessor接口,那么在spring的接下来的逻辑中会接着处理MapperScannerConfigurer,下面就是spring中的逻辑
boolean reiterate = true; // 在一个BeanDefinitionRegistryPostProcessor中可以注册另一个BeanDefinitionRegistryPostProcessor,所以需要递归找出所有的BeanDefinitionRegistryPostProcessor // 一个没有实现PriorityOrdered接口的BeanDefinitionRegistryPostProcessor如果在内部注册了一个实现了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor,那么就是没有实现PriorityOrdered接口的先执行 while (reiterate) { reiterate = false; // 这里会再一次拿到实现了PriorityOrdered接口或Ordered接口的BeanDefinitionRegistryPostProcessor,所以需要processedBeans进行过滤 postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); reiterate = true; } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); }
继续走到invokeBeanDefinitionRegistryPostProcessors方法,这里会拿到我们在之前添加的MapperScannerConfigurer,然后调用postProcessBeanDefinitionRegistry方法
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) { //解析调整MapperScan注解的属性 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)); } if (StringUtils.hasText(defaultScope)) { scanner.setDefaultScope(defaultScope); } scanner.registerFilters(); //走的父类spring的scan逻辑,会先调子类ClassPathMapperScanner的doScan逻辑 scanner.scan( StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); }
还是会走一样的doScan逻辑,高版本这样优化的作用是什么呢?
这样做的话,我们没有必要再去使用@MapperScan注解,可以在配置类中自定义一个bean,来实现和注解一样的效果,而且更灵活,可以增强实现。
@Bean public MapperScannerConfigurer configurer(){ MapperScannerConfigurer configurer = new MapperScannerConfigurer(); configurer.setBasePackage("com.luban.dao"); return configurer; }
在注册完扫描出来的接口bd后,就会接着走bean的实例化过程,因为我们之前把这个bd的class全部修改成了MapperFactoryBean,他实现了FactoryBean接口,那么接下来bean实例化的时候就会走FactoryBean的逻辑,通过getObject返回一个实例化对象。
四、MapperFactoryBean
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { private Class<T> mapperInterface; private boolean addToConfig = true; public MapperFactoryBean(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } @Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); } ``` }
之前我们说过,创建Mapper的BeanDefinition的时候,设置了构造器的入参,就是各个Mapper的class,那么在bean使用构造器实例化的时候,就会把mapperInterface赋值上了。
MapperFactoryBean继承自SqlSessionDaoSupport,来看一下这个类
public abstract class SqlSessionDaoSupport extends DaoSupport { private SqlSessionTemplate sqlSessionTemplate; public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) { this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory); } } protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } }
他有一个属性SqlSessionTemplate,并且还有一个set方法,setSqlSessionFactory(SqlSessionFactory sqlSessionFactory),之前在讲解Spring源码的是否提到过,在填充属性的时候,会根据自动注入类型来注入属性,其中一个就是byType,这里就是用的byType自动注入,来看一下Spring的部分源码:
// autowire属性 int resolvedAutowireMode = mbd.getResolvedAutowireMode(); if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { // by_name是根据根据属性名字找bean // by_type是根据属性所对应的set方法的参数类型找bean // 找到bean之后都要调用set方法进行注入 // 找不到bean就说明此set方法并不是我们需要的属性注入方法,则不会执行set方法 MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // Add property values based on autowire by name if applicable. if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // Add property values based on autowire by type if applicable. if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; // 注意,执行完这里的代码之后,这是把属性以及找到的值存在了pvs里面,并没有完成反射赋值 // 是在后续的方法applyPropertyValues(beanName, mbd, bw, pvs)中执行set方法赋值 }
SqlSessionFactory也是需要我们配置成bean的,所以会执行SqlSessionDaoSupport的setSqlSessionFactory方法创建sqlSessionTemplate对象并赋值。
当我们需要在一个service中使用创建好的mappe类型的bean时候,直接通过@Autowired注解就行了,或者通过getBean方法获取,这个时候就是走MapperFactoryBean的getObject方法,获取真正的对象,我们来看一下。
五、getBean
public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); }
getSqlSession()获取的就是之前创建赋值的sqlSessionTemplate,看一下他的getMapper方法
public <T> T getMapper(Class<T> type) { return getConfiguration().getMapper(type, this); } public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } public <T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } }
最后走到了mybatis的源码,为mapper创建代理对象,返回完成属性注入。