手写还原MyBatis与SpringBoot整合流程

182 阅读2分钟

1: 首先创建一个基本的SpringBoot项目,整体项目结构如下

WeChat7492804b8fd3c9447429d318cf02932b.png

2:创建一个自己的注解,也叫 MapperScan 吧,内容如下,这里要注意一下,有个 @Import 注解,里面还有个 MapperScanRegister 类,这个类也是需要我们自己实现的

import org.springframework.context.annotation.Import;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MapperScanRegister.class)
public @interface MapperScan {

    String basePackage() default "";
}

3: MapperScanRegister.class

  • 3.1: 这个类的作用只有一个那就是注册 MapperScannerConfigurer.class,这个类也是自己实现的
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class MapperScanRegister implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        System.out.println("=======注册MapperScannerConfigurer实例========");
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
        registry.registerBeanDefinition("mapperScannerConfigurer",builder.getBeanDefinition());
    }
}

4: MapperScannerConfigurer.class

  • 4.1: 可以看到这里类就是负责扫描我们指定包路径下的类的,就像MyBatis中的Mapper接口一样
  • 4.2: 这里有个 ClassPathMapperScanner,这个扫描器也是自定义的,最复杂的也就是这个类
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.util.StringUtils;

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        scanner.registerFilters();
        scanner.scan(StringUtils.tokenizeToStringArray("com.coco.mapper", ",; \t\n"));
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { }
}

5: ClassPathMapperScanner.class

import com.coco.proxy.ServiceFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import java.util.Set;

/**
 * 自定义MapperScan扫描器
 */
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {

    public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
        super(registry, false);
    }

    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
        if(beanDefinitionHolders.isEmpty()) {
            return null;
        }
        processBeanDefinition(beanDefinitionHolders);
        return beanDefinitionHolders;
    }


    //这个方法一定要实现,我这里是只要是接口就返回true,因为MyBatis的Mapper都是接口,当然你也可以做复杂一点
    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface();
    }
    
    // 这个方法也一定要实现,这个方法不用改什么,直接这样写就行了
    public void registerFilters() {
        addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
    }
    
    // 这里才是最核心的,MyBatis之所以执行Mapper接口就能执行xml文件中的SQL,主要还是靠这里实现的
    public void processBeanDefinition(Set<BeanDefinitionHolder> beanDefinitions) {
        AbstractBeanDefinition definition;
        BeanDefinitionRegistry registry = getRegistry();
        for (BeanDefinitionHolder holder : beanDefinitions) {
            definition = (AbstractBeanDefinition) holder.getBeanDefinition();
            String beanClassName = definition.getBeanClassName();
            definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
            definition.setBeanClass(ServiceFactory.class);
            definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
            definition.setLazyInit(false);
        }
    }
}

