1.3.2版本
- 通过@MapperScan导入了MapperScannerRegistrar类
- MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法
- 在registerBeanDefinitions方法中定义了一个ClassPathMapperScanner对象,用来扫描mapper
- 设置ClassPathMapperScanner对象可以扫描到接口,因为在Spring中是不会扫描接口的
- 同时因为ClassPathMapperScanner中重写了isCandidateComponent方法,导致isCandidateComponent只会认为接口是备选者Component
- 通过利用Spring的扫描后,会把接口扫描出来并且得到对应的BeanDefinition
- 接下来把扫描得到的BeanDefinition进行修改,把BeanClass修改为MapperFactoryBean,把AutowireMode修改为byType
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59 definition.setBeanClass(this.mapperFactoryBean.getClass()); //这里其实就是MapperFactoryBean.class
- 扫描完成后,Spring就会基于BeanDefinition去创建Bean了,相当于每个Mapper对应一个FactoryBean(单例的)
- 在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); } }
- sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生
- MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean。
- 如果你定义的是一个SqlSessionFactory类型的bean,那么最终也会被包装为一个SqlSessionTemplate对象,并且赋值给sqlSession属性
- 而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就会利用SqlSessionFactory来生成一个代理对象
2.0.5版本
- 通过@MapperScan导入了MapperScannerRegistrar类
- MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法
- 在registerBeanDefinitions方法中生成了一个MapperScannerConfigurer类型的BeanDefinition
- 而MapperScannerConfigurer实现了实现了BeanDefinitionRegistryPostProcessor接口,所以Spring在启动过程中时会调用它的postProcessBeanDefinitionRegistry()方法
- 在postProcessBeanDefinitionRegistry方法中会生成一个ClassPathMapperScanner对象,然后进行扫描
- 后续的逻辑和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来生成一个代理对象