小言_互联网的博客

深入spring源码 spring整合mybatis原理解析及spring扩展点分析

395人阅读  评论(0)

前言

最近看了一个讲如何面试的视频,里面说到不要只写自己阅读过某某源码,要把它体现在项目中,就算自己做的项目中没有,也可以说自己看到别人的项目中利用了某个框架的某些特性,于是我就准备自己动手试试,学习一下优秀框架的精髓,我手动整合了spring和mybatis,视图体会mybatis的优秀之处。

开始

要开始整合spring和mybatis,自然是要先搭建一个maven的项目,我在整合spring和mybatis的同时还整合了log4j方便查看日志,整合了阿里的druid作为mysql的数据库连接池,由于我只是要整合spring和mybatis并找到mybatis基于spring的扩展点及整合的原理,没有必要使用web项目,所以我只引入了spring-context包。项目pom文件如下


  
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation= "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0 </modelVersion>
  6. <groupId>com.ww </groupId>
  7. <artifactId>mybatis-spring </artifactId>
  8. <version>1.0-SNAPSHOT </version>
  9. <dependencies>
  10. <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
  11. <dependency>
  12. <groupId>org.mybatis </groupId>
  13. <artifactId>mybatis </artifactId>
  14. <version>3.4.6 </version>
  15. </dependency>
  16. <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
  17. <dependency>
  18. <groupId>org.springframework </groupId>
  19. <artifactId>spring-context </artifactId>
  20. <version>4.3.25.RELEASE </version>
  21. </dependency>
  22. <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
  23. <dependency>
  24. <groupId>mysql </groupId>
  25. <artifactId>mysql-connector-java </artifactId>
  26. <version>5.1.47 </version>
  27. </dependency>
  28. <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
  29. <dependency>
  30. <groupId>org.springframework </groupId>
  31. <artifactId>spring-jdbc </artifactId>
  32. <version>4.3.25.RELEASE </version>
  33. </dependency>
  34. <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
  35. <dependency>
  36. <groupId>log4j </groupId>
  37. <artifactId>log4j </artifactId>
  38. <version>1.2.17 </version>
  39. </dependency>
  40. <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
  41. <dependency>
  42. <groupId>org.mybatis </groupId>
  43. <artifactId>mybatis-spring </artifactId>
  44. <version>1.3.2 </version>
  45. </dependency>
  46. <dependency>
  47. <groupId>org.projectlombok </groupId>
  48. <artifactId>lombok </artifactId>
  49. <version>1.18.10 </version>
  50. <scope>compile </scope>
  51. </dependency>
  52. <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
  53. <dependency>
  54. <groupId>com.alibaba </groupId>
  55. <artifactId>druid </artifactId>
  56. <version>1.1.10 </version>
  57. </dependency>
  58. </dependencies>
  59. </project>

由于我不喜欢使用xml,所以本次搭建我是用了纯注解的形式,在项目的resource目录下,我建立了application.properties文件


  
  1. spring.datasource.username=root
  2. spring.datasource.password= 123456
  3. spring.datasource.url=jdbc:mysql: //localhost:3306/testmybatis
  4. spring.datasource.driver=com.mysql.jdbc.Driver

编写配置类,获取application.properties中配置的数据库信息


  
  1. @Configuration
  2. @PropertySource( "classpath:application.properties")
  3. public class PropertiesConfig {
  4. @Value( "${spring.datasource.url}")
  5. public String url;
  6. @Value( "${spring.datasource.username}")
  7. public String username;
  8. @Value( "${spring.datasource.password}")
  9. public String password;
  10. @Value( "${spring.datasource.driver}")
  11. public String driver;
  12. public String getUrl() {
  13. return url;
  14. }
  15. public String getUsername() {
  16. return username;
  17. }
  18. public String getPassword() {
  19. return password;
  20. }
  21. public String getDriver() {
  22. return driver;
  23. }
  24. }

mapper文件如下


  
  1. public interface UserMapper {
  2. @Select( "select id,name,height,weight from user where id=#{id}")
  3. public User selectUser(Integer id);
  4. }

实体类


  
  1. @Data
  2. public class User {
  3. private int id;
  4. private String name;
  5. private String height;
  6. private String weight;
  7. }

