当前位置:   article > 正文

mybatis源码整体结构分析与plus的扩展(结构图完成)_mybatisplus 源码

mybatisplus 源码

​​​​​​mybatis – MyBatis 3 | Getting started        在介绍前,先想想mybatis的用法,通常是写mapper接口与对应的xml,很明显这些接口要被动态代理,实现将请求参数转换成可执行的sql语句,使用sqlSession来进行操作,最后返回结果。这样在spring使用的话,应该是有注册beanDefination,里面是一个FactoryBean,由它的getObject()产生真正的接口实现类,这一点上,与dubbo的接口可以变成一个远程调用差不多机制。

        下图就是最后根据分析,整理的结构索引图。分析完代码其实也很容易忘记,看图方便记忆。

  对用户写的mapper类,典型的处理过程:

  1. ClassPathMapperScanner扫描包下的mapper接口类,产生beanDefination。
  2. beanDefination中设置为MapperFactoryBean,可用getObject()生成接口的实现类。
  3. getObject()中,又使用sessionFactory产生的sessionTemplate创建,创建时委托configuration创建的同时,把自己当this传进去。
  4. Configuration中又找到MapperRegistry后,从其map中找到mapper对应的MapperProxyFactory(MapperFactoryBean初始化时放入map中的)。
  5. MapperProxyFactory动态生成是,会用MapperProxy 这个invocationHandler来动态生成。
  6. MapperProxy的invoker方法中,又会用plainMethodInvoker和新的参数MapperMethod(包括了用户method与sessionTemplatemethod的关系)来处理。
  7. MapperMethod因为有了对应关系,最后还是前面this时放入的sessionTemplate处理。
  8. sessionTemplate内部又用动态代理生成sqlSessionProxy,委托它处理。
  9. sqlSessionProxy的invocationHandler是SqlSessionInterceptor,它又会产生一个DefaultSqlSession来处理,并用configuration产生一个executor给它。它会先从configuration中获取新的MappedStatement,再用executor使用MappedStatement处理。
  10. executor执行时,又从configuration获取一个新的StatementHandler来处理MappedStatement中的参数对象。
  11. StatementHandler内有生成的parameterHandler,resultSetHandler,typeHandlerRegistry等,可以处理请求参数,可以处理返回值,可以对参数中的属性,按类型进行转换。
  12. StatementHandler会用连接DB后生成的java.sql.Statement来处理转换后产生的真正的SQL语句,最后还会用resultSetHandler处理返回值。

      下面详细介绍分析的过程,我们还是先看一下官方的介绍,找到非spring环境中的通常使用方法,来构建出mybatis的运行时关系图,以及创建运行时关系的过程。

一、mybatis-3.5.9.jar

1、官方说明与初步分析
mybatis – MyBatis 3 | Getting started

Every MyBatis application centers around an instance of SqlSessionFactory. A SqlSessionFactory instance can be acquired by using the SqlSessionFactoryBuilder. SqlSessionFactoryBuilder can build a SqlSessionFactory instance from an XML configuration file, or from a custom prepared instance of the Configuration class.

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
  new SqlSessionFactoryBuilder().build(inputStream);
.........
//使用方式一:
try (SqlSession session = sqlSessionFactory.openSession()) {
  Blog blog = session.selectOne(
    "org.mybatis.example.BlogMapper.selectBlog", 101);
}
//使用方式二:
try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);//interface
  Blog blog = mapper.selectBlog(101);
}

SqlSessionFactory就是核心,而且简单的看,就是从配置文件来构建这样一个核心类。怎么与前面的接口设想有点不一样了,当然sqlSesssion很重要,接口的实现都要使用它,所以它的工厂也是入口操作类也正常。从两种使用方式来看,可以设想mapper 就是那个接口实现类。