6: 说说为什么一定要实现 isCandidateComponentregisterFilters这二个方法

  • 6.1: 看下 super.doScan(basePackages); 方法做了什么
  • 6.2: 这时候是进入到 ClassPathBeanDefinitionScanner.classdoScan 方法
  • 6.3: 这个方法中有一个 Set candidates = findCandidateComponents(basePackage);
  • 6.4: 进入 scanCandidateComponents(basePackage);
  • 6.5:
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
  Set<BeanDefinition> candidates = new LinkedHashSet<>();
  try {
     String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
           resolveBasePackage(basePackage) + '/' + this.resourcePattern;
     Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
     boolean traceEnabled = logger.isTraceEnabled();
     boolean debugEnabled = logger.isDebugEnabled();
     for (Resource resource : resources) {
        if (traceEnabled) {
           logger.trace("Scanning " + resource);
        }
        if (resource.isReadable()) {
           try {
              MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
              // 这个方法只有返回 ture 的时候,才有机会把这个Bean的配置信息添加到candidates这个集合中去,
              // 记住这里为 ture 只是有机会
              if (isCandidateComponent(metadataReader)) {
                 ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                 sbd.setResource(resource);
                 sbd.setSource(resource);
                 // 这个方法只有返回 ture 的时候,才会把这个Bean的配置信息添加到candidates这个集合中去
                 if (isCandidateComponent(sbd)) {
                    candidates.add(sbd);
                 }

  
  return candidates;
}
  • 6.6: 看下第一个方法 isCandidateComponent
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
   // 这里默认都是不会返回false的,所以这里可以跳过
   for (TypeFilter tf : this.excludeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         return false;
      }
   }
   // 因为要返回true的话,只有在这里返回,这里是遍历this.includeFilters,还记得我们在那里添加
   // 过嘛,是的大家回头看下 ClassPathMapperScanner 的 registerFilters方法,而且就是返回true,
   // 这也是为什么一定要实现 registerFilters方法的原因
   for (TypeFilter tf : this.includeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         return isConditionMatch(metadataReader);
      }
   }
   return false;
}
  • 6.7: 现在来看下 isConditionMatch(metadataReader);
// 我们一直跟到这个方法,可以知道metadata肯定不为空,因为这个是我们Mapper类的元数据,
// 第二个肯定也没有Conditional这个注解,所以这里直接返回false,但是在返回的时候是取 返 的,
// 也就是这里返回 false,那么最终我们得到的就是 true
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
   return false;
}
  • 6.8: 第一个返回true了,那么现在看下 isCandidateComponent(sbd),当我们在ClassPathMapperScanner 是实现了这个方法的。也就是下面这个,也就是当我们的Mapper是接口就返回true,所以这样就可以注册我们的Mapper接口了
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    return beanDefinition.getMetadata().isInterface();
}

7 :为Mapper接口创建代理类

  • 7.1: 在ClassPathMapperScanner类中,我们得到了所有Mapper接口的BeanDefinitionHolder
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
    if(beanDefinitionHolders.isEmpty()) {
        return null;
    }
    // 为mapper接口生成代理类
    processBeanDefinition(beanDefinitionHolders);
    return beanDefinitionHolders;
}
public void processBeanDefinition(Set<BeanDefinitionHolder> beanDefinitions) {
    AbstractBeanDefinition definition;
    BeanDefinitionRegistry registry = getRegistry();
    for (BeanDefinitionHolder holder : beanDefinitions) {
        definition = (AbstractBeanDefinition) holder.getBeanDefinition();
        String beanClassName = definition.getBeanClassName();
           definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
        // 在这里为每个Mapper接口的Bean设置成代理类
        definition.setBeanClass(ServiceFactory.class);
        definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
        definition.setLazyInit(false);
    }
}

8: 代理类创建

import org.springframework.beans.factory.FactoryBean;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class ServiceFactory<T> implements FactoryBean<T> {


    private Class<T> interfaceType;

    public ServiceFactory(Class<T> interfaceType) {
        this.interfaceType = interfaceType;
    }

    @Override
    public T getObject() throws Exception {
        //这里主要是创建接口对应的实例,便于注入到spring容器中
        InvocationHandler handler = new ServiceProxy<>(interfaceType);
        return (T) Proxy.newProxyInstance(interfaceType.getClassLoader(),
                new Class[] {interfaceType},handler);
    }

    @Override
    public Class<T> getObjectType() {
        return interfaceType;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;


public class ServiceProxy<T> implements InvocationHandler {


    private Class<T> interfaceType;

    public ServiceProxy(Class<T> interfaceType) {
        this.interfaceType = interfaceType;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("执行到这里来了======");
       return null;
    }
}

8: 最后我们可以创建一个Controller测试一下

@RestController
public class IndexController {


    @Resource
    private IndexMapper indexMapper;

    @GetMapping("/index")
    public String index() {
        System.out.println(indexMapper.getClass());
        indexMapper.test();
        return "ok";
    }
}

可以看到输出的IndexMapper就是一个代理类,其实MyBatis与SpringBoot的整合流程大致也是如此