写点什么

Spring-Spring 整合 MyBatis 原理分析

用户头像
魔曦
关注
发布于: 2021 年 01 月 17 日
Spring-Spring整合MyBatis原理分析
  • 对于 CRUD boy 天天用 SSM 框架,可是问起 Spring 如何整合 MyBatis 的原理却支支吾吾,不能很好的说出底层的原理

  • Mybatis 也随着 spring 的版本变化在新版本也做出了一些调整,能说说都有哪些调整吗?

  • 你如何能手写一个 MyBatis 框架吗?

模拟 Spring 整合 Mybatis

@Componentpublic class UserService {
@Autowired OrderMapper orderMapper;
@Autowired UserMapper userMapper;

public void test(){ orderMapper.selectById(); userMapper.insert1(); }}
public interface UserMapper {
@Insert("insert into t values(1,'1','1',1,'1')") void insert1();
@Insert("insert into t values(2,'1','1',1,'1')") void insert2();
}
public interface OrderMapper {
@Select("select 'test'") String selectById();
}
@Componentpublic class MyBatisFactoryBean implements FactoryBean {
private Class mapperInterface;
public MyBatisFactoryBean() {
}

public MyBatisFactoryBean(Class mapperInterface) { this.mapperInterface = mapperInterface; }
@Override public Object getObject() throws Exception { Object o = Proxy.newProxyInstance(MyBatisFactoryBean.class.getClassLoader(), new Class[] {mapperInterface}, new InvocationHandler() { @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { System.out.println(method.getName()); return null; } }); return o; }
@Override public Class<?> getObjectType() { return mapperInterface; }}

public class MyBatisBeanDefinitionRegistar implements ImportBeanDefinitionRegistrar {
@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
List<Class> mapperList = new ArrayList<>(); mapperList.add(OrderMapper.class); mapperList.add(UserMapper.class);
for(Class s:mapperList){ AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); bd.setBeanClass(MyBatisFactoryBean.class); bd.getConstructorArgumentValues().addGenericArgumentValue(s); registry.registerBeanDefinition(s.getName(), bd); } }}
@ComponentScan({"com.spring.test.ch05"})@Import(value = {MyBatisBeanDefinitionRegistar.class})public class Ch05Config {
}
public class Ch05Test {
public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(); ac.register(Ch05Config.class); ac.refresh(); UserService userService = (UserService)ac.getBean("userService"); userService.test();
}}
复制代码

运行结果:

实现思路:

  • 实现 FactoryBean,在 getObject 方法中用返回接口的代理对象,getObject 方法中需要 Mybatis 来生成代理对象

  • 把指定路径下的 Mapper 需要扫描出来,然后变成对应的 BeanDefinition 存入 BeanFactory 中;

myBatis-spring 2.0.5

使用姿势是需要在配置类引入注解,标识需要扫描那些 mapper

@ComponentScan({"com.spring.test.ch05"})@MapperScan({"com.spring.test.ch05"})public class Ch05Config {
@Bean public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test?useSSL=false"); dataSource.setUsername("root"); dataSource.setPassword("123456"); return dataSource; } // @Bean public SqlSessionFactory sqlSessionFactory() { SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource()); try { return sessionFactoryBean.getObject(); } catch (Exception e) { e.printStackTrace(); } return null; }
}
复制代码

点击 @MapperScan 注解,可以看到 @Import(MapperScannerRegistrar.class),这个就很关键了;MapperScannerRegistrar 类实现了 ImportBeanDefinitionRegistrar 类,该类从名字就能猜到可以进行 BeanDefinition 的注册。

/**   * {@inheritDoc}   */  @Override  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {    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) {
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); }
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
复制代码

以上代码中生成了 MapperScannerConfigurer 的 BeanDefinition:

    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
复制代码

打开 MapperScannerConfigurer 的类,根据类的实现可以知道该类的执行时机;

public class MapperScannerConfigurer    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
复制代码

以下是该类的关键代码:

/**   * {@inheritDoc}   *   * @since 1.0.2   */  @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); 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,也就是自己实现了 ClassPathBeanDefinitionScanner 的部分功能。

public ClassPathMapperScanner(BeanDefinitionRegistry registry) {    super(registry, false);  }
复制代码