不如直接查一下DefaultSqlSession的getMapper怎么用,果然是把session传进去生成接口的实现类,并且是给这个实现类用。后面分别向下介绍session的操作,向上介绍接口的处理,最后理出一个关系出来。

  1. @Override
  2.   public <T> T getMapper(Class<T> type) {
  3.     return configuration.getMapper(type, this);
  4.   }

2、Session的功能

 看看上面使用方法:session.selectOne(),所使用的selectList方法。几个参数很明确,大概可以分析出执行过程:

  • 一个从配置中获取MappedStatement,这些都是预先解析好的configuration的数据。
  • StatementHandler是为每一个sql操作而产生的处理类对象,stmt也是如此。而configuration,mappedStatement,executor,connection,transaction这些都可以看做是单例对象。
  • SimpleExecutor中的StatementHandler对象本来想的不需要在doQuery()这里出现吧? 产生的StatementHandler后,它又当参数生成stmt,stmt之后又当参数给StatementHandler处理用,感觉关系有点不清晰。
  • 私以为类关系最好不要循环,但在prepareStatement()与getConnection()中,都用到了SimpleExecutor的transaction对象,所以不能下沉到StatementHandler中处理,当然我写的话,可能会把transaction传递下去,在StatementHandler中处理。也许是因为Executor或者其它接口有多个实现类,为了统一代码的结构而这么设计吧,暂不深入。

上述分析中要分清单例类(包括配置数据对象)与每一次处理产生的类/对象,最后理出一个运行关系图。

  1. //DefaultSession.java
  2. private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
  3. try {
  4. MappedStatement ms = configuration.getMappedStatement(statement);
  5. return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
  6. } catch (Exception e) {
  7. throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
  8. } finally {
  9. ErrorContext.instance().reset();
  10. }
  11. }
  12. //SimpleExecutor.java
  13. @Override
  14. public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  15. Statement stmt = null;
  16. try {
  17. Configuration configuration = ms.getConfiguration();
  18. StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
  19. stmt = prepareStatement(handler, ms.getStatementLog());
  20. return handler.query(stmt, resultHandler);
  21. } finally {
  22. closeStatement(stmt);
  23. }
  24. }
  25. //SimpleStatementHandler.java
  26. @Override
  27. public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  28. String sql = boundSql.getSql();
  29. statement.execute(sql);
  30. return resultSetHandler.handleResultSets(statement);
  31. }

3. MappedStatement

从上面可以看出,配置中的MappedStatement是一个重要的对象,可以认为是配置对象,从名字可以看到是mapped的声明,我们知道mapper有很多种方式,包括xml,包括接口,最终应该都被特定的parse类,解析成一个个MappedStatement,存起来来用吧。

这个过程应该是启动后的初始化中完成,之后可以正常处理用户过来的请求。

再回看一下官方的 Building SqlSessionFactory without XML,有助于我们理解类关系。

DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory =
  new JdbcTransactionFactory();
Environment environment =
  new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory =
  new SqlSessionFactoryBuilder().build(configuration);

 这里就比较清楚了,需要用配置configuration来产生SqlSessionFactory。而configuration中包括了:dataSource,TransactionFactory,以及BlogMapper.class这些接口类产生的mapper。所以前面说这些都是单例对象,包括解释用户接口产生的。我们猜测这个BlogMapper.class应该会产生MappedStatement与接口实现类。实现类中用方法名字与参数,使用sqlSession时,会找到MappedStatement,这样进行数据库操作。

