Spring-Mybatis源码分析

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

首先给出Spring-mybatis的配置文件

        <!-- 配置数据源 -->
        <bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource"
            destroy-method="close">
            <property name="driverClass" value="${DriverClasses}" />
            <property name="jdbcUrl" value="${ecology.url}" />
            <property name="username" value="${ecology.user}" />
            <property name="password" value="${ecology.password}" />
            <property name="idleConnectionTestPeriod" value="60" />
            <property name="idleMaxAge" value="240" />
            <property name="maxConnectionsPerPartition" value="30" />
            <property name="minConnectionsPerPartition" value="10" />
            <property name="partitionCount" value="1" />
            <property name="acquireIncrement" value="5" />
            <property name="statementsCacheSize" value="100" />
            <property name="releaseHelperThreads" value="3" />
        </bean>
        <!-- 配置数据工厂 -->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!-- 自动扫描mapper.xml文件 -->
            <property name="configLocation" value="classpath:cn/resources/mybatis-cfg.xml" />
            <property name="mapperLocations" value="classpath:cn/resources/mapper/*Mapper.xml" />
            <property name="dataSource" ref="dataSource" />
        </bean>
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
            <property name="basePackage" value="cn.belle.mapper"></property>
        </bean>

mybatis-cfg.xml

    <configuration>
        <settings>
            <!-- 启用自动将数据库字段和pojo中的字段做驼峰式的匹配 -->
            <setting name="mapUnderscoreToCamelCase" value="true"/>
            <!-- 懒加载模式 -->
            <setting name="lazyLoadingEnabled" value="false" />
            <!-- JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER -->
            <setting name="jdbcTypeForNull" value="NULL" />
            <setting name="defaultStatementTimeout" value="30" />
        </settings>
    </configuration>

核心类是MapperScannerConfigurer

    public class MapperScannerConfigurer  implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware

这个类实现了BeanDefinitionRegistryPostProcessor这个抽象接口,让Spring容器在加载时执行postProcessBeanDefinitionRegistry方法

     1 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
     2     if (this.processPropertyPlaceHolders) {
     3       processPropertyPlaceHolders();
     4     }
     5 
     6     ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
     7     scanner.setAddToConfig(this.addToConfig);
     8     scanner.setAnnotationClass(this.annotationClass);
     9     scanner.setMarkerInterface(this.markerInterface);
    10     scanner.setSqlSessionFactory(this.sqlSessionFactory);
    11     scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    12     scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
    13     scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    14     scanner.setResourceLoader(this.applicationContext);
    15     scanner.setBeanNameGenerator(this.nameGenerator);
    16     scanner.registerFilters();
    17     scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
    18   }

可以看到 scanner设置了大量属性,并且最后执行了scan方法

                 public int scan(String[] basePackages)
    /*     */   {
    /* 224 */     int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    /*     */     
    /* 226 */     doScan(basePackages);
    /*     */     
    /*     */ 
    /* 229 */     if (this.includeAnnotationConfig) {
    /* 230 */       AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    /*     */     }
    /*     */     
    /* 233 */     return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
    /*     */   }

doScan(注意这里不要搞错了,之前我就搞错了,这里的scan应该是子类的sacn)

       public Set<BeanDefinitionHolder> doScan(String[] basePackages)
    /*     */   {
    /* 164 */     Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    /*     */     
    /* 166 */     if (beanDefinitions.isEmpty()) {
    /* 167 */       this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    /*     */     } else {
    /* 169 */       processBeanDefinitions(beanDefinitions);
    /*     */     }
    /*     */     
    /* 172 */     return beanDefinitions;
    /*     */   }

首先调用了父类的doScan

    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

父类的doScan

    protected Set<BeanDefinitionHolder> doScan(String... basePackages)
    /*     */   {
    /* 245 */     Assert.notEmpty(basePackages, "At least one base package must be specified");
    /* 246 */     Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet();
    /* 247 */     for (String basePackage : basePackages) {
    /* 248 */       Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
    /* 249 */       for (BeanDefinition candidate : candidates) {
    /* 250 */         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
    /* 251 */         candidate.setScope(scopeMetadata.getScopeName());
    /* 252 */         String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
    /* 253 */         if ((candidate instanceof AbstractBeanDefinition)) {
    /* 254 */           postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName);
    /*     */         }
    /* 256 */         if ((candidate instanceof AnnotatedBeanDefinition)) {
    /* 257 */           AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate);
    /*     */         }
    /* 259 */         if (checkCandidate(beanName, candidate)) {
    /* 260 */           BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
    /* 261 */           definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    /* 262 */           beanDefinitions.add(definitionHolder);
    /* 263 */           registerBeanDefinition(definitionHolder, this.registry);
    /*     */         }
    /*     */       }
    /*     */     }
    /* 267 */     return beanDefinitions;
    /*     */   }

该方法主要做了以下操作: 1)扫描basePackage下面的java文件 2)解析扫描到的java文件 3)调用各个在上一步骤注册的过滤器,执行相应的方法。 4)为解析后的java注册bean,注册方式采用编码的动态注册实现。 5)构造MapperFactoryBean的属性,mapperInterface,sqlSessionFactory等等,填充到BeanDefinition里面去。 做完这些,MapperFactoryBean对象也就构造完成了,扫描方式添加dao的工作也完成了。