相对应的,在mysql中我建了一个数据库,表名为user

 service类


  
  1. @Service
  2. public class UserService{
  3. @Autowired
  4. UserMapper mapper;
  5. public User getUser(int id) {
  6. //一开始log4j并没有输出日志,在官网上查了之后说加上这句话就可以打印日志了
  7. org.apache.ibatis.logging.LogFactory.useLog4JLogging();
  8. return mapper.selectUser(id);
  9. }
  10. }

让spring来启动的主配置类


  
  1. @Configuration
  2. @ComponentScan( "com.ww")
  3. @MapperScan( "com.ww.mapper")
  4. @PropertySource( "classpath:application.properties")
  5. public class MybatisConfig {
  6. //这些都是mybatis-spring官网上的例子,照着改改就行
  7. @Bean
  8. public DataSource dataSource(PropertiesConfig config) {
  9. DruidDataSource dataSource = new DruidDataSource();
  10. dataSource.setDriverClassName(config.getDriver());
  11. dataSource.setUrl(config.getUrl());
  12. dataSource.setPassword(config.getPassword());
  13. dataSource.setUsername(config.getUsername());
  14. return dataSource;
  15. }
  16. //这些都是mybatis-spring官网上的例子,照着改改就行
  17. @Bean
  18. public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
  19. SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
  20. factoryBean.setDataSource(dataSource);
  21. return factoryBean.getObject();
  22. }
  23. }

到这里项目算搭建完成了,接下来我们运行项目,来观察mybatis整合spring之后的运行过程,以及mybatis整合spring和不整合spring究竟有什么不同

首先我们看到主配置类上有一行@MapperScan的注解,表示扫描mapper到spring容器中,把mapper交给spring管理,那么我想知道mapper是什么时候被spring扫描并注入的呢,我们点进这个注解,看到这个注解是一个组合注解,其中有这么一行注解引起了我的注意

@Import(MapperScannerRegistrar.class)

这个注解是什么意思呢,Import注解的意思是导入资源,那么我们看看它导入的是个什么资源,看类的名字,我猜想这可能是一个mapper扫描器的注册器,于是我就点进去看看


  
  1. public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
  2. private ResourceLoader resourceLoader;
  3. /**
  4. * {@inheritDoc}
  5. */
  6. @Override
  7. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  8. AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
  9. ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
  10. // this check is needed in Spring 3.1
  11. if (resourceLoader != null) {
  12. scanner.setResourceLoader(resourceLoader);
  13. }
  14. Class<? extends Annotation> annotationClass = annoAttrs.getClass( "annotationClass");
  15. if (!Annotation.class.equals(annotationClass)) {
  16. scanner.setAnnotationClass(annotationClass);
  17. }
  18. Class<?> markerInterface = annoAttrs.getClass( "markerInterface");
  19. if (!Class.class.equals(markerInterface)) {
  20. scanner.setMarkerInterface(markerInterface);
  21. }
  22. Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass( "nameGenerator");
  23. if (!BeanNameGenerator.class.equals(generatorClass)) {
  24. scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
  25. }
  26. Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass( "factoryBean");
  27. if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
  28. scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
  29. }
  30. scanner.setSqlSessionTemplateBeanName(annoAttrs.getString( "sqlSessionTemplateRef"));
  31. scanner.setSqlSessionFactoryBeanName(annoAttrs.getString( "sqlSessionFactoryRef"));
  32. List<String> basePackages = new ArrayList<String>();
  33. for (String pkg : annoAttrs.getStringArray( "value")) {
  34. if (StringUtils.hasText(pkg)) {
  35. basePackages.add(pkg);
  36. }
  37. }
  38. for (String pkg : annoAttrs.getStringArray( "basePackages")) {
  39. if (StringUtils.hasText(pkg)) {
  40. basePackages.add(pkg);
  41. }
  42. }
  43. for (Class<?> clazz : annoAttrs.getClassArray( "basePackageClasses")) {
  44. basePackages.add(ClassUtils.getPackageName(clazz));
  45. }
  46. scanner.registerFilters();
  47. //调用ClassPathMapperScanner中的doScan方法,来扫描mapper并组装成beanDefinition
  48. scanner.doScan(StringUtils.toStringArray(basePackages));
  49. }
  50. /**
  51. * {@inheritDoc}
  52. */
  53. @Override
  54. public void setResourceLoader(ResourceLoader resourceLoader) {
  55. this.resourceLoader = resourceLoader;
  56. }
  57. }