从以下代码跟踪发现,会从XML配置文件中,产生MappedStatement。

  1. //configuration.java
  2. protected void buildAllStatements() {
  3. ...
  4. incompleteStatements.removeIf(x -> {
  5. x.parseStatementNode();
  6. return true;
  7. });
  8. ...
  9. //这里还有从接口解析出statement,后面说明。
  10. }
  11. public void addIncompleteStatement(XMLStatementBuilder incompleteStatement) {
  12. incompleteStatements.add(incompleteStatement);
  13. }
  14. //XMLMapperBuilder.java
  15. private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
  16. for (XNode context : list) {
  17. final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
  18. try {
  19. statementParser.parseStatementNode();
  20. } catch (IncompleteElementException e) {
  21. configuration.addIncompleteStatement(statementParser);
  22. }
  23. }
  24. }
  25. //XMLStatementBuilder.java 可以发现产生MappedStatement对象了。
  26. public void parseStatementNode() {
  27. String id = context.getStringAttribute("id");
  28. String databaseId = context.getStringAttribute("databaseId");
  29. ...
  30. builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
  31. fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
  32. resultSetTypeEnum, flushCache, useCache, resultOrdered,
  33. keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  34. }

那么,如何从BlogMapper.class这样的接口中产生MappedStatement呢?我们从前面说的官方without XML的使用示例configuration.getMapper()开始。

  • 解析的时候,按接口放入一个MapperProxyFactory。
  • 使用的时候,用MapperProxyFactory产生实例,这时候传入sqlSession进去。
  • sqlSession是在MapperMethod的executor中使用的,sqlSession会从configuration中得到MappedStatement。configuration.getMappedStatement(statement);
  • 但是前面没有看到从接口产生MappedStatement的代码啊?只看了从xml产生地代码。实际上在buildAllStatements();中,之前只分析了一种情况,还有另两种情况,其中一个是incompleteMethods中解析。

上述前两点,正好说明与前面的猜测一致。接口是要产生一个实现类,这个实现类用sqlSession访问数据库。

  1. //configuratin.java
  2. public <T> void addMapper(Class<T> type) {
  3. mapperRegistry.addMapper(type);
  4. }
  5. //MapperRegistry.java
  6. public <T> void addMapper(Class<T> type) {
  7. ...
  8. try {
  9. knownMappers.put(type, new MapperProxyFactory<>(type));
  10. // It's important that the type is added before the parser is run
  11. // otherwise the binding may automatically be attempted by the
  12. // mapper parser. If the type is already known, it won't try.
  13. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
  14. parser.parse();
  15. loadCompleted = true;
  16. }
  17. ...
  18. }
  19. public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  20. final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  21. ...
  22. try {
  23. return mapperProxyFactory.newInstance(sqlSession);
  24. } catch (Exception e) {
  25. throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  26. }
  27. }
  28. //MapperProxyFactory.java
  29. @SuppressWarnings("unchecked")
  30. protected T newInstance(MapperProxy<T> mapperProxy) {
  31. return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  32. }
  33. //MapperMethod.java
  34. public Object execute(SqlSession sqlSession, Object[] args) {
  35. Object result;
  36. switch (command.getType()) {
  37. case INSERT: {
  38. Object param = method.convertArgsToSqlCommandParam(args);
  39. result = rowCountResult(sqlSession.insert(command.getName(), param));
  40. break;
  41. ...
  42. }
  43. }
  44. //MapperAnnotationBuilder.java
  45. void parseStatement(Method method) {
  46. final Class<?> parameterTypeClass = getParameterType(method);
  47. final LanguageDriver languageDriver = getLanguageDriver(method);
  48. ...
  49. final String mappedStatementId = type.getName() + "." + method.getName();//id就是类.方法名
  50. ...
  51. }

4. configuration

前面的分析,已经了解了mybatis的大概类之间关系。configuration是一个重要的配置数据类,内容非常丰富。因为有足够的配置信息,它还会new一些实例类,有部分类工厂的功能。

这里我就仅分析一下TypeHandlerRegistry,因为我们生产环境出现了对enum类型参数,在高并发时解析出错的情况。enum的handler是使用时才注册的。此版本前一版本中,是发现jdbcHandlerMap中拿type对应的map为null时,new一个hashmap放进去,之后才设置map中的值,导致有可能后面线程发现已经不是null,却拿不到里面的值的情况。

  1. //Configuration.java
  2. protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
  3. //TypeHandlerRegistry.java
  4. public TypeHandlerRegistry(Configuration configuration) {
  5. this.unknownTypeHandler = new UnknownTypeHandler(configuration);
  6. register(Boolean.class, new BooleanTypeHandler());
  7. register(boolean.class, new BooleanTypeHandler());
  8. register(JdbcType.BOOLEAN, new BooleanTypeHandler());
  9. register(JdbcType.BIT, new BooleanTypeHandler());
  10. ...
  11. }
  12. @SuppressWarnings("unchecked")
  13. private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
  14. if (ParamMap.class.equals(type)) {
  15. return null;
  16. }
  17. Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
  18. TypeHandler<?> handler = null;
  19. if (jdbcHandlerMap != null) {
  20. handler = jdbcHandlerMap.get(jdbcType);
  21. if (handler == null) {
  22. handler = jdbcHandlerMap.get(null);//在上一个版本中,如果null,会new HashMap并放进去,之后再给这个map设置handler,这样在高并发时,可能造成得不到正确的handler,比如enum时,因为这个类型的handler,是使用时才会设置,并不是一开始就设置好。
  23. }
  24. if (handler == null) {
  25. // #591
  26. handler = pickSoleHandler(jdbcHandlerMap);
  27. }
  28. }
  29. // type drives generics here
  30. return (TypeHandler<T>) handler;
  31. }
  32. private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) {
  33. Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(type);
  34. if (jdbcHandlerMap != null) {
  35. return NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap) ? null : jdbcHandlerMap;
  36. }
  37. if (type instanceof Class) {
  38. Class<?> clazz = (Class<?>) type;
  39. if (Enum.class.isAssignableFrom(clazz)) {
  40. Class<?> enumClass = clazz.isAnonymousClass() ? clazz.getSuperclass() : clazz;
  41. jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(enumClass, enumClass);
  42. if (jdbcHandlerMap == null) {//当get不到时,会注册enum的handler。
  43. register(enumClass, getInstance(enumClass, defaultEnumTypeHandler));
  44. return typeHandlerMap.get(enumClass);
  45. }
  46. } else {
  47. jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz);
  48. }
  49. }
  50. typeHandlerMap.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);
  51. return jdbcHandlerMap;
  52. }