然后是processBeanDefinitions

      private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions)
    /*     */   {
    /* 177 */     for (BeanDefinitionHolder holder : beanDefinitions) {
    /* 178 */       GenericBeanDefinition definition = (GenericBeanDefinition)holder.getBeanDefinition();
    /*     */       
    /* 180 */       if (this.logger.isDebugEnabled()) {
    /* 181 */         this.logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition
    /* 182 */           .getBeanClassName() + "' mapperInterface");
    /*     */       }
    /*     */       
    /*     */ 
    /*     */ 
    /* 187 */       definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
    /* 188 */       definition.setBeanClass(this.mapperFactoryBean.getClass());
    /*     */       
    /* 190 */       definition.getPropertyValues().add("addToConfig", Boolean.valueOf(this.addToConfig));
    /*     */       
    /* 192 */       boolean explicitFactoryUsed = false;
    /* 193 */       if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
    /* 194 */         definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
    /* 195 */         explicitFactoryUsed = true;
    /* 196 */       } else if (this.sqlSessionFactory != null) {
    /* 197 */         definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
    /* 198 */         explicitFactoryUsed = true;
    /*     */       }
    /*     */       
    /* 201 */       if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
    /* 202 */         if (explicitFactoryUsed) {
    /* 203 */           this.logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
    /*     */         }
    /* 205 */         definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
    /* 206 */         explicitFactoryUsed = true;
    /* 207 */       } else if (this.sqlSessionTemplate != null) {
    /* 208 */         if (explicitFactoryUsed) {
    /* 209 */           this.logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
    /*     */         }
    /* 211 */         definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
    /* 212 */         explicitFactoryUsed = true;
    /*     */       }
    /*     */       
    /* 215 */       if (!explicitFactoryUsed) {
    /* 216 */         if (this.logger.isDebugEnabled()) {
    /* 217 */           this.logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
    /*     */         }
    /* 219 */         definition.setAutowireMode(2);
    /*     */       }
    /*     */     }
    /*     */   }

最关键的两行

    definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
    definition.setBeanClass(this.mapperFactoryBean.getClass());

到这里动态注册的过程已经实现,dao层的接口已经全部被改造成MapperFactoryBean

所以最终我们还是要分析MapperFactoryBean的实现原理!

MapperFactoryBean继承了SqlSessionDaoSupport类,SqlSessionDaoSupport类继承DaoSupport抽象类,DaoSupport抽象类实现了InitializingBean接口,因此实例个MapperFactoryBean的时候,都会调用InitializingBean接口的afterPropertiesSet方法。

               public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException
    /*    */   {
    /* 44 */     checkDaoConfig();
    /*    */     
    /*    */     try
    /*    */     {
    /* 48 */       initDao();
    /*    */     }
    /*    */     catch (Exception ex) {
    /* 51 */       throw new BeanInitializationException("Initialization of DAO failed", ex);
    /*    */     }
    /*    */   }

MapperFactoryBean重写了checkDaoConfig方法

    protected void checkDaoConfig()
    /*     */   {
    /*  74 */     super.checkDaoConfig();
    /*     */     
    /*  76 */     Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
    /*     */     
    /*  78 */     Configuration configuration = getSqlSession().getConfiguration();
    /*  79 */     if ((this.addToConfig) && (!configuration.hasMapper(this.mapperInterface))) {
    /*     */       try {
    /*  81 */         configuration.addMapper(this.mapperInterface);
    /*     */       } catch (Exception e) {
    /*  83 */         this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
    /*  84 */         throw new IllegalArgumentException(e);
    /*     */       } finally {
    /*  86 */         ErrorContext.instance().reset();
    /*     */       }
    /*     */     }
    /*     */   }

然后通过spring工厂拿对应的bean的时候:

     public T getObject()
    /*     */     throws Exception
    /*     */   {
    /*  96 */     return getSqlSession().getMapper(this.mapperInterface);
    /*     */   }

这里的SqlSession是SqlSessionTemplate,SqlSessionTemplate的getMapper方法:

    public <T> T getMapper(Class<T> type)
    /*     */   {
    /* 319 */     return getConfiguration().getMapper(type, this);
    /*     */   }

Configuration的getMapper方法,会使用MapperRegistry的getMapper方法:

     public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    /* 717 */     return this.mapperRegistry.getMapper(type, sqlSession);
    /*     */   }

getMapper

        */   public <T> T getMapper(Class<T> type, SqlSession sqlSession)
    /*     */   {
    /*  45 */     MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
    /*  46 */     if (mapperProxyFactory == null) {
    /*  47 */       throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    /*     */     }
    /*     */     try {
    /*  50 */       return mapperProxyFactory.newInstance(sqlSession);
    /*     */     } catch (Exception e) {
    /*  52 */       throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    /*     */     }
    /*     */   }

newInstance

    protected T newInstance(MapperProxy<T> mapperProxy)
    /*    */   {
    /* 47 */     return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[] { this.mapperInterface }, mapperProxy);
    /*    */   }
    /*    */   

这里就出现了动态代理

mapperProxy的源码

    public class MapperProxy<T>
    /*    */   implements InvocationHandler, Serializable

它的invoke

      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    /*    */   {
    /* 45 */     if (Object.class.equals(method.getDeclaringClass())) {
    /*    */       try {
    /* 47 */         return method.invoke(this, args);
    /*    */       } catch (Throwable t) {
    /* 49 */         throw ExceptionUtil.unwrapThrowable(t);
    /*    */       }
    /*    */     }
    /* 52 */     MapperMethod mapperMethod = cachedMapperMethod(method);
    /* 53 */     return mapperMethod.execute(this.sqlSession, args);
    /*    */   }

这两行相信大家已经很熟悉了

      MapperMethod mapperMethod = cachedMapperMethod(method);
    /* 53 */     return mapperMethod.execute(this.sqlSession, args);

来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring-Mybatis源码分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