注意初始化方法第二个参数 useDefaultFilters 传递值是 false,即跳过父类的一段处理逻辑,此处理逻辑是用于扫描 @Component 注解标注的类;

自己是实现过滤操作public void registerFilters() {    boolean acceptAllInterfaces = true;
// if specified, use the given annotation and / or marker interface if (this.annotationClass != null) { addIncludeFilter(new AnnotationTypeFilter(this.annotationClass)); acceptAllInterfaces = false; }
// override AssignableTypeFilter to ignore matches on the actual marker interface if (this.markerInterface != null) { addIncludeFilter(new AssignableTypeFilter(this.markerInterface) { @Override protected boolean matchClassName(String className) { return false; } }); acceptAllInterfaces = false; }
if (acceptAllInterfaces) { // default include filter that accepts all classes addIncludeFilter((metadataReader, metadataReaderFactory) -> true); }
// exclude package-info.java addExcludeFilter((metadataReader, metadataReaderFactory) -> { String className = metadataReader.getClassMetadata().getClassName(); return className.endsWith("package-info"); }); }
复制代码

扫描的操作依旧是采用父类

 @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; }
复制代码

isCandidateComponent 方法也进行了重写,支持接口;

/**   * {@inheritDoc}   */  @Override  protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {    return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();  }
复制代码

以下这个方法就很熟悉了,和之前模拟的逻辑基本类似:

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 // 关键代码 definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59 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() + "'."); // 关键代码 definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } definition.setLazyInit(lazyInitialization); } }
复制代码

以下的代码很重要,修改了注入模式为 ByType,主要是为了给 SqlSessionTemplate 赋值,之前在配置类定义了 SqlSessionFactory 的 bean,而在 SqlSessionDaoSupport 类属性注入的时候会根据类型寻找对应的 bean,关于属性注入的查找是先找 set 方法等这个后面会详细介绍;

definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {    if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {      this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);    }  }
复制代码

beanClass 为 MapperFactoryBeanClass,而 MapperFactoryBean 实现了 FactoryBean

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
private boolean addToConfig = true;
public MapperFactoryBean() { // intentionally empty }
public MapperFactoryBean(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; }
/** * {@inheritDoc} */ @Override 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(); } } }
/** * {@inheritDoc} */ @Override public T getObject() throws Exception { //执行的是Mybatis的jar中逻辑 return getSqlSession().getMapper(this.mapperInterface); }
/** * {@inheritDoc} */ @Override public Class<T> getObjectType() { return this.mapperInterface; }
/** * {@inheritDoc} */ @Override public boolean isSingleton() { return true; }
// ------------- mutators --------------
/** * Sets the mapper interface of the MyBatis mapper * * @param mapperInterface * class of the interface */ public void setMapperInterface(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; }
/** * Return the mapper interface of the MyBatis mapper * * @return class of the interface */ public Class<T> getMapperInterface() { return mapperInterface; }
/** * If addToConfig is false the mapper will not be added to MyBatis. This means it must have been included in * mybatis-config.xml. * <p> * If it is true, the mapper will be added to MyBatis in the case it is not already registered. * <p> * By default addToConfig is true. * * @param addToConfig * a flag that whether add mapper to MyBatis or not */ public void setAddToConfig(boolean addToConfig) { this.addToConfig = addToConfig; }
/** * Return the flag for addition into MyBatis config. * * @return true if the mapper will be added to MyBatis in the case it is not already registered. */ public boolean isAddToConfig() { return addToConfig; }}
复制代码

新版与旧版区别

2.0.5 版本

Import-MapperScannerRegistrar->注册了一个 MapperScannerConfigurer->扫描->BeanDefinition

1.0.3 版本

Import-MapperScannerRegistrar->扫描->BeanDefinition

增加这一层的好处,就是可以不使用 MapperScan 注解,直接定义一个 MapperScannerConfigurer 类型的 bean 来扫描,使用更加灵活了;


注意:

@Mapper 注解目前没有使用场景,除非在 spring-boot 中,@MapperScan 注解和 @Mapper 配合使用,意思是接口只有加了 @Mapper 的注解才能被解析,否则不行。

用户头像

魔曦

关注

我思故我在! 2018.01.15 加入

凡事有交代,件件有着落,事事有回音。

评论

发布
暂无评论
Spring-Spring整合MyBatis原理分析