二、mybatis-spring-2.0.6.jar

        前面的分析只是在非spring的情况下分析的,通常我们使用的是spring的环境,这时候要使用mybatis-spring的包了。而且后面的mybatis-plus的分析也基于spring环境中的使用。

        这里就直接从mybatis plus的官方的使用示例开始,也就是其中的@MapperScan。

  1. @SpringBootApplication
  2. @MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
  3. public class Application {
  4. public static void main(String[] args) {
  5. SpringApplication.run(Application.class, args);
  6. }
  7. }
  8. @Retention(RetentionPolicy.RUNTIME)
  9. @Target(ElementType.TYPE)
  10. @Documented
  11. @Import(MapperScannerRegistrar.class) //注解的处理类
  12. @Repeatable(MapperScans.class)
  13. public @interface MapperScan
  14. //注解处理中,产生的bean定义:MapperScannerConfigurer
  15. BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);

通过跟踪发现:

  • MapperScan注解上导入MapperScannerRegistrar.class,里面再注册一个bean:MapperScannerConfigurer
  • 这个类又实现了BeanDefinitionRegistryPostProcessor,InitializingBean等重要接口。从名字看,本身是一个configurer,所以用它在spring扩展中产生需要的bean是很合理的。
  • postProcessBeanDefinitionRegistry中对找到的beanDefination进行了设置。比如:definition.setBeanClass(this.mapperFactoryBeanClass);//mapperFactoryBeanClass = MapperFactoryBean.class;
  • 而class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T>。正好是一个FactoryBean,与最前面说的,类似dubbo的consumer接口,所有这种接口都会被定义为FactoryBean放入spring容器,这样用getObject()产生真正的实现类。
  • getObject()中正好是:getSqlSession().getMapper(this.mapperInterface);与前面分析中非XML时使用的官方示例(使用方式二:BlogMapper mapper = session.getMapper(BlogMapper.class);//interface)一样了。
  • spring就是给最终客户一种简便的方式,将客户的配置纳入原有的体系中来。

其它就不分析了,关于mybatis-spring-boot-start中的autoconfiguration,后面会提到,现在快速进入mybatis-plus部分。

三、mybatis-plus-3.0.jar

是不是上面的方式用着还不够爽?jpa是不是也有自己的方便之处?拿来一些给mybatis助力吧。另外如果在执行sql过程前后想插入自己的处理,怎么办?plus都提供了机制。

1. mybatis plus的扩展之一

还是使用前面的mybatis plus的官方示例,发现用了这么一个base接口,还有泛型pojo类。

  1. public interface UserMapper extends BaseMapper<User> {
  2. }

这还是一个mapper接口,方法都在base类中,也对,每一个mapper中写的多数都一样,那整一个abstract类就行MybatisSqlSessionFactoryBean了。至于处理的数据对象不一样,那使用通用的反射代码,也都能获取各自的参数与sql,估计jpa也是这么弄的吧。想想这条路是可行的,也确实方便了使用者。自己有特殊的SQL,继承后另外写就行了。

2. MybatisPlusAutoConfiguration中的变化

MybatisPlusAutoConfiguration代替了mybatis的MybatisAutoConfiguration,看看有什么变化呢?

  1. //MybatisPlusAutoConfiguration.java
  2. package com.baomidou.mybatisplus.autoconfigure;
  3. import com.baomidou.mybatisplus.core.MybatisConfiguration;
  4. import com.baomidou.mybatisplus.core.config.GlobalConfig;
  5. import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
  6. import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
  7. import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
  8. import com.baomidou.mybatisplus.core.injector.ISqlInjector;
  9. import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
  10. // TODO 入参使用 MybatisSqlSessionFactoryBean
  11. private void applyConfiguration(MybatisSqlSessionFactoryBean factory) {
  12. // TODO 使用 MybatisConfiguration
  13. MybatisConfiguration configuration = this.properties.getConfiguration();
  14. if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
  15. configuration = new MybatisConfiguration();//使用扩展继承的configuration
  16. }
  17. ...
  18. factory.setConfiguration(configuration);
  19. }
  20. @Bean
  21. @ConditionalOnMissingBean
  22. public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
  23. // TODO 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean
  24. MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();//使用扩展继承的
  25. ...
  26. applyConfiguration(factory);//上面的方法
  27. ...
  28. }