可以看到这个类实现了ImportBeanDefinitionRegistrar这个接口,这就引出了spring的第一个扩展点,ImportBeanDefinitionRegistrar接口可以用来动态注册bean,它可以支持我们自己写的代码封装成BeanDefinition对象。在这里,mybatis作为一个第三方框架因为没有办法像第一方组件那样使用@Component或者@Service来表示这是一个需要注入的bean,所以只能扩展这个接口来动态的注入bean。

我们再来看registerBeanDefinitions这个方法,这个方法的含义就是注册bd(为了方便起见,以下beanDefinition都简称bd),我们看到方法最后调用了ClassPathMapperScanner的doScan()方法,我们看看ClassPathMapperScanner这个类


  
  1. public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
  2. ...
  3. }

这个类属于spring-mybatis包下,继承了spring的ClassPathBeanDefinitionScanner类


  
  1. @Override
  2. public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  3. //调用spring的doScan方法来扫描bd
  4. Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
  5. if (beanDefinitions.isEmpty()) {
  6. logger.warn( "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
  7. } else {
  8. //扫描完成后执行bd处理
  9. processBeanDefinitions(beanDefinitions);
  10. }
  11. return beanDefinitions;
  12. }

由于spring先执行componentScan,我把断点打在ClassPathBeanDefinitionScanner的doScan方法中的时候,首先扫描我自己的bean的时候也会进入这个断点,本次由于不分析componentScan,这一部分略过。跳过这个断点,此时控制台上输出


  
  1. Registering bean definition for @Bean method com.ww.config.MybatisConfig.dataSource()
  2. Registering bean definition for @Bean method com.ww.config.MybatisConfig.sqlSessionFactory()

表示我自己的bean已经注册完成了,接下来就应该进入mapper扫描了,我把断点打在MapperScannerRegistrar类中registerBeanDefinitions()方法的第一行,果然断点跳了进来,一路执行下去,当执行完ClassPathBeanDefinitionScanner的doScan的方法之后,控制台输出一句:

Identified candidate component class: file [E:\mycode\gitclone\mybatis-spring\target\classes\com\ww\mapper\UserMapper.class]

