spring boot mybatis 加载过程

2021-03-22  本文已影响0人  草祭木初

其实最简单的使用mybatis是这样
创建spring boot 项目
maven加载mabatis
创建数据源配置
建个文件夹
建个接口文件用@Mapper注解
ok了

@Mapper
public interface UserDao {
    @Select("select * from t_user limit 1")
    public List<Map> getUserList();
}

然后就可以在其他类里注入这个Dao并调用了

是不是 很简单
那spring boot 和 mabatis 是怎么实现的,让这个接口可以用呢?

1,先看下我们用maven都引入哪些关于mybatis的包

maven配置

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>

共引入的4个包

光看名字就知道了
org.mybatis.spring.boot:mybatis-spring-boot-autoconfigure:2.1.4
这个就是自动配置的包,符合spring-boot自动配置约定

看下spring.fatories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

两个自动配置类,会被spring-boot在启动时扫描到

2,MybatisAutoConfiguration

@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
public class MybatisAutoConfiguration implements InitializingBean {
    private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
    private final MybatisProperties properties;
    private final Interceptor[] interceptors;
    private final TypeHandler[] typeHandlers;
    private final LanguageDriver[] languageDrivers;
    private final ResourceLoader resourceLoader;
    private final DatabaseIdProvider databaseIdProvider;
    private final List<ConfigurationCustomizer> configurationCustomizers;

    public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
        this.properties = properties;
        this.interceptors = (Interceptor[])interceptorsProvider.getIfAvailable();
        this.typeHandlers = (TypeHandler[])typeHandlersProvider.getIfAvailable();
        this.languageDrivers = (LanguageDriver[])languageDriversProvider.getIfAvailable();
        this.resourceLoader = resourceLoader;
        this.databaseIdProvider = (DatabaseIdProvider)databaseIdProvider.getIfAvailable();
        this.configurationCustomizers = (List)configurationCustomizersProvider.getIfAvailable();
    }
    。。。
}

@Configuration 和 InitializingBean 接口
请参照:spring 生命周期 扩展点

请他注解请自己去查
但 看名字也知道了,主要是要在DataSource加载完后,才会加载这个类

不过这个类有点特别,它的属性没有用注解的方式注入
而是用构造函数,但我们又没有用显示的@Bean注解才构造它
是怎么实现的呢?
是因为在
spring 4.3之后,引入了一个新特性:当构造方法的参数为单个构造参数时,可以不使用@Autowired进行注解
同样是在Spring 4.3版本中,不仅隐式的注入了单构造参数的属性。还引入了ObjectProvider接口。
具体请参照# ObjectProvider使用说明

到目前为止好像没有发生什么

再看看它还有什么方法

    // afterPropertiesSet 是实现的 InitializingBean 接口 会在属性加载完调用
    // 去检查 mybatis.config-location 指定的配置文件,我们没用上(什么配置都没有)
    public void afterPropertiesSet() {        this.checkConfigFileExists();    }
    private void checkConfigFileExists() {。。。}

    // 初始化SqlSessionFactory
    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) {
        。。。
        this.applyConfiguration(factory);
        。。。
    }
    // 根据配置文件 初始化一些配置,我们没用上(什么配置都没有)
    private void applyConfiguration(SqlSessionFactoryBean factory) {。。。}

    // 初始化 SqlSessionTemplate,你可以手动调用这个类执行sql
    // 但它并没有做别的
    @Bean
    @ConditionalOnMissingBean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {。。。}

    // 重点来了 这也是一个配置类
    // 看名字也知道了,MapperScanner
    // 但这个类其实没干啥,只不过在MapperFactoryBean.class, MapperScannerConfigurer.class 不存在时,记录log
    // 真正做事的是AutoConfiguredMapperScannerRegistrar
    @Configuration
    @Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})
    @ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
    public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
        public MapperScannerRegistrarNotFoundConfiguration() {
        }

        public void afterPropertiesSet() {
            MybatisAutoConfiguration.logger.debug("Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
        }
    }

    public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar {。。。}

先不管@Configuration为何嵌套
@Configuration可以参照:@Configuration 详解

3,AutoConfiguredMapperScannerRegistrar

首先要注意一件事
MapperFactoryBean.class, MapperScannerConfigurer.class 他们很重要,
故名思意啊

看下源码

    public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar {
        private BeanFactory beanFactory;
        public AutoConfiguredMapperScannerRegistrar() {
        }

        // 主要是这个方法,实现至ImportBeanDefinitionRegistrar 
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {。。。}

        public void setBeanFactory(BeanFactory beanFactory) {
            this.beanFactory = beanFactory;
        }
    }

实现了两个接口

