spring整合mybatis-plus
基于 spring-boot-2.3.2.RELEASE 和 mybatis-spring-1.3.2
1、为了让mybatis的mapper接口扫描病注册到spring容器中,有两种方案:使用@MapperScan或者使用@Mapper
2、我们今天查看源码就从@MapperScan开始
3、查看注解@MapperScan发现导入了 MapperScannerRegistrar
@Import({MapperScannerRegistrar.class})
public @interface MapperScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends Annotation> annotationClass() default Annotation.class;
Class<?> markerInterface() default Class.class;
String sqlSessionTemplateRef() default "";
String sqlSessionFactoryRef() default "";
Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
}
4、MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,spring启动时会调用MapperScannerRegistrar#registerBeanDefinitions方法
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); //初始化Mapper接口扫描器 ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); if (this.resourceLoader != null) { scanner.setResourceLoader(this.resourceLoader); } // 1. 处理注解过滤(默认是 @Mapper 注解),如下: /** * public interface MyMarkerInterface { * } * public interface UserMapper extends MyMarkerInterface { ... } * @MapperScan( * basePackages = "com.example.mapper", * markerInterface = MyMarkerInterface.class // 这里指定自定义标记接口 * ) */ Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass"); if (!Annotation.class.equals(annotationClass)) { scanner.setAnnotationClass(annotationClass); } // 2. 处理标记接口过滤(可选,通过接口继承过滤),如下: /** * // 自定义一个注解 * @Target(ElementType.TYPE) * @Retention(RetentionPolicy.RUNTIME) * public @interface MyCustomMapper { * } * @MyCustomMapper * public interface UserMapper { ... } * @MapperScan( * basePackages = "com.example.mapper", * annotationClass = MyCustomMapper.class // 这里指定自定义注解 * ) */ Class<?> markerInterface = annoAttrs.getClass("markerInterface"); if (!Class.class.equals(markerInterface)) { scanner.setMarkerInterface(markerInterface); } // 1. 配置 Bean 名称生成器 Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator"); if (!BeanNameGenerator.class.equals(generatorClass)) { scanner.setBeanNameGenerator((BeanNameGenerator)BeanUtils.instantiateClass(generatorClass)); } // 2. 配置 Mapper 工厂 Bean(默认是 MapperFactoryBean) Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean"); if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) { scanner.setMapperFactoryBean((MapperFactoryBean)BeanUtils.instantiateClass(mapperFactoryBeanClass)); } scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef")); scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef")); List<String> basePackages = new ArrayList(); String[] var10 = annoAttrs.getStringArray("value"); ... scanner.registerFilters(); scanner.doScan(StringUtils.toStringArray(basePackages)); }
5、继续查看ClassPathMapperScanner#doScan方法
public Set<BeanDefinitionHolder> doScan(String... basePackages) { //ClassPathMapperScanner#addIncludeFilter实现扫描Mapper接口 /** * this.addIncludeFilter(new TypeFilter() { * public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { * return true; * } * }); * protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { * return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent(); * } */ Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration."); } else { //继续查看ClassPathMapperScanner#processBeanDefinitions方法 this.processBeanDefinitions(beanDefinitions); } return beanDefinitions; }
6、继续查看ClassPathMapperScanner#processBeanDefinitions方法,发现Mapper接口的beanDefinition的beanClass属性被注册为mapperFactoryBean
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { Iterator var3 = beanDefinitions.iterator(); //扫描到的接口注册bean定义信息的beanClass为mapperFactoryBean //设置mapperFactoryBean的构造函数为Mapper //设置bean定义信息的autowireMode为AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE //最后Mapper的代理对象通过MapperFactoryBean#getObject获取 while(var3.hasNext()) { BeanDefinitionHolder holder = (BeanDefinitionHolder)var3.next(); GenericBeanDefinition definition = (GenericBeanDefinition)holder.getBeanDefinition(); ... definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); 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)) { ... definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionTemplate != null) { ... definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); explicitFactoryUsed = true; } if (!explicitFactoryUsed) { if (this.logger.isDebugEnabled()) { this.logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."); } definition.setAutowireMode(2); } } }
总结:
1、通过@MapperScan导入了MapperScannerRegistrar类
2、spring启动时会调用MapperScannerRegistrar#registerBeanDefinitions方法
3、在registerBeanDefinitions方法中定义了ClassPathMapperScanner用来扫描Mapper的beanDefinition
4、ClassPathMapperScanner重写isCandidateComponent方法,指定只扫描接口
5、最终把spring扫描得到的beanDefinition#beanClass属性设置为MapperFactoryBean,并把AutowireMode修改为byType
6、最终spring ioc通过MapperFactoryBean#getObject方法生成一个Mapper接口的代理对象,成为spring中的bean
7、一个userMapper.selectById的执行过程
/** * sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(), * new Class[] { SqlSession.class }, new SqlSessionInterceptor()); */ userMapper#selectById -> sqlSessionTemplate#selectOne -> sqlSessionProxy#selectOne -> defaultSqlSession#selectOne
8、为什么spring整合mybatis后,一级缓存会失效
mybatis中一级缓存是基于SqlSession来实现的,在执行一个sql时,如果使用的同一个SqlSession对象,那么就能使用到一级缓存。
在spring整合mybatis后,如果方法上没有开启事务(如@Transactional),那么在方法中每执行一个sql就会新创建一个SqlSession对象来执行该sql,这就是我们说的一级缓存失效,而如果开启了事务,那么在同一个事务内就会使用同一个SqlSession对象执行多个sql,因此一级缓存生效。
在spring中一级缓存生效具体代码:
private class SqlSessionInterceptor implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //在spring中,获取SqlSession对象逻辑,会判断是否开启事务,开启事务则返回同一个SqlSession对象 SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); try { Object result = method.invoke(sqlSession, args); if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { sqlSession.commit(true); } return result; } catch (Throwable t) { ... } finally { if (sqlSession != null) { closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } } } //继续查看SqlSessionUtils#getSqlSession public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { ... //通过TransactionSynchronizationManager#resources获取上下文中的资源对象 SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); SqlSession session = sessionHolder(executorType, holder); if (session != null) { return session; } session = sessionFactory.openSession(executorType); registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; } //继续查看TransactionSynchronizationManager#getResource方法 public static Object getResource(Object key) { Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); //最终通过TransactionSynchronizationManager#resources获取包含sqlSession的对象 Object value = doGetResource(actualKey); ... return value; }

浙公网安备 33010602011771号