确认了候选的组件类,也就是说明spring已经扫描到mapper了,同时spring已经注册了bd,接下来再执行,则会进入processBeanDefinitions()方法


  
  1. //处理注册好的bd
  2. private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
  3. GenericBeanDefinition definition;
  4. for (BeanDefinitionHolder holder : beanDefinitions) {
  5. definition = (GenericBeanDefinition) holder.getBeanDefinition();
  6. if (logger.isDebugEnabled()) {
  7. logger.debug( "Creating MapperFactoryBean with name '" + holder.getBeanName()
  8. + "' and '" + definition.getBeanClassName() + "' mapperInterface");
  9. }
  10. // the mapper interface is the original class of the bean
  11. // but, the actual class of the bean is MapperFactoryBean
  12. definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
  13. //bd中加入mapperFactoryBean类,由此可见一个mapper对应的beanClass就是mapperFactoryBean,这是mybatis核心类之一,会详细说
  14. definition.setBeanClass( this.mapperFactoryBean.getClass());
  15. definition.getPropertyValues().add( "addToConfig", this.addToConfig);
  16. boolean explicitFactoryUsed = false;
  17. if (StringUtils.hasText( this.sqlSessionFactoryBeanName)) {
  18. definition.getPropertyValues().add( "sqlSessionFactory", new RuntimeBeanReference( this.sqlSessionFactoryBeanName));
  19. explicitFactoryUsed = true;
  20. } else if ( this.sqlSessionFactory != null) {
  21. definition.getPropertyValues().add( "sqlSessionFactory", this.sqlSessionFactory);
  22. explicitFactoryUsed = true;
  23. }
  24. if (StringUtils.hasText( this.sqlSessionTemplateBeanName)) {
  25. if (explicitFactoryUsed) {
  26. logger.warn( "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
  27. }
  28. definition.getPropertyValues().add( "sqlSessionTemplate", new RuntimeBeanReference( this.sqlSessionTemplateBeanName));
  29. explicitFactoryUsed = true;
  30. } else if ( this.sqlSessionTemplate != null) {
  31. if (explicitFactoryUsed) {
  32. logger.warn( "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
  33. }
  34. definition.getPropertyValues().add( "sqlSessionTemplate", this.sqlSessionTemplate);
  35. explicitFactoryUsed = true;
  36. }
  37. if (!explicitFactoryUsed) {
  38. if (logger.isDebugEnabled()) {
  39. logger.debug( "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
  40. }
  41. //自动注入类型为byType
  42. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  43. }
  44. }
  45. }

等这个方法处理完成之后,整个bd算是建立完了,接下来mybatis就开始初始化的过程了,首先我们来看之前被添加进bd中的MapperFactoryBean类


  
  1. public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
  2. private Class<T> mapperInterface;
  3. private boolean addToConfig = true;
  4. public MapperFactoryBean() {
  5. //intentionally empty
  6. }
  7. public MapperFactoryBean(Class<T> mapperInterface) {
  8. this.mapperInterface = mapperInterface;
  9. }
  10. /**
  11. * {@inheritDoc}
  12. */
  13. @Override
  14. protected void checkDaoConfig() {
  15. super.checkDaoConfig();
  16. notNull( this.mapperInterface, "Property 'mapperInterface' is required");
  17. Configuration configuration = getSqlSession().getConfiguration();
  18. if ( this.addToConfig && !configuration.hasMapper( this.mapperInterface)) {
  19. try {
  20. configuration.addMapper( this.mapperInterface);
  21. } catch (Exception e) {
  22. logger.error( "Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
  23. throw new IllegalArgumentException(e);
  24. } finally {
  25. ErrorContext.instance().reset();
  26. }
  27. }
  28. }
  29. /**
  30. * {@inheritDoc}
  31. */
  32. @Override
  33. public T getObject() throws Exception {
  34. return getSqlSession().getMapper( this.mapperInterface);
  35. }
  36. /**
  37. * {@inheritDoc}
  38. */
  39. @Override
  40. public Class<T> getObjectType() {
  41. return this.mapperInterface;
  42. }
  43. /**
  44. * {@inheritDoc}
  45. */
  46. @Override
  47. public boolean isSingleton() {
  48. return true;
  49. }
  50. //------------- mutators --------------
  51. /**
  52. * Sets the mapper interface of the MyBatis mapper
  53. *
  54. * @param mapperInterface class of the interface
  55. */
  56. public void setMapperInterface(Class<T> mapperInterface) {
  57. this.mapperInterface = mapperInterface;
  58. }
  59. /**
  60. * Return the mapper interface of the MyBatis mapper
  61. *
  62. * @return class of the interface
  63. */
  64. public Class<T> getMapperInterface() {
  65. return mapperInterface;
  66. }
  67. /**
  68. * If addToConfig is false the mapper will not be added to MyBatis. This means
  69. * it must have been included in mybatis-config.xml.
  70. * <p/>
  71. * If it is true, the mapper will be added to MyBatis in the case it is not already
  72. * registered.
  73. * <p/>
  74. * By default addToCofig is true.
  75. *
  76. * @param addToConfig
  77. */
  78. public void setAddToConfig(boolean addToConfig) {
  79. this.addToConfig = addToConfig;
  80. }
  81. /**
  82. * Return the flag for addition into MyBatis config.
  83. *
  84. * @return true if the mapper will be added to MyBatis in the case it is not already
  85. * registered.
  86. */
  87. public boolean isAddToConfig() {
  88. return addToConfig;
  89. }
  90. }

MapperFactoryBean扩展了Spring的FactoryBean接口,FactoryBean作为Spring的扩展点,FactoryBean的功能是可以让我们自定义Bean的创建过程


  
  1. //返回的对象实例
  2. T getObject() throws Exception;
  3. //Bean的类型
  4. Class<?> getObjectType();
  5. //true是单例,false是非单例 在Spring5.0中此方法利用了JDK1.8的新特性变成了default方法,返回true
  6. boolean isSingleton();

同时MapperFactory还继承了SqlSessionDaoSupport类,SqlSessionDaoSupport类又继承了Spring的DaoSupport类,利用了Spring中InitializingBean这个扩展点,在属性设置之后对bean进行操作,我们来看SqlSessionDaoSupport类和DaoSupport类都是做什么的


  
  1. public abstract class SqlSessionDaoSupport extends DaoSupport {
  2. private SqlSession sqlSession;
  3. private boolean externalSqlSession;
  4. //设置sqlSessionFactory,由于mapperScan最后执行了definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE),所以这个set方法会自动注入进spring容器中
  5. public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
  6. if (! this.externalSqlSession) {
  7. this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
  8. }
  9. }
  10. //设置sqlSessionTemplate,spring-mybatis的核心类之一,替代了普通mybatis中的DefaultSqlSession类,这个类控制sqlSession,包含一个内部类用来执行动态代理,同时sqlSessionTemplate类线程安全,可以在spring中作为单例bean使用
  11. public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
  12. this.sqlSession = sqlSessionTemplate;
  13. this.externalSqlSession = true;
  14. }
  15. /**
  16. * Users should use this method to get a SqlSession to call its statement methods
  17. * This is SqlSession is managed by spring. Users should not commit/rollback/close it
  18. * because it will be automatically done.
  19. *
  20. * @return Spring managed thread safe SqlSession
  21. */
  22. public SqlSession getSqlSession() {
  23. return this.sqlSession;
  24. }
  25. /**
  26. * {@inheritDoc}
  27. */
  28. @Override
  29. protected void checkDaoConfig() {
  30. notNull( this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
  31. }
  32. }
  33. //实现InitializingBean接口,会执行afterPropertiesSet()方法,在mybatis整合了spring后会执行MapperFactoryBean类中的checkDaoConfig()方法
  34. public abstract class DaoSupport implements InitializingBean {
  35. /** Logger available to subclasses */
  36. protected final Log logger = LogFactory.getLog(getClass());
  37. @Override
  38. public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
  39. // Let abstract subclasses check their configuration.
  40. checkDaoConfig();
  41. // Let concrete implementations initialize themselves.
  42. try {
  43. initDao();
  44. }
  45. catch (Exception ex) {
  46. throw new BeanInitializationException( "Initialization of DAO failed", ex);
  47. }
  48. }
  49. /**
  50. * Abstract subclasses must override this to check their configuration.
  51. * <p>Implementors should be marked as {@code final} if concrete subclasses
  52. * are not supposed to override this template method themselves.
  53. * @throws IllegalArgumentException in case of illegal configuration
  54. */
  55. protected abstract void checkDaoConfig() throws IllegalArgumentException;
  56. /**
  57. * Concrete subclasses can override this for custom initialization behavior.
  58. * Gets called after population of this instance's bean properties.
  59. * @throws Exception if DAO initialization fails
  60. * (will be rethrown as a BeanInitializationException)
  61. * @see org.springframework.beans.factory.BeanInitializationException
  62. */
  63. protected void initDao() throws Exception {
  64. }
  65. }