registerBeanDefinitions方法

        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            if (!AutoConfigurationPackages.has(this.beanFactory)) {
                MybatisAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
            } else {
                MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper");
                // spring-boot扫描的包
                List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
                if (MybatisAutoConfiguration.logger.isDebugEnabled()) {
                    packages.forEach((pkg) -> {
                        MybatisAutoConfiguration.logger.debug("Using auto-configuration base package '{}'", pkg);
                    });
                }

                // 构建MapperScannerConfigurer定义,并加入spring容器
                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
                builder.addPropertyValue("processPropertyPlaceHolders", true);
                builder.addPropertyValue("annotationClass", Mapper.class);
                builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
                BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
                Set<String> propertyNames = (Set)Stream.of(beanWrapper.getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet());
                if (propertyNames.contains("lazyInitialization")) {
                    builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
                }

                if (propertyNames.contains("defaultScope")) {
                    builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}");
                }

                registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
            }
        }

这个方法其实就做了一件事
构建MapperScannerConfigurer定义并加入到spring容器中,并注入了一些属性
这样在spring容器加载类的时候就可以实例化它了

为什么要这么做,而不是@Component注解来加载MapperScannerConfigurer
个人理解
主要是为了,注入一些特别的属性

4,MapperScannerConfigurer

定义

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
  。。。
}

实现了4个接口
请参照:spring 生命周期 扩展点

其他函数不介绍了
主要是这个

    // spring 生命周期函数 实现至BeanDefinitionRegistryPostProcessor
    // 会在spring容器扫描完@Component后执行
    // 请注意@Mapper是Mybatis的注解,并没有组合@Component,所以不会被spring容器加载
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        // 这个依然是给 mapperScannerBean 注入属性,看名字也知道
        if (this.processPropertyPlaceHolders) {
            this.processPropertyPlaceHolders();
        }
        
        // 看名字 就知道要开始 扫描mapper了
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        scanner.setAddToConfig(this.addToConfig);
        // 扫描@Mapper标注的类
        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);
        // 其他的属性 不一一介绍,关键是这个看使用的的FactoryBean
        scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
        if (StringUtils.hasText(this.lazyInitialization)) {
            scanner.setLazyInitialization(Boolean.valueOf(this.lazyInitialization));
        }

        if (StringUtils.hasText(this.defaultScope)) {
            scanner.setDefaultScope(this.defaultScope);
        }

        scanner.registerFilters();
        // 扫描指定包,如果不指定就是 main函数所在包 与 spring 扫描的一样
        scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
    }    

5,ClassPathMapperScanner

继承自:ClassPathBeanDefinitionScanner
重写了:doScan

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
  // 上面提到的,事实上我们没有传入mapperFactoryBeanClass,所以这里用的是MapperFactoryBean
  public void setMapperFactoryBeanClass(Class<? extends MapperFactoryBean> mapperFactoryBeanClass) {
        this.mapperFactoryBeanClass = mapperFactoryBeanClass == null ? MapperFactoryBean.class : mapperFactoryBeanClass;
    }

    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        // 调用父类spring的扫描器
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
        if (beanDefinitions.isEmpty()) {
            LOGGER.warn(() -> {
                return "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.";
            });
        } else {
            // 如果有Mapper,这个时候Mapper已经在spring容器中了
            this.processBeanDefinitions(beanDefinitions);
        }

        return beanDefinitions;
    }

    // 修改Mapper定义
    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        BeanDefinitionRegistry registry = this.getRegistry();
        Iterator var4 = beanDefinitions.iterator();

        while(var4.hasNext()) {
            BeanDefinitionHolder holder = (BeanDefinitionHolder)var4.next();
            AbstractBeanDefinition definition = (AbstractBeanDefinition)holder.getBeanDefinition();
            // 我们的Mapper通常都是单例的,所以这个是 false
            boolean scopedProxy = false;
            if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
                definition = (AbstractBeanDefinition)Optional.ofNullable(((RootBeanDefinition)definition).getDecoratedDefinition()).map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> {
                    return new IllegalStateException("The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]");
                });
                scopedProxy = true;
            }

            String beanClassName = definition.getBeanClassName();
            LOGGER.debug(() -> {
                return "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' mapperInterface";
            });
            definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
            // 指定Mapper的class为MapperFactoryBean。为生成实例做准备
            // 因为我们Mapper是接口,不能生成实例
            definition.setBeanClass(this.mapperFactoryBeanClass);
            definition.getPropertyValues().add("addToConfig", this.addToConfig);
            definition.setAttribute("factoryBeanObjectType", beanClassName);
            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(() -> {
                        return "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(() -> {
                        return "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.";
                    });
                }

                definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
                explicitFactoryUsed = true;
            }

            if (!explicitFactoryUsed) {
                LOGGER.debug(() -> {
                    return "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.";
                });
                definition.setAutowireMode(2);
            }

            definition.setLazyInit(this.lazyInitialization);
            if (!scopedProxy) {
                if ("singleton".equals(definition.getScope()) && this.defaultScope != null) {
                    definition.setScope(this.defaultScope);
                }

                if (!definition.isSingleton()) {
                    BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
                    if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
                        registry.removeBeanDefinition(proxyHolder.getBeanName());
                    }
                    // 如果不是单例模式的时候 重新注册
                    registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
                }
            }
        }

    }
}

6,MapperFactoryBean

上面的步骤是吧我们的Mapper注册到spring容器了
还没有实例化
但是,我们的Mapper是接口,没有办法直接实例化
这个时候MapperFactoryBean 就上场了

