Spring整合Mybatis核心原理分析

1.3.2版本

 

  1. 通过@MapperScan导入了MapperScannerRegistrar类
  2. MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法
  3. 在registerBeanDefinitions方法中定义了一个ClassPathMapperScanner对象,用来扫描mapper
  4. 设置ClassPathMapperScanner对象可以扫描到接口,因为在Spring中是不会扫描接口的
  5. 同时因为ClassPathMapperScanner中重写了isCandidateComponent方法,导致isCandidateComponent只会认为接口是备选者Component
  6. 通过利用Spring的扫描后,会把接口扫描出来并且得到对应的BeanDefinition
  7. 接下来把扫描得到的BeanDefinition进行修改,把BeanClass修改为MapperFactoryBean,把AutowireMode修改为byType
          definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
          definition.setBeanClass(this.mapperFactoryBean.getClass()); 
          //这里其实就是MapperFactoryBean.class

     

  8. 扫描完成后,Spring就会基于BeanDefinition去创建Bean了,相当于每个Mapper对应一个FactoryBean(单例的)
  9. 在MapperFactoryBean中的getObject方法中,调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个代理对象。MapperFactoryBean通过父类SqlSessionDaoSupport的setSqlSessionFactory或setSqlSessionTemplate方法来设置SqlSession。
    public abstract class SqlSessionDaoSupport extends DaoSupport {
    
      private SqlSession sqlSession;
    
      private boolean externalSqlSession;
    
      public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        if (!this.externalSqlSession) {
          this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
        }
      }
    
      public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSession = sqlSessionTemplate;
        this.externalSqlSession = true;
      }
    
    public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    
      @Override
      public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
      }
    public class DefaultSqlSession implements SqlSession {
        private final Configuration configuration;
    
        public <T> T getMapper(Class<T> type) {
            return this.configuration.getMapper(type, this);
        }
    	public class Configuration {
    	
        protected final MapperRegistry mapperRegistry;
    	
        public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
            return this.mapperRegistry.getMapper(type, sqlSession);
        }
    public class MapperRegistry {
        private final Configuration config;
        private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
    
        public MapperRegistry(Configuration config) {
            this.config = config;
        }
    
        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 {
                    return mapperProxyFactory.newInstance(sqlSession);
                } catch (Exception var5) {
                    throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
                }
            }
        }
    public class MapperProxyFactory<T> {
        private final Class<T> mapperInterface;
        private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();
    
        public MapperProxyFactory(Class<T> mapperInterface) {
            this.mapperInterface = mapperInterface;
        }
    
        public Class<T> getMapperInterface() {
            return this.mapperInterface;
        }
    
        public Map<Method, MapperMethod> getMethodCache() {
            return this.methodCache;
        }
    
        protected T newInstance(MapperProxy<T> mapperProxy) {
            //在这里使用JDK动态代理生成的代理对象
            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);
        }
    }

     

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

     

  11. MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean。
  12. 如果你定义的是一个SqlSessionFactory类型的bean,那么最终也会被包装为一个SqlSessionTemplate对象,并且赋值给sqlSession属性
  13. 而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就会利用SqlSessionFactory来生成一个代理对象

 

 

2.0.5版本

  1. 通过@MapperScan导入了MapperScannerRegistrar类
  2. MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法
  3. registerBeanDefinitions方法中生成了一个MapperScannerConfigurer类型的BeanDefinition
  4. 而MapperScannerConfigurer实现了实现了BeanDefinitionRegistryPostProcessor接口,所以Spring在启动过程中时会调用它的postProcessBeanDefinitionRegistry()方法
  5. 在postProcessBeanDefinitionRegistry方法中会生成一个ClassPathMapperScanner对象,然后进行扫描
  6. 后续的逻辑和1.3.2版本一样。

 

带来的好处是,可以不使用@MapperScan注解,而可以直接定义一个Bean,比如:

@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
    MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
    mapperScannerConfigurer.setBasePackage("com.luban");
    return mapperScannerConfigurer;
}

    -----------------------------------------------理解-----------------------------------------------------------
1.3.2
第一种实现方式:使用注解通过@MapperScan导入了MapperScannerRegistrar类   (@Import(ImportBeanDefinitionRegistar.class) )
    ImportBeanDefinitionRegistar.registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry bdr,){
        
    
    }
    定义两个bean
        1)DataSource
        2)SqlSessionFactory
    
    @MapperScan(扫描路径)
        这个注解也可以指定SqlSessionFactory
        也可以指定Mapper.class 指定注解 annotionClass=Mapper.class ,如果增加了注解,会生成一个注解过滤器,只搜索使用了Mapper注解的。springboot 中才使用了Mapper.class注解。
        ClassPathMapperScanner(BeanDefinitionRegistry)扫描器集成了spring的扫描器ClassPathBeanDefinitionScanner,先调用父类spring的扫描器(扫描的结果,已经使用mapper等过滤了,而且扫描出来的只有接口,而且还都只是接口的全路径)
            isCandidateComponent(AnnotatedBeanDefinition)方法被mybatis重写了,只查找接口的class
        扫描过程:
            1)先调用父类spring的扫描器(扫描的结果是beandefinition,已经使用mapper等过滤了,而且扫描出来的只有接口,而且beanclass还都只是接口的全路径)
            2)处理 MapperFactoryBean
                (1)修改构造函数参数为接口名字,动态代理时使用
                (2)修改beanclass为this.mapperBeanFactoryBean.getClass,即为
                如果指定了SqlSessionFactory,就修改成指定的SqlSessionFactory
                (3)如果没指定,默认的装配模式是byType:后面根据接口类型查找值
                (4)MapperFactoryBean通过父类SqlSessionDaoSupport的setSqlSessionFactory或setSqlSessionTemplate方法来设置SqlSession
                (5)MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean。
如果你定义的是一个SqlSessionFactory类型的bean,那么最终也会被包装为一个SqlSessionTemplate对象,并且赋值给sqlSession属性
而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就会利用SqlSessionFactory来生成一个代理对象

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值