开发者

spring整合mybatis的底层原理分析

目录
  • spring整合myBATis的底层原理
    • 一、手写一个spring集成mybatis
    • 二、原理解析
  • 总结

    spring整合mybatis的底层原理

    原理:

    • FactoryBean的自定义对象
    • jdk动态代理Mapper接口对象

    一、手写一个spring集成mybatis

    目录结构:

    spring整合mybatis的底层原理分析

    1.1 入口类

    public class Test {
        public static void main(String[] args) {
    
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
            context.register(AppConfig.class);
            context.refresh();
    
            UserService userService = (UserService)context.getBean("userService");
            userService.test();
        }
    }

    1.2 配置类

    @CondorHeroMapperScan("com.athome.tulin.springmybatis.mapper")
    @ComponentScan("com.athome.tulin.springmybatis")
    public class AppConfig {
        @Bean
        public SqlSessionFactory sqlSessionFactory() throws IOException {
            System.out.println("4.依赖注入MemberMapper需要先创建对象………AppConfig…………SqlSessionFactory………");
            InputStream inputStream = Resources.getResourceAsStream("mybatis.XML");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            return sqlSessionFactory;
        }
    }

    1.3 业务类

    @Component
    public class UserService {
    
        public UserService() {
            System.out.println("3.…………创建UserService…………");
        }
    
        //如何把mybatis生成的UserMapper的代理对象赋值给UserMapper
        @Autowired
        private UserMapper userMapper;
    
        @Autowired
        private OrderMapper orderMapper;
    
        @Autowired
        private MemberMapper memberMapper;
    
        public void test(){
            System.out.println("7.……UserService…test…");
            System.out.println(userMapper.selectById());
            System.out.println(orderMapper.selectById());
            System.out.println(memberMapper.selectById());
        }
    }

    1.4 创建3个Mapper接口

    public interface MemberMapper {
    
        @Select("select 'member' ")
        String selectById();
    }
    public interface OrderMapper {
        @Select("select 'order' ")
        String selectById();
    }
    public interface UserMapper {
        @Select("select 'user' ")
        String selectById();
    }

    1.5 自定义注解

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Import(CondorHeroImportBeanDefinitionRegistrar.class)
    public @interface CondorHeroMapperScan {
        String www.devze.comvalue();
    }

    1.6 自定义fanctoryBean

    public class CondorHeroFactoryBean implements FactoryBean {
        
        private Class mapperInterface;
    
        private SqlSession sqlSession;
        public CondorHeroFactoryBean(Class mapperInterface) {
            this.mapperInterfajsce = mapperInterface;
        }
        /**
         * 从容器查找SqlSessionFactory 并获取sqlSession 赋值于sqlSession
         * 扫描的时候有 beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
         * 那他就会自动找set方法
         * @param sqlSessionFactory
         */
        public void setSqlSession(SqlSessionFactory sqlSessionFactory) {
            System.out.println("5.……setSqlSession……");
            sqlSessionFactory.getConfiguration().addMapper(mapperInterface);
            this.sqlSession = sqlSessionFactory.openSession();
        }
        
        @Override
        public Object getObject() throws Exception {
            //动态代理获取UserMapper接口对象
            System.out.println("6.……getObject……");
    
           return sqlSession.getMapper(mapperInterface);
        }
    
        @Override
        public Class<?> getObjectType() {
            return mapperInterface;
        }
    }

    1.7 自定义Bean注册类

    public class CondorHeroImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
            System.out.println("1.……registerBeanDefinitions……");
            //1.获取注解上的指定路径
            Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(CondorHeroMapperScan.class.getName());
            String path = (String)annotationAttributes.get("value");
            //2.扫描
            CondorHeroBeanDefinitionScanner scanner = new CondorHeroBeanDefinitionScanner(registry);
           scanner.addIncludeFilter(new TypeFilter() {
               @Override
               public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                   return true;
               }
           });
    
            scanner.scan(path);
        }
    }

    1.8 自定义扫描类

    public class CondorHeroBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
        public CondorHeroBeanDefinitionScanner(BeanDefinitionRegistry registry) {
            super(registry);
        }
    
        @Override
        protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition)  {
            //只关心接口(判断是否是接口)
           return beanDefinition.getMetadata().isInterface();
        }
    
        /**
         * 扫描路径并得到beanDefinition
         * @param basePackages
         * @return
         */
        @Override
        protected Set<BeanDefinitionHolder> DOScan(String... basePackages) {
            System.out.println("2.……doScan……");
            Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
    
            for (BeanDefinitionHolder beanDefinitionHolder: beanDefinitionHolders) {
                GenericBeanDefinition beanDefinition = (GenericBeanDefinition)beanDefinitionHolder.getBeanDefinition();
                //设置值
                beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
                //设置名称
                beanDefinition.setBeanClassName(CondorHeroFactoryBean.class.getName());
    
                //将MapperFactoryBean的注入模型设置为By-Type。也就是说,MapperFactoryBean中的setXxx中的属性会从容器中来进行查找
                beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
            }
            return beanDefinitionHolders;
        }
    }

    运行结果是:

    1.……registerBeanDefinitions……

    2.……doScan……

    3.…………创建UserService…………

    4.依赖注入MemberMapper需要先创建对象………AppConfig…………SqlSessionFactory………

    5.……setSqlSession……

    6.……getObject……

    5.……setSqlSession……

    6.……getObject……

    5.……setSqlSession……

    6.……getObject……

    7.……UserService…test… user order member

    二、原理解析

    2.1 通过@MapperScan导入了MapperScannerRegistrar类

    spring整合mybatis的底层原理分析

    2.2 MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法

    spring整合mybatis的底层原理分析

    2.3 在registerBeanDefinitions方法中注册一个MapperScannerConfigurer类型的BeanDefinition

    spring整合mybatis的底层原理分析

    2.4 而MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,所以Spring在启动过程中时会调用它的postProcessBeanDefinitionRegistry()方法

    spring整合mybatis的底层原理分析

    2.5 在postProcessBeanDefinitionRegistry方法中会生成一个ClassPathMapperScanner对象,然后进行扫描(scanner.scan)2.6 通过利用Spring的扫描后,会把接口扫描出来并且得到对应的BeanDefinition

    spring整合mybatis的底层原理分析

    2.7 接下来把扫描得到的BeanDefinition进行修改,把BeanClass修改为MapperFactoryBean,把AutowireMode修改为byType(在类ClassPathMapperScanner的方法processBeanDefinitions中)

    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 =www.devze.com true;
       php   }
    
          if (!explicitFactoryUsed) {
            LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
            //将MapperFactoryBean的注入模型设置为By-Type。也就是说,MapperFactoryBean中的setXxx中的属性会从容器中来进行查找
           definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
          }
          definition.setLazyInit(lazyInitialization);javascript
        }
      }

    2.8 扫描完成后,Spring就会基于BeanDefinition去创建Bean了,相当于每个Mapper对应一个FactoryBean

    2.9 在MapperFactoryBean中的getObject方法中,调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,这个代理对象就成为Spring容器中的Bean

    public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {}
     @Override
      public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
      }
      @Override
      public Class<T> getObjectType() {
        return this.mapperInterface;
      }

    注意:这里的getObject调用时机是,在创建的对象依赖了Mapper对象就会去创建该Mapper对象,此时通过MapperFactoryBean去获取

    2.10 sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生

    上面的getSqlSession()对应源码是:

    spring整合mybatis的底层原理分析

    2.11 MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean

    2.12 如果你定义的是一个SqlSessionFactory类型的bean,那么最终也会被包装为一个SqlSessionTemplate对象,并且赋值给sqlSession属性

    这一步是程序员自己定义一个SqlSessionFactory,例如:

    @CondorHeroMapperScan("com.athome.tulin.springmybatis.mapper")
    @ComponentScan("com.athome.tulin.springmybatis")
    public class AppConfig {
        @Bean
        public SqlSessionFactory sqlSessionFactory() throws IOException {
            System.out.println("4.依赖注入MemberMapper需要先创建对象………AppConfig…………SqlSessionFactory………");
            InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            return sqlSessionFactory;
        }
    }

    这里定义的SqlSessionFactory 会赋值于2.10的sqlSessionTemplate

    2.13 而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就产生一个Mapper接口代理对象

    spring整合mybatis的底层原理分析

    2.14 当执行该代理对象的某个方法时,就会进入到Mybatis框架的底层执行流程

    至此:业务类中的引入Mapper对象就复制成功。

     @Autowired
        private OrderMapper orderMapper;

    即:这时候的orderMapper就 是赋值了代理对象的对象是有值 的。

    总结

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