MybatisSqlSessionFactoryBean:中增加了一个globalConfig,配置了一些东西,包括MybatisConfiguration。

MybatisConfiguration:使用了新的mapperRegistry来处理用户的接口

  1. @Override
  2. public <T> void addMapper(Class<T> type) {
  3. mybatisMapperRegistry.addMapper(type);
  4. }

3. MybatisMapperRegistry

        前面说了,plus中有了一个通用的接口,这里注册时,会使用pojo解析mappedStatement功能吗?实际上猜错了,这里没有找到,mappedstatement中还是记录的pojo对象,转化是后面executor中才有,在处理mappedstatement中的参数泛型对象时,而不是先转化好放入mappedstatement。

  1. //这里跟踪,没有找到处理 pojo的@Table注解的功能,实际上是有一个chain来处理参数。
  2. public <T> void addMapper(Class<T> type) {
  3. if (type.isInterface()) {
  4. ...
  5. try {
  6. // TODO 这里也换成 MybatisMapperProxyFactory 而不是 MapperProxyFactory
  7. knownMappers.put(type, new MybatisMapperProxyFactory<>(type));
  8. // It's important that the type is added before the parser is run
  9. // otherwise the binding may automatically be attempted by the
  10. // mapper parser. If the type is already known, it won't try.
  11. // TODO 这里也换成 MybatisMapperAnnotationBuilder 而不是 MapperAnnotationBuilder
  12. MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);
  13. parser.parse();
  14. loadCompleted = true;
  15. }...
  16. }
  17. //config中有parameterHandler,这里组成chain,来处理pojo等参数问题。
  18. public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
  19. ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
  20. parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
  21. return parameterHandler;
  22. }