MapperFactoryBean
继承自:SqlSessionDaoSupport 这个辅助类先不看
实现了:FactoryBean,这个接口的作用是,当spring创建实现了FactoryBean接口的Bean时,当你将这个类型注入到其他对象时,实际上返回的是getObject方法返回的对象,返回的类型是getObjectType返回的类型

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    // 实现自 FactoryBean
    // 这个方法是真正生成 Mapper实例的
    public T getObject() throws Exception {
        // 这个 this.getSqlSession() 是SqlSessionDaoSupport  的方法,返回的sqlSessionTemplate
        // sqlSessionTemplate在最初的MybatisAutoConfiguration 实例化过
        return this.getSqlSession().getMapper(this.mapperInterface);
    }
    // 实现自 FactoryBean
    public Class<T> getObjectType() {
        return this.mapperInterface;
    }
}

看下
SqlSessionTemplate

public class SqlSessionTemplate implements SqlSession, DisposableBean {
    public <T> T getMapper(Class<T> type) {
        return this.getConfiguration().getMapper(type, this);
    }

    public Configuration getConfiguration() {
        // sqlSessionFactory 在最初的MybatisAutoConfiguration 实例化过
        return this.sqlSessionFactory.getConfiguration();
    }
}

然后 Configuration

public class Configuration {
    protected final MapperRegistry mapperRegistry;
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return this.mapperRegistry.getMapper(type, sqlSession);
    }
}

然后 MapperRegistry

public class MapperRegistry {
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                // 终于new对象了
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }
}

然后MapperProxyFactory

public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Class<T> getMapperInterface() {
        return this.mapperInterface;
    }

    public Map<Method, MapperMethodInvoker> getMethodCache() {
        return this.methodCache;
    }

    protected T newInstance(MapperProxy<T> mapperProxy) {
        // 通过java的动态代理 创建Mappe对象
        // 动态代理就不讲了
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

    public T newInstance(SqlSession sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }
}

到上面为止,Mapper类的实例化已经完成了

7,接口调用

MapperProxy
实现了InvocationHandler:java动态代理的Handler

public class MapperProxy<T> implements InvocationHandler, Serializable { 
    // 真正执行方法是这个代理方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            // 这个判断没弄明白,不知道 什么情况可以直接调用原类的方法,这边走的是后者
            return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
    }

    private MapperProxy.MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
        try {
            MapperProxy.MapperMethodInvoker invoker = (MapperProxy.MapperMethodInvoker)this.methodCache.get(method);
            // 如果执行过就缓存起来
            return invoker != null ? invoker : (MapperProxy.MapperMethodInvoker)this.methodCache.computeIfAbsent(method, (m) -> {
                // 判断是否是默认方法,我们的都不是
                if (m.isDefault()) {
                    try {
                        return privateLookupInMethod == null ? new MapperProxy.DefaultMethodInvoker(this.getMethodHandleJava8(method)) : new MapperProxy.DefaultMethodInvoker(this.getMethodHandleJava9(method));
                    } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException var4) {
                        throw new RuntimeException(var4);
                    }
                } else {
                    // PlainMethodInvoker是个内部类 很简单
                    return new MapperProxy.PlainMethodInvoker(new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()));
                }
            });
        } catch (RuntimeException var4) {
            Throwable cause = var4.getCause();
            throw (Throwable)(cause == null ? var4 : cause);
        }
    }

    private static class PlainMethodInvoker implements MapperProxy.MapperMethodInvoker {
        private final MapperMethod mapperMethod;

        public PlainMethodInvoker(MapperMethod mapperMethod) {
            this.mapperMethod = mapperMethod;
        }

        public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
            return this.mapperMethod.execute(sqlSession, args);
        }
    }

    interface MapperMethodInvoker {
        Object invoke(Object var1, Method var2, Object[] var3, SqlSession var4) throws Throwable;
    }
}

MapperMethod

public class MapperMethod {
    private final MapperMethod.SqlCommand command;
    private final MapperMethod.MethodSignature method;

    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        // 生成sql 对象
        this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
        // 方法签名
        this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
    }

    // 最终调用的是sqlSession 的方法,也就是
    // 最初在MybatisAutoConfiguration里生成的 sqlSessionTemplate
    public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        Object param;
        switch(this.command.getType()) {
        case INSERT:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
            break;
        case UPDATE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
            break;
        case DELETE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
            break;
        case SELECT:
            if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                this.executeWithResultHandler(sqlSession, args);
                result = null;
            } else if (this.method.returnsMany()) {
                result = this.executeForMany(sqlSession, args);
            } else if (this.method.returnsMap()) {
                result = this.executeForMap(sqlSession, args);
            } else if (this.method.returnsCursor()) {
                result = this.executeForCursor(sqlSession, args);
            } else {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(this.command.getName(), param);
                if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
                    result = Optional.ofNullable(result);
                }
            }
            break;
        case FLUSH:
            result = sqlSession.flushStatements();
            break;
        default:
            throw new BindingException("Unknown execution method for: " + this.command.getName());
        }

        if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
            throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
        } else {
            return result;
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读