• spring与mybatis整合(扫描Mapper接口)


    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"></property>
            <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property>
            <!-- 定义别名 -->
            <property name="typeAliasesPackage" value="com.booway.pojo"></property>
    
            <!-- 配置映射文件的位置,如果配置文件与mapper接口在同一个位置,可以不写 -->
            <!-- <property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"></property>  -->
             <!-- <property name="mapperLocations">
                <array>
                    <value>classpath:mybatis/mapper/User.xml</value>
                </array>
            </property> -->
            
        </bean>
    
        <!-- 将mybatis实现的接口注入到spring容器中 -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
            <property name="basePackage" value="com.booway.mapper"></property>
        </bean>
    
    
    看下这个类的实现MapperScannerConfigurer
    
    类申明为:
    public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware
    
    这个类实现了BeanDefinitionRegistryPostProcessor,InitializingBean,ApplicationContextAware,BeanNameAware接口
    
    实现了BeanDefinitionRegistryPostProcessor接口意味着在beanDefinition注册之后就会调用这个接口的postProcessBeanDefinitionRegistry方法。实现了InitializingBean接口意味在bean创建初始化时会调用afterPropertiesSet方法。ApplicationContextAware意味着会注入application实例,BeanNameAware意味着会注入这个bean当前的beanName
    
    
    这几个接口使用的顺序为,BeanDefinitionRegistryPostProcessor,BeanNameAware,ApplicationContextAware,InitializingBean
    
    
    1、先来看BeanDefinitionRegistryPostProcessor接口的实现方法
    
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {//处理站位符
          processPropertyPlaceHolders();
        }
    
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        scanner.setAddToConfig(this.addToConfig);
        scanner.setAnnotationClass(this.annotationClass);
        scanner.setMarkerInterface(this.markerInterface);
        scanner.setSqlSessionFactory(this.sqlSessionFactory);
        scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
        scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
        scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
        scanner.setResourceLoader(this.applicationContext);
        scanner.setBeanNameGenerator(this.nameGenerator);
    //注册过滤器,比如一些排除过滤器,注解过滤器,标记过滤器,定义一些获取类的过滤条件
        scanner.registerFilters();
    //扫描指定包下的类,将com.test.a,com.test,b拆分为数组
        scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
      }
    
    
    public void registerFilters() {
        boolean acceptAllInterfaces = true;//默认获取所有的接口作为mybatis的mapper接口
    
        // if specified, use the given annotation and / or marker interface
        if (this.annotationClass != null) {
    //如果指定了标记的注解类型,那么就使用这个注解来标记mybatis的mapper接口
          addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
          acceptAllInterfaces = false;
        }
    
        // override AssignableTypeFilter to ignore matches on the actual marker interface
    //通过标记的接口来识别是不是mapper接口,只要继承了指定接口的就是,不过此处的逻辑认为继承了这个接口的就返回false,也就是不是mybatis的mapper接口
        if (this.markerInterface != null) {
          addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
            @Override
            protected boolean matchClassName(String className) {
              return false;
            }
          });
          acceptAllInterfaces = false;
        }
    //如果acceptAllInterfaces为true,也就是没有指定任何筛选逻辑,那么认为任何接口都返回true
        if (acceptAllInterfaces) {
          // default include filter that accepts all classes
          addIncludeFilter(new TypeFilter() {
            @Override
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
              return true;
            }
          });
        }
    //排除package-info.java
        // exclude package-info.java
        addExcludeFilter(new TypeFilter() {
          @Override
          public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
            String className = metadataReader.getClassMetadata().getClassName();
            return className.endsWith("package-info");
          }
        });
      }
    
    
    
    public int scan(String... basePackages) {
    //获取已经注册bean的个数
            int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    //扫描
            doScan(basePackages);
    
            // Register annotation config processors, if necessary.如果包含注解配置,那么就注册注解配置处理器。
            if (this.includeAnnotationConfig) {
                AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
            }
    //新增加的beandefinitoinCount就是mybatis扫描添加的。
            return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
        }
    
     @Override
      public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    
        if (beanDefinitions.isEmpty()) {
          logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
    //处理获取到的beanDefinition
          processBeanDefinitions(beanDefinitions);
        }
    
        return beanDefinitions;
      }
    
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
            Assert.notEmpty(basePackages, "At least one base package must be specified");
            Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
            for (String basePackage : basePackages) {
    //获取候选组件
                Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
                for (BeanDefinition candidate : candidates) {
    //获取范围元数据,通过类获取这个类的scope范围还有代理模式
                    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
    //将解析出来的scope设置到beanDefinition中
                    candidate.setScope(scopeMetadata.getScopeName());
    //使用命名生成器生成beanName
                    String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                    if (candidate instanceof AbstractBeanDefinition) {
    //给这个beanDefinition设置默认的属性,比如默认的lazy属性啊,等等
                        postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
                    }
                    if (candidate instanceof AnnotatedBeanDefinition) {
                    //设置注解配置的一些属性    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                    }
    //检查候选beanDefinition类,以确定是否可以要进行注册,防止重复注册
                    if (checkCandidate(beanName, candidate)) {
                        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                        beanDefinitions.add(definitionHolder);
                        registerBeanDefinition(definitionHolder, this.registry);
                    }
                }
            }
            return beanDefinitions;
        }
    
    
    
    
    
    public Set<BeanDefinition> findCandidateComponents(String basePackage) {
            Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
            try {
    //classpath*:com/test/a/**/*.class
                String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                        resolveBasePackage(basePackage) + "/" + this.resourcePattern;
                Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);//通过spring的PathMatchingResourcePatternResolver,获取到所有符合条件的class资源
                boolean traceEnabled = logger.isTraceEnabled();
                boolean debugEnabled = logger.isDebugEnabled();
                for (Resource resource : resources) {
                    if (traceEnabled) {
                        logger.trace("Scanning " + resource);
                    }
                    if (resource.isReadable()) {//如果是这个资源是可读的
                        try {
                            MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);//将类包装成元数据读取器,底层使用的ASM框架。
                            if (isCandidateComponent(metadataReader)) {//通过过滤器配置这个类是否是候选组件
                                ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);//这个类继承自GenericBeanDefinition
                                sbd.setResource(resource);
                                sbd.setSource(resource);
    //判断这个类是不是独立的,是不是具体的(即不是抽象或接口,可以被继承)
                                if (isCandidateComponent(sbd)) {
                                    if (debugEnabled) {
                                        logger.debug("Identified candidate component class: " + resource);
                                    }
                                    candidates.add(sbd);
                                }
                                else {
                                    if (debugEnabled) {
                                        logger.debug("Ignored because not a concrete top-level class: " + resource);
                                    }
                                }
                            }
                            else {
                                if (traceEnabled) {
                                    logger.trace("Ignored because not matching any filter: " + resource);
                                }
                            }
                        }
                        catch (Throwable ex) {
                            throw new BeanDefinitionStoreException(
                                    "Failed to read candidate component class: " + resource, ex);
                        }
                    }
                    else {
                        if (traceEnabled) {
                            logger.trace("Ignored because not readable: " + resource);
                        }
                    }
                }
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
            }
            return candidates;
        }
    
    
    
    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    //判断当前类是否是排除的,如果是被排除,那么就不是候选的mapper
    接口
            for (TypeFilter tf : this.excludeFilters) {
                if (tf.match(metadataReader, this.metadataReaderFactory)) {
                    return false;
                }
            }
    //判断当前类是否被包含
            for (TypeFilter tf : this.includeFilters) {
                if (tf.match(metadataReader, this.metadataReaderFactory)) {
                    return isConditionMatch(metadataReader);
                }
            }
            return false;
        }
    
    
    @Override
        public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
            ScopeMetadata metadata = new ScopeMetadata();
            if (definition instanceof AnnotatedBeanDefinition) {
                AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
                AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(annDef.getMetadata(), this.scopeAnnotationType);
                if (attributes != null) {
    //获取Scope注解的value属性值
                    metadata.setScopeName(attributes.getAliasedString("value", this.scopeAnnotationType, definition.getSource()));
    //获取proxyMode属性指定的代理模式
                    ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
                    if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {
                        proxyMode = this.defaultProxyMode;
                    }
                    metadata.setScopedProxyMode(proxyMode);
                }
            }
            return metadata;
        }
    
    
    这样就将定义的类变成了beanDefinition注册到spring中
    
    
    
    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        GenericBeanDefinition definition;
        for (BeanDefinitionHolder holder : beanDefinitions) {
          definition = (GenericBeanDefinition) holder.getBeanDefinition();
    
          if (logger.isDebugEnabled()) {
            logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
              + "' and '" + definition.getBeanClassName() + "' mapperInterface");
          }
    
          // the mapper interface is the original class of the bean
          // but, the actual class of the bean is MapperFactoryBean
      //给这个 beanDefinition定义构造参数为自己,因为下面会定义FactoryBean
    definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
    //设置工厂bean
          definition.setBeanClass(this.mapperFactoryBean.getClass());
    //设置addToConfig属性
          definition.getPropertyValues().add("addToConfig", this.addToConfig);
    //使用指定的工厂
          boolean explicitFactoryUsed = false;
          if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {//如果指定了sqlSessionFactory,表示配置文件中已经配置了对应的beanName
            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 = true;
          }
    //如果没有指定明确的工厂bean使用,那么就设置转配模式为按类型进行装配,让spring自己到容器中找到对应bean进行装配。
          if (!explicitFactoryUsed) {
            if (logger.isDebugEnabled()) {
              logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
            }
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
          }
        }
      }
    
    
    那么问题来了,既然最后把所有的类的都变为了MapperFactoryBean来创造,那么这个MapperFactoryBean是怎么构造这些接口的实现类的呢?
    
    MapperFactoryBean实现了FactoryBean接口
    
    
    
    
    //MapperFactoryBean的类定义
    public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    
      private Class<T> mapperInterface;
    
      private boolean addToConfig = true;
    
      public MapperFactoryBean() {
        //intentionally empty 
      }
      //这个构造参数在上面的分析中已经看到,它把对应Mapper接口设置进来了
      public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      protected void checkDaoConfig() {
        super.checkDaoConfig();
    
        notNull(this.mapperInterface, "Property 'mapperInterface' is required");
    
        Configuration configuration = getSqlSession().getConfiguration();
        if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
          try {
            configuration.addMapper(this.mapperInterface);
          } catch (Exception e) {
            logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
            throw new IllegalArgumentException(e);
          } finally {
            ErrorContext.instance().reset();
          }
        }
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public Class<T> getObjectType() {
        return this.mapperInterface;
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public boolean isSingleton() {
        return true;
      }
    
      //------------- mutators --------------
    
      /**
       * Sets the mapper interface of the MyBatis mapper
       *
       * @param mapperInterface class of the interface
       */
      public void setMapperInterface(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }
    
      /**
       * Return the mapper interface of the MyBatis mapper
       *
       * @return class of the interface
       */
      public Class<T> getMapperInterface() {
        return mapperInterface;
      }
    
      /**
       * If addToConfig is false the mapper will not be added to MyBatis. This means
       * it must have been included in mybatis-config.xml.
       * <p/>
       * If it is true, the mapper will be added to MyBatis in the case it is not already
       * registered.
       * <p/>
       * By default addToCofig is true.
       *
       * @param addToConfig
       */
      public void setAddToConfig(boolean addToConfig) {
        this.addToConfig = addToConfig;
      }
    
      /**
       * Return the flag for addition into MyBatis config.
       *
       * @return true if the mapper will be added to MyBatis in the case it is not already
       * registered.
       */
      public boolean isAddToConfig() {
        return addToConfig;
      }
    }
    
    
    //底层就是使用的mybatis获取接口实现的方法。
    public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
      }
  • 相关阅读:
    一篇就搞懂Mysql存储引擎和索引的文章
    ShardedJedisPipeline中sync()和syncAndReturnAll()区别
    17.win10安装Nginx及负载均衡配置,实现代理访问内网机器
    iDempiere 使用指南 系统安装 以及 virtualbox虚拟机下载
    程序员学数学【整理】
    element 表单校验
    draggable 拖拽列表排序(指定被拖拽的子元素)
    导出多个表的excel文件
    js自定义鼠标的图片
    table 导出简单的excel
  • 原文地址:https://www.cnblogs.com/honger/p/9519300.html
Copyright © 2020-2023  润新知