把断点打在MapperFactoryBean类中的checkDaoConfig()方法上,继续执行,我们看到执行了configuration.addMapper(this.mapperInterface),点进去,我们看到跳入了org.apache.ibatis.session.Configuration类中的addMapper方法


  
  1. //mapper注册器,添加和获取mapper的实际类
  2. protected final MapperRegistry mapperRegistry = new MapperRegistry( this);
  3. public <T> void addMapper(Class<T> type) {
  4. mapperRegistry.addMapper(type);
  5. }

 于是乎再进入一层


  
  1. //mapper被加入到MapperProxyFactory类中
  2. private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
  3. public <T> void addMapper(Class<T> type) {
  4. if (type.isInterface()) {
  5. if (hasMapper(type)) {
  6. throw new BindingException( "Type " + type + " is already known to the MapperRegistry.");
  7. }
  8. boolean loadCompleted = false;
  9. try {
  10. knownMappers.put(type, new MapperProxyFactory<T>(type));
  11. //加入到map中后立即解析mapper中的注解,目的是拿到mapper中的注解sql语句
  12. // It's important that the type is added before the parser is run
  13. // otherwise the binding may automatically be attempted by the
  14. // mapper parser. If the type is already known, it won't try.
  15. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
  16. parser.parse();
  17. loadCompleted = true;
  18. } finally {
  19. if (!loadCompleted) {
  20. knownMappers.remove(type);
  21. }
  22. }
  23. }
  24. }
  25. public class MapperProxyFactory<T> {
  26. private final Class<T> mapperInterface;
  27. private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
  28. public MapperProxyFactory(Class<T> mapperInterface) {
  29. this.mapperInterface = mapperInterface;
  30. }
  31. public Class<T> getMapperInterface() {
  32. return mapperInterface;
  33. }
  34. public Map<Method, MapperMethod> getMethodCache() {
  35. return methodCache;
  36. }
  37. //动态代理回调方法
  38. @SuppressWarnings( "unchecked")
  39. protected T newInstance(MapperProxy<T> mapperProxy) {
  40. return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  41. }
  42. //mapper代理类的实例化方法
  43. public T newInstance(SqlSession sqlSession) {
  44. final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
  45. return newInstance(mapperProxy);
  46. }
  47. }