上面getLang(),提示我们在MybatisXMLLanguageDriver中的MybatisParameterHandler中找到了pojo注解的处理过程。并且interceptorChain中有三个handler组成,最后给executor使用。

  • interceptorChain.pluginAll(parameterHandler);
  • interceptorChain.pluginAll(resultSetHandler);
  • interceptorChain.pluginAll(statementHandler);
  • executor = (Executor) interceptorChain.pluginAll(executor);

说明executor在执行时,会使用这些handler分别处理请求的pojo泛型参数,还会处理返回值。

4. mybatis plus的扩展之二---插件

插件也是对终端用户提供的一个非常有用的功能。一般的扩展机制,最多的就是interceptor,filter这类的责任链设计模式的使用,而plus的插件有点不同,虽然也是写interceptor。

先看一下写的interceptor如果加载到框架中,给谁使用吧。有两种试,一种是配置xml中写plugin后解析,一种是注解为spring的bean。介绍后一种吧。在自动配置类的构造函数中,会用ObjectProvider找到容器中所有的Interceptor。如:MybatisPlusAutoConfiguration(...,ObjectProvider<Interceptor[]> interceptorsProvider...),下面的代码又说明了会从xml中找到interceptor,也都给configuration,放入interceptorChain中 。

configuration有一个功能就是产生成4个重要的类对象,executor,statementHandler,parameterHandler,resultSetHandler。生成时,会有例如:interceptorChain.pluginAll(executor);的处理,产生一个个代理对象。由于interceptor注解上有说明使用的对象以及方法签名信息,所以只会对应的有效果。

  1. //MyplusAutoConfiguration.java中,会设置给MybatisSqlSessionFactoryBean
  2. if (!ObjectUtils.isEmpty(this.interceptors)) {
  3. factory.setPlugins(this.interceptors);
  4. }
  5. //MybatisSqlSessionFactoryBean.java 的buildSqlSessionFactory()中,会设置给mybatisConfiguration类自己的plugins。它的又是通过解析XNode 来的。而自动配置中从容器找到的,也会加过来,最后都给configuration。
  6. if (!isEmpty(this.plugins)) {
  7. Stream.of(this.plugins).forEach(plugin -> {
  8. targetConfiguration.addInterceptor(plugin);
  9. LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
  10. });
  11. }
  12. //MybatisXMLConfigBuilder.java中,会解析配置文件中的plugin
  13. private void pluginElement(XNode parent) throws Exception {
  14. if (parent != null) {
  15. for (XNode child : parent.getChildren()) {
  16. String interceptor = child.getStringAttribute("interceptor");
  17. Properties properties = child.getChildrenAsProperties();
  18. Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
  19. interceptorInstance.setProperties(properties);
  20. configuration.addInterceptor(interceptorInstance);
  21. }
  22. }
  23. }

Plus extension包中的MybatisPlusInterceptor就是这样的功能,不过它内部又包含了一个innerInterceptor.java的内部子拦截器列表。怎么加载此内部拦截器?方法上有个注释说明了:

     * 使用内部规则,拿分页插件举个栗子:
     * <p>
     * - key: "@page" ,value: "com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor"
     * - key: "page:limit" ,value: "100"
     * <p>

根据需要,可以自己定义拦截器,我们有组件使用拦截器,实现动态路由不同的数据库。

四、结束

上面的基本过程简单分析完了,我们项目还进一步扩展mybatis-plus,支持更多的base方法等。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/201418
推荐阅读
相关标签
  

闽ICP备14008679号