分析Mybatis如何利用Spring的扩展点集成到框架中的,Mybatis自己的扩展点再也不本次分析范畴html
上Github上下载https://github.com/mybatis/spring。经过Git的方式试了几回没成功,后来直接Down的zip包,再导入的Idea中的。java
导入的过程中会有点慢,要下载很多东西。记得必定要修改Maven的配置文件和本地仓库地址,不然可能以前你已经下过的相关包会又下载到C盘的本地仓库当中mysql
直接在源码目录下新建了一个目录来写测试代码git
测试类github
@Configuration @MapperScan("com.jv.mapper") @ComponentScan("com.jv.scan") public class TestMybatis { public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestMybatis.class); UserService bean = ac.getBean(UserService.class); System.out.println(bean.query()); } @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); PooledDataSource dataSource = new PooledDataSource(); dataSource.setDriver("org.mariadb.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://192.168.10.12:3306/acct?useSSL=false&serverTimezone=UTC"); dataSource.setUsername("dev01"); dataSource.setPassword("12340101"); factoryBean.setDataSource(dataSource); return factoryBean.getObject(); } }
Service类 spring
@Service public class UserService { @Autowired private UserMapper userMapper; public List<User> query(){ return userMapper.query(); } }
Mapper类sql
public interface UserMapper { @Select("SELECT name,age FROM user") List<User> query(); }
实体类mybatis
@ToString public class User { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } private Integer age; }
注意:运行以前必定要修改pom.xml。由于Mybatis导入的Spring相关的依赖不在运行时生效app
<scope>provided</scope>所有注释掉,不然运行的时候会报好不到类框架
表结构:
CREATE TABLE `user` (
`name` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
`age` int(4) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
insert into user(name,age) values("Messi",35);
commit;
从mybatis-spring官方文档中能够找到@MapperScan的用法:http://mybatis.org/spring/mappers.html
既然和Spring集成是经过@MapperScan完成的,那从它入手
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MapperScannerRegistrar.class) @Repeatable(MapperScans.class) public @interface MapperScan { .... }
其中@Import(MapperScannerRegistrar.class)是重点,再看MapperScannerRegistrar
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware { /** * {@inheritDoc} * * @deprecated Since 2.0.2, this method not used never. */ @Override @Deprecated public void setResourceLoader(ResourceLoader resourceLoader) { // NOP } /** * {@inheritDoc} */ @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) { /** * 注册一个MapperScannerConfigurer的BeanDefinition,MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor * BeanDefinitionRegistryPostProcessor接口的实现类,一旦放入Spring容器中,那么在Spring容器启动的时候它能够担任注册本身须要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); } //自定义BeanNameGenerator Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator"); if (!BeanNameGenerator.class.equals(generatorClass)) { builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass)); } //自定义MapperFactoryBean Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean"); if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) { builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass); } //自定义sqlSessionTemplate String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef"); if (StringUtils.hasText(sqlSessionTemplateRef)) { builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef")); } //自定义sqlSessionFactory String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef"); if (StringUtils.hasText(sqlSessionFactoryRef)) { builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef")); } //生成全部的scan要扫描的基础包路径 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)); //将MapperScannerConfigurer的BeanDefinition注册 registry.registerBeanDefinition(beanName, builder.getBeanDefinition()); } }
MapperScannerRegistrar implements ImportBeanDefinitionRegistrar
这就是它集成到Spring的关键点,关于ImportBeanDefinitionRegistrar能够参考http://www.javashuo.com/article/p-ccwckotr-q.html
MapperScannerRegistrar 在Spring容器初始化的时候完成从外部导入MapperScannerConfigurer类对应的BeanDefinition
MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor(又是Spring的另一个扩展点,也是扩展BeanDefinition注册功能,但它要晚于ImportBeanDefinitionRegistrar生效,由于前者的做用触发时机是Spring的ConfigurationClassPostProcessor Implements PriorityOrder。 这个类是扫描路径下全部Mapper的关键类
/** * BeanDefinitionRegistryPostProcessor递归地从基本包中搜索接口并将它们注册为MapperFactoryBean * MapperFactoryBean很是重要,它实现了InitializingBean,Spring会让Bean属性设置完以后调用它的抽象方法afterPropertiesSet,完成一些初始化操做 */ public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware { .................省略部分代码.................. @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); //默认是MapperFactoryBean,能够是自定义的 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,重写了doScan方法
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner { .................省略部分代码.................. /** * 重写了ClassPathBeanDefinitionScanner的doScan方法,可是扫描工做仍是由父类的doScan完成 */ @Override public Set<BeanDefinitionHolder> doScan(String... basePackages) { 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 { processBeanDefinitions(beanDefinitions); } return beanDefinitions; } /** * 重写了ClassPathBeanDefinitionScanner的processBeanDefinitions方法 * 完成BeanDefinition的属性填充 * 其中的setAutowireMode=AbstractBeanDefinition.AUTOWIRE_BY_TYPE 是Spring根据类型完成自动注入的关键。 * @param beanDefinitions */ 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 /** * 自定义的Mapper接口只是Bean最初的类,当Spring初始化以后Bean的Class其实是MapperFactoryBean */ definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59 //设置BeanClass为MapperFactoryBean definition.setBeanClass(this.mapperFactoryBeanClass); 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) { LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."); // 至关的重要,这就是Spring的根据类型进行依赖注入。 // @Autowired使用的时候,其实Spring默认是没有自动注入的,也就是说autowireMode是AUTOWIRE_NO definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } definition.setLazyInit(lazyInitialization); } } .................省略部分代码.................. }
上面的代码完成了对basePackage的类扫描,Mybatis根据扫描生成的BeanDefinition作加强,至关重要的两点:
1.BeanClass=MapperFactoryBean
也就是说:最开始Spring容器初始化完成以后,全部Mapper并无真正的实例化(能够经过观察XXXApplicationContext.beanFactory.FactoryBeanObjectCache中是否有对象),而是它们的FactoryBean已经完成了实例化。当须要Mapper时再建立,若是是单例,则将已经实例化的Bean放到FactoryBeanObjectCache中
2.autowireMode=AUTOWIRE_BY_TYPE
按类型自动注入和@Autowired没直接联系,Spring默认是AUTOWIRE_NO,只不过Spring发现你用了@Autowired注解会自动根据类型注入而已。按类型自动注入必需要有setXXX方法。
表明了被扫描的类支持按类型实例化,为何要设置这个值喃?咱们本身写的Mapper牢牢是一个接口,为何还要注入东西喃。。。根本缘由是MapperFactoryBean extends SqlSessionDaoSupport
SqlSessionDaoSupport里面有两个set方法,分别是:
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory)
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate)
能够验证一下,将“definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)” 注释掉,报错:
表明着没有注入成功,也验证了AUTOWIRE_BY_TYPE的重要性
截止到这里,Mybatis相关类的BeanDefinition(BeanClass=MapperFactoryBean)所有完成了注册,接下来就是实例化。
实例化时有两个很是重要的点:
1.前面说过由于添加到BeanDefinitionMap中的bd都是MapperFactoryBean,因此完成实例化的Mapper都是MapperFactoryBean,而不是真正的Mapper。可是在实例化MapperFactoryBean所须要的SqlSessionFactory,SqlSessionTemplate从哪里获取到喃。
首先在TestMybatis类中使用了@Bean注解,Spring对于@Bean和ImportBeanDefinitionRegistrar,前者会先处理,也就是说@MapperScan扫描到的类会晚于@Bean修饰的SqlSessionFactory注册到容器中。因此Spring在实例化MapperFactoryBean的时候自动注入SqlSessionFactory是能够成功的,具体源码见:
根据BeanDefinition.propertValues+setterXXX标记的属性获取Bean,在这里就是被setSqlSesstionFactory()标记的SqlSessionFactory属性:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#autowireByType()
protected void autowireByType( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } Set<String> autowiredBeanNames = new LinkedHashSet<>(4); //过滤掉不须要自动注入的属性,好比没有setter方法啊,简单类:Class,URL,Number等 String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); //循环处理属性,最终会将要注入的对象获取到并放入到PVS当中,等候后面的注入 for (String propertyName : propertyNames) { try { PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName); // Don't try autowiring by type for type Object: never makes sense, // even if it technically is a unsatisfied, non-simple property. if (Object.class != pd.getPropertyType()) { MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd); // Do not allow eager init for type matching in case of a prioritized post-processor. boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance()); //获取到Class对象 DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager); //获取到Class对象 Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter); if (autowiredArgument != null) { pvs.add(propertyName, autowiredArgument); } for (String autowiredBeanName : autowiredBeanNames) { registerDependentBean(autowiredBeanName, beanName); if (logger.isTraceEnabled()) { logger.trace("Autowiring by type from bean name '" + beanName + "' via property '" + propertyName + "' to bean named '" + autowiredBeanName + "'"); } } autowiredBeanNames.clear(); } } catch (BeansException ex) { throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex); } } }
根据获取到的Bean进行注入:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
/** * Populate the bean instance in the given BeanWrapper with the property values * from the bean definition. 用bean定义中的属性值填充给定BeanWrapper中的bean实例 * @param beanName the name of the bean * @param mbd the bean definition for the bean * @param bw the BeanWrapper with bean instance */ @SuppressWarnings("deprecation") // for postProcessPropertyValues protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { 。。。。省略部分代码。。。。 /** * 给任何InstantiationAwareBeanPostProcessors机会在属性被设置以前修改Bean的状态(属性),好比设置特殊的属性值, * 或者修改PropertyValues中的值 */ boolean continueWithPropertyPopulation = true; //第十二次PostProcessor InstantiationAwareBeanPostProcessors if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { continueWithPropertyPopulation = false; break; } } } } if (!continueWithPropertyPopulation) { return; } PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); int resolvedAutowireMode = mbd.getResolvedAutowireMode(); //根据名称或类型注入依赖 if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // Add property values based on autowire by name if applicable. // 经过属性名称注入依赖 BeanDefinition.autowireMode=AUTOWIRE_BY_NAME if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // Add property values based on autowire by type if applicable. // 经过属性类型注入依赖 BeanDefinition.autowireMode=AUTOWIRE_BY_TYPE if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds = null; if (hasInstAwareBpps) { if (pvs == null) { pvs = mbd.getPropertyValues(); } for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; //PostProcessor 和下面调用是一样的效果,后者已通过期了,Spring5就是在这里注入的, // -----------------好比对象和属性值的注入------------------------ PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } //PostProcessor pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } } pvs = pvsToUse; } } } 。。。。省略部分代码。。。。 }
可是Spring容器中并无SqlSessionTemplate,为何MapperFactoryBean最后也仍是有喃,由于在注入SqlSessionFactory的时候Mybatis自动实例化了它
public abstract class SqlSessionDaoSupport extends DaoSupport { 。。。。。注释部分代码。。。。 public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) { this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory); } } 。。。。。注释部分代码。。。。 }
2.执行DML所须要的信息从哪里来?
接下来看看MapperFactoryBean的类图
能够看到最终实现了InitializingBean,Spring针对实现了该接口的Bean,在完成属性填充以后会调用实现类的afterPropertiesSet()方法。
调用位置:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethods
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { 。。。。省略部分代码。。。。 if (System.getSecurityManager() != null) { 。。。。省略部分代码。。。。 } else { //执行实现了InitializingBean子类的afterPropertiesSet ((InitializingBean) bean).afterPropertiesSet(); } } //执行设置的InitMethod if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } } }
来看看Mybatis的DaoSupport类的afterPropertiesSet()方法干了什么
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException { //运行时会调用MapperFactoryBean的checkDaoConfig this.checkDaoConfig(); try { this.initDao(); } catch (Exception var2) { throw new BeanInitializationException("Initialization of DAO failed", var2); } }
checkDaoConfig最终调用的是实现类MapperFactoryBean.checkDaoConfig方法
protected void checkDaoConfig() { super.checkDaoConfig(); notNull(this.mapperInterface, "Property 'mapperInterface' is required"); Configuration configuration = getSqlSession().getConfiguration(); if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) { try { //很是重要 configuration.addMapper(this.mapperInterface); } catch (Exception e) { logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e); throw new IllegalArgumentException(e); } finally { ErrorContext.instance().reset(); } } }
configuration.addMapper(this.mapperInterface)完成了MappedStatement添加。
configuration是DefaultSqlSesstionFactory(默认的)中的属性,全局惟一
MappedStatement描述了一个要执行的SQL,参数、返回类型等等
经过这一步,调用真正的query方法所须要的东西都准备好了。
总结:Mybatis利用了@Import(class implements ImportBeanDefinitionRegistrar),BeanDefinitionRegistryPostProcessor,InitializingBean三个扩展点来完成整合。
其中Mybatis的以下几个类很是重要:
MapperScan MapperScannerRegistrar MapperScannerConfigurer ClassPathMapperScanner MapperFactoryBean DefaultSqlSessionFactory Configuration MapperStatement