继续往下执行,同时注意控制台的输出情况,看到控制台输出一句Finished creating instance of bean 'userMapper',此时mapper实例化完成,既然实例化完成了,那么就要返回实例化好的mapper,由于bd中放的beanClass是mapperFactoryBean,所以mapper实例要从mapperFactoryBean的getObject方法中来获得,来看代码


  
  1. //org.mybatis.spring.mapper.MapperFactoryBean#getObject
  2. @Override
  3. public T getObject() throws Exception {
  4. return getSqlSession().getMapper( this.mapperInterface);
  5. }
  6. //org.mybatis.spring.SqlSessionTemplate#getMapper
  7. //sqlSessionTemplate作为sqlSession
  8. @Override
  9. public <T> T getMapper(Class<T> type) {
  10. return getConfiguration().getMapper(type, this);
  11. }
  12. //org.apache.ibatis.session.Configuration#getMapper
  13. public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  14. return mapperRegistry.getMapper(type, sqlSession);
  15. }
  16. //org.apache.ibatis.binding.MapperRegistry#getMapper
  17. public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  18. final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  19. if (mapperProxyFactory == null) {
  20. throw new BindingException( "Type " + type + " is not known to the MapperRegistry.");
  21. }
  22. try {
  23. //之前说过,这里返回的是个代理的对象,也就是经过动态代理的mapper
  24. return mapperProxyFactory.newInstance(sqlSession);
  25. } catch (Exception e) {
  26. throw new BindingException( "Error getting mapper instance. Cause: " + e, e);
  27. }
  28. }

我们观察变量的变化,发现最终返回的object被MapperProxy所代理,到此为止,mapper已经被spring所管理,spring将通过动态代理技术使用代理类来执行mapper中的各种操作。

总结

经过自己动手用纯注解的方式整合spring和mybatis,我们看到了mybatis基于spring做的许多扩展,同时也看到了spring的很多扩展点,比如ImportBeanDefinitionRegistrar、InitializingBean、FactoryBean,下面总结一下mybatis整合spring过程中所用到的几个关键的类和这些类的基本功能

  • ClassPathMapperScanner:继承Spring的ClassPathBeanDefinitionScanner,作用是在classPath中扫描mapper以及扫描之后处理beanDefinition,mapperFactoryBean在这个类中的processBeanDefinitions方法中被加入beanDefinition
  • MapperFactoryBean:继承mybatis-spring的SqlSessionDaoSupport,实现了Spring的FactoryBean接口,作用是自定义bean,同时负责在mybatis的初始化结束之后添加mapper以及获取mapper的代理对象
  • MapperRegistry:mapper注册器,添加和获取mapper的实际类
  • MapperProxy:mapper代理类,调用invoke方法来执行代理方法代理实际mapper中的操作及缓存mapper中的method
  • MapperMethod:mapper方法类,包含目标方法对象和sql命令对象,主要作用是执行目标方法的sql语句
  • SqlSessionTemplate:mybatis整合spring的核心类,这个类控制sqlSession,包含一个内部类用来执行动态代理

转载:https://blog.csdn.net/wangjimmy1994/article/details/106771698
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场