美文网首页spring
spring-mybatis分析

spring-mybatis分析

作者: loveFXX | 来源:发表于2019-10-25 14:29 被阅读0次

    spring整合mybatis过程分析

    version: '2.0.2'之前

    实现原理:执行内置的BeanDefinitionRegistryPostProcessor在完成扫描后,处理扫描出来的beandrfinition当中的import的时候,实例化这个import对象,调用import进来的对象的方法

    version: '2.0.2'及之后使用:

    实现原理:spring执行内置的BeanDefinitionRegistryPostProcessor时候,注册了一个mybatis自己提供的BeanDefinitionRegistryPostProcessor实现类,接着执行mybatis提供的BeanDefinitionRegistryPostProcessor的方法,实例化一个扫描器。mybatis是BeanDefinitionRegistryPostProcessor的扩展点
    依据依赖包

    compile group: 'org.mybatis', name: 'mybatis-spring', version: '2.0.3'
    

    @MapperScan可以不写,只要把MapperScannerConfigurer对象按下面方式或注解给spring容器
    官方注释文档:

     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="org.mybatis.spring.sample.mapper" />
            <!-- optional unless there are multiple session factories defined -->
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
     </bean>
    

    @Bean注解形式MapperScannerConfigurer类替代@MapperScan

    package com.dao;
    public interface UserDao {
        @Select( "select name from users where id =2" )
        public String query();
    }
    
    package com;
    @Configuration
    @ComponentScan("com.service")
    //@MapperScan("com.dao")
    public class AppConfig {
    
        @Bean
        public DataSource dataSource() {
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName( "com.mysql.jdbc.Driver" );
            dataSource.setUrl( "jdbc:mysql://127.0.0.1:3306/eshop" );
            dataSource.setUsername( "root" );
            dataSource.setPassword( "root" );
            return dataSource;
        }
    
        @Bean
        public SqlSessionFactory sqlSessionFactory() throws Exception {
            SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
            factoryBean.setDataSource(dataSource()  );
            return factoryBean.getObject();
        }
    
        @Bean
        public SqlSessionTemplate sqlSessionTemplate() throws Exception{
            SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory());
            return sqlSessionTemplate;
        }
    
        @Bean
        public MapperScannerConfigurer getMapperScannerConfigurer() throws Exception{
            MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
            mapperScannerConfigurer.setBasePackage( "com.dao" );
            mapperScannerConfigurer.setSqlSessionTemplateBeanName( "sqlSessionTemplate" );
            return mapperScannerConfigurer;
        }
    
          public static void main(String[] args) {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext( AppConfig.class );
            UserDao userDao = ac.getBean(UserDao.class );
            System.out.println( userDao.query() );
        }
    }
    

    源码分析

    执行时机是在spring执行内置的BeanDefinitionRegistryPostProcessor的方法时,会进行解析扫描
    得到配置类上MapperScan注解的值,并且创建MapperScannerConfigurer类型的BeanDefinition


    MapperScan_val.png

    在执行MapperScannerConfigurer的postProcessBeanDefinitionRegistry方法时,就会进入ClassPathBeanDefinitionScanner#scan方法,最后注册basePackages路径下的类成为BeanDefinition

    参考类

    MapperScan注解类,

    package org.mybatis.spring.annotation;
    /**
     * Use this annotation to register MyBatis mapper interfaces when using Java Config. It performs when same work as
     * {@link MapperScannerConfigurer} via {@link MapperScannerRegistrar}.
     *
     * <p>
     * Configuration example:
     * </p>
     * 
     * <pre class="code">
     * &#064;Configuration
     * &#064;MapperScan("org.mybatis.spring.sample.mapper")
     * public class AppConfig {
     *
     *   &#064;Bean
     *   public DataSource dataSource() {
     *     return new EmbeddedDatabaseBuilder().addScript("schema.sql").build();
     *   }
     *
     *   &#064;Bean
     *   public DataSourceTransactionManager transactionManager() {
     *     return new DataSourceTransactionManager(dataSource());
     *   }
     *
     *   &#064;Bean
     *   public SqlSessionFactory sqlSessionFactory() throws Exception {
     *     SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
     *     sessionFactory.setDataSource(dataSource());
     *     return sessionFactory.getObject();
     *   }
     * }
     * </pre>
     *
     * @author Michael Lanyon
     * @author Eduardo Macarron
     *
     * @since 1.2.0
     * @see MapperScannerRegistrar
     * @see MapperFactoryBean
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(MapperScannerRegistrar.class)
    @Repeatable(MapperScans.class)
    public @interface MapperScan {
    
      /**
       * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation declarations e.g.:
       * {@code @MapperScan("org.my.pkg")} instead of {@code @MapperScan(basePackages = "org.my.pkg"})}.
       *
       * @return base package names
       */
      String[] value() default {};
    
      /**
       * Base packages to scan for MyBatis interfaces. Note that only interfaces with at least one method will be
       * registered; concrete classes will be ignored.
       *
       * @return base package names for scanning mapper interface
       */
      String[] basePackages() default {};
    
      /**
       * Type-safe alternative to {@link #basePackages()} for specifying the packages to scan for annotated components. The
       * package of each class specified will be scanned.
       * <p>
       * Consider creating a special no-op marker class or interface in each package that serves no purpose other than being
       * referenced by this attribute.
       *
       * @return classes that indicate base package for scanning mapper interface
       */
      Class<?>[] basePackageClasses() default {};
    
      /**
       * The {@link BeanNameGenerator} class to be used for naming detected components within the Spring container.
       *
       * @return the class of {@link BeanNameGenerator}
       */
      Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
    
      /**
       * This property specifies the annotation that the scanner will search for.
       * <p>
       * The scanner will register all interfaces in the base package that also have the specified annotation.
       * <p>
       * Note this can be combined with markerInterface.
       *
       * @return the annotation that the scanner will search for
       */
      Class<? extends Annotation> annotationClass() default Annotation.class;
    
      /**
       * This property specifies the parent that the scanner will search for.
       * <p>
       * The scanner will register all interfaces in the base package that also have the specified interface class as a
       * parent.
       * <p>
       * Note this can be combined with annotationClass.
       *
       * @return the parent that the scanner will search for
       */
      Class<?> markerInterface() default Class.class;
    
      /**
       * Specifies which {@code SqlSessionTemplate} to use in the case that there is more than one in the spring context.
       * Usually this is only needed when you have more than one datasource.
       *
       * @return the bean name of {@code SqlSessionTemplate}
       */
      String sqlSessionTemplateRef() default "";
    
      /**
       * Specifies which {@code SqlSessionFactory} to use in the case that there is more than one in the spring context.
       * Usually this is only needed when you have more than one datasource.
       *
       * @return the bean name of {@code SqlSessionFactory}
       */
      String sqlSessionFactoryRef() default "";
    
      /**
       * Specifies a custom MapperFactoryBean to return a mybatis proxy as spring bean.
       *
       * @return the class of {@code MapperFactoryBean}
       */
      Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
    
      /**
       * Whether enable lazy initialization of mapper bean.
       *
       * <p>
       * Default is {@code false}.
       * </p>
       * 
       * @return set {@code true} to enable lazy initialization
       * @since 2.0.2
       */
      String lazyInitialization() default "";
    
    }
    

    MapperScannerRegistrar 导入的类

    package org.mybatis.spring.annotation;
    /**
     * A {@link ImportBeanDefinitionRegistrar} to allow annotation configuration of MyBatis mapper scanning. Using
     * an @Enable annotation allows beans to be registered via @Component configuration, whereas implementing
     * {@code BeanDefinitionRegistryPostProcessor} will work for XML configuration.
     *
     * @author Michael Lanyon
     * @author Eduardo Macarron
     * @author Putthiphong Boonphong
     *
     * @see MapperFactoryBean
     * @see ClassPathMapperScanner
     * @since 1.2.0
     */
    public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    
      /**
       * {@inheritDoc}
       * 
       * @deprecated Since 2.0.2, this method not used never.
       */
      @Override
      @Deprecated
      public void setResourceLoader(ResourceLoader resourceLoader) {
        // NOP
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AnnotationAttributes mapperScanAttrs = AnnotationAttributes
            .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        if (mapperScanAttrs != null) {
          registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
        }
      }
    
      void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
    
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
        builder.addPropertyValue("processPropertyPlaceHolders", true);
    
        Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
        if (!Annotation.class.equals(annotationClass)) {
          builder.addPropertyValue("annotationClass", annotationClass);
        }
    
        Class<?> markerInterface = annoAttrs.getClass("markerInterface");
        if (!Class.class.equals(markerInterface)) {
          builder.addPropertyValue("markerInterface", markerInterface);
        }
    
        Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
        if (!BeanNameGenerator.class.equals(generatorClass)) {
          builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
        }
    
        Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
        if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
          builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
        }
    
        String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
        if (StringUtils.hasText(sqlSessionTemplateRef)) {
          builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
        }
    
        String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
        if (StringUtils.hasText(sqlSessionFactoryRef)) {
          builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
        }
    
        List<String> basePackages = new ArrayList<>();
        basePackages.addAll(
            Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
    
        basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
            .collect(Collectors.toList()));
    
        basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
            .collect(Collectors.toList()));
    
        String lazyInitialization = annoAttrs.getString("lazyInitialization");
        if (StringUtils.hasText(lazyInitialization)) {
          builder.addPropertyValue("lazyInitialization", lazyInitialization);
        }
    
        builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
    
        registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
    
      }
    
      private static String generateBaseBeanName(AnnotationMetadata importingClassMetadata, int index) {
        return importingClassMetadata.getClassName() + "#" + MapperScannerRegistrar.class.getSimpleName() + "#" + index;
      }
    
      /**
       * A {@link MapperScannerRegistrar} for {@link MapperScans}.
       * 
       * @since 2.0.0
       */
      static class RepeatingRegistrar extends MapperScannerRegistrar {
        /**
         * {@inheritDoc}
         */
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
          AnnotationAttributes mapperScansAttrs = AnnotationAttributes
              .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScans.class.getName()));
          if (mapperScansAttrs != null) {
            AnnotationAttributes[] annotations = mapperScansAttrs.getAnnotationArray("value");
            for (int i = 0; i < annotations.length; i++) {
              registerBeanDefinitions(annotations[i], registry, generateBaseBeanName(importingClassMetadata, i));
            }
          }
        }
      }
    
    }
    

    MapperScannerConfigurer类

    package org.mybatis.spring.mapper;
    
    /**
     * BeanDefinitionRegistryPostProcessor that searches recursively starting from a base package for interfaces and
     * registers them as {@code MapperFactoryBean}. Note that only interfaces with at least one method will be registered;
     * concrete classes will be ignored.
     * <p>
     * This class was a {code BeanFactoryPostProcessor} until 1.0.1 version. It changed to
     * {@code BeanDefinitionRegistryPostProcessor} in 1.0.2. See https://jira.springsource.org/browse/SPR-8269 for the
     * details.
     * <p>
     * The {@code basePackage} property can contain more than one package name, separated by either commas or semicolons.
     * <p>
     * This class supports filtering the mappers created by either specifying a marker interface or an annotation. The
     * {@code annotationClass} property specifies an annotation to search for. The {@code markerInterface} property
     * specifies a parent interface to search for. If both properties are specified, mappers are added for interfaces that
     * match <em>either</em> criteria. By default, these two properties are null, so all interfaces in the given
     * {@code basePackage} are added as mappers.
     * <p>
     * This configurer enables autowire for all the beans that it creates so that they are automatically autowired with the
     * proper {@code SqlSessionFactory} or {@code SqlSessionTemplate}. If there is more than one {@code SqlSessionFactory}
     * in the application, however, autowiring cannot be used. In this case you must explicitly specify either an
     * {@code SqlSessionFactory} or an {@code SqlSessionTemplate} to use via the <em>bean name</em> properties. Bean names
     * are used rather than actual objects because Spring does not initialize property placeholders until after this class
     * is processed.
     * <p>
     * Passing in an actual object which may require placeholders (i.e. DB user password) will fail. Using bean names defers
     * actual object creation until later in the startup process, after all placeholder substitution is completed. However,
     * note that this configurer does support property placeholders of its <em>own</em> properties. The
     * <code>basePackage</code> and bean name properties all support <code>${property}</code> style substitution.
     * <p>
     * Configuration sample:
     *
     * <pre class="code">
     * {@code
     *   <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
     *       <property name="basePackage" value="org.mybatis.spring.sample.mapper" />
     *       <!-- optional unless there are multiple session factories defined -->
     *       <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
     *   </bean>
     * }
     * </pre>
     *
     * @author Hunter Presnall
     * @author Eduardo Macarron
     *
     * @see MapperFactoryBean
     * @see ClassPathMapperScanner
     */
    public class MapperScannerConfigurer
        implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
    
      private String basePackage;
    
      private boolean addToConfig = true;
    
      private String lazyInitialization;
    
      private SqlSessionFactory sqlSessionFactory;
    
      private SqlSessionTemplate sqlSessionTemplate;
    
      private String sqlSessionFactoryBeanName;
    
      private String sqlSessionTemplateBeanName;
    
      private Class<? extends Annotation> annotationClass;
    
      private Class<?> markerInterface;
    
      private Class<? extends MapperFactoryBean> mapperFactoryBeanClass;
    
      private ApplicationContext applicationContext;
    
      private String beanName;
    
      private boolean processPropertyPlaceHolders;
    
      private BeanNameGenerator nameGenerator;
    
      /**
       * This property lets you set the base package for your mapper interface files.
       * <p>
       * You can set more than one package by using a semicolon or comma as a separator.
       * <p>
       * Mappers will be searched for recursively starting in the specified package(s).
       *
       * @param basePackage
       *          base package name
       */
      public void setBasePackage(String basePackage) {
        this.basePackage = basePackage;
      }
    
      /**
       * Same as {@code MapperFactoryBean#setAddToConfig(boolean)}.
       *
       * @param addToConfig
       *          a flag that whether add mapper to MyBatis or not
       * @see MapperFactoryBean#setAddToConfig(boolean)
       */
      public void setAddToConfig(boolean addToConfig) {
        this.addToConfig = addToConfig;
      }
    
      /**
       * Set whether enable lazy initialization for mapper bean.
       * <p>
       * Default is {@code false}.
       * </p>
       *
       * @param lazyInitialization
       *          Set the @{code true} to enable
       * @since 2.0.2
       */
      public void setLazyInitialization(String lazyInitialization) {
        this.lazyInitialization = lazyInitialization;
      }
    
      /**
       * This property specifies the annotation that the scanner will search for.
       * <p>
       * The scanner will register all interfaces in the base package that also have the specified annotation.
       * <p>
       * Note this can be combined with markerInterface.
       *
       * @param annotationClass
       *          annotation class
       */
      public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
        this.annotationClass = annotationClass;
      }
    
      /**
       * This property specifies the parent that the scanner will search for.
       * <p>
       * The scanner will register all interfaces in the base package that also have the specified interface class as a
       * parent.
       * <p>
       * Note this can be combined with annotationClass.
       *
       * @param superClass
       *          parent class
       */
      public void setMarkerInterface(Class<?> superClass) {
        this.markerInterface = superClass;
      }
    
      /**
       * Specifies which {@code SqlSessionTemplate} to use in the case that there is more than one in the spring context.
       * Usually this is only needed when you have more than one datasource.
       * <p>
       * 
       * @deprecated Use {@link #setSqlSessionTemplateBeanName(String)} instead
       *
       * @param sqlSessionTemplate
       *          a template of SqlSession
       */
      @Deprecated
      public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
      }
    
      /**
       * Specifies which {@code SqlSessionTemplate} to use in the case that there is more than one in the spring context.
       * Usually this is only needed when you have more than one datasource.
       * <p>
       * Note bean names are used, not bean references. This is because the scanner loads early during the start process and
       * it is too early to build mybatis object instances.
       *
       * @since 1.1.0
       *
       * @param sqlSessionTemplateName
       *          Bean name of the {@code SqlSessionTemplate}
       */
      public void setSqlSessionTemplateBeanName(String sqlSessionTemplateName) {
        this.sqlSessionTemplateBeanName = sqlSessionTemplateName;
      }
    
      /**
       * Specifies which {@code SqlSessionFactory} to use in the case that there is more than one in the spring context.
       * Usually this is only needed when you have more than one datasource.
       * <p>
       * 
       * @deprecated Use {@link #setSqlSessionFactoryBeanName(String)} instead.
       *
       * @param sqlSessionFactory
       *          a factory of SqlSession
       */
      @Deprecated
      public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
      }
    
      /**
       * Specifies which {@code SqlSessionFactory} to use in the case that there is more than one in the spring context.
       * Usually this is only needed when you have more than one datasource.
       * <p>
       * Note bean names are used, not bean references. This is because the scanner loads early during the start process and
       * it is too early to build mybatis object instances.
       *
       * @since 1.1.0
       *
       * @param sqlSessionFactoryName
       *          Bean name of the {@code SqlSessionFactory}
       */
      public void setSqlSessionFactoryBeanName(String sqlSessionFactoryName) {
        this.sqlSessionFactoryBeanName = sqlSessionFactoryName;
      }
    
      /**
       * Specifies a flag that whether execute a property placeholder processing or not.
       * <p>
       * The default is {@literal false}. This means that a property placeholder processing does not execute.
       * 
       * @since 1.1.1
       *
       * @param processPropertyPlaceHolders
       *          a flag that whether execute a property placeholder processing or not
       */
      public void setProcessPropertyPlaceHolders(boolean processPropertyPlaceHolders) {
        this.processPropertyPlaceHolders = processPropertyPlaceHolders;
      }
    
      /**
       * The class of the {@link MapperFactoryBean} to return a mybatis proxy as spring bean.
       *
       * @param mapperFactoryBeanClass
       *          The class of the MapperFactoryBean
       * @since 2.0.1
       */
      public void setMapperFactoryBeanClass(Class<? extends MapperFactoryBean> mapperFactoryBeanClass) {
        this.mapperFactoryBeanClass = mapperFactoryBeanClass;
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public void setBeanName(String name) {
        this.beanName = name;
      }
    
      /**
       * Gets beanNameGenerator to be used while running the scanner.
       *
       * @return the beanNameGenerator BeanNameGenerator that has been configured
       * @since 1.2.0
       */
      public BeanNameGenerator getNameGenerator() {
        return nameGenerator;
      }
    
      /**
       * Sets beanNameGenerator to be used while running the scanner.
       *
       * @param nameGenerator
       *          the beanNameGenerator to set
       * @since 1.2.0
       */
      public void setNameGenerator(BeanNameGenerator nameGenerator) {
        this.nameGenerator = nameGenerator;
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public void afterPropertiesSet() throws Exception {
        notNull(this.basePackage, "Property 'basePackage' is required");
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // left intentionally blank
      }
    
      /**
       * {@inheritDoc}
       * 
       * @since 1.0.2
       */
      @Override
      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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
        if (StringUtils.hasText(lazyInitialization)) {
          scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
        }
        scanner.registerFilters();
        scanner.scan(
            StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
      }
    
      /*
       * BeanDefinitionRegistries are called early in application startup, before BeanFactoryPostProcessors. This means that
       * PropertyResourceConfigurers will not have been loaded and any property substitution of this class' properties will
       * fail. To avoid this, find any PropertyResourceConfigurers defined in the context and run them on this class' bean
       * definition. Then update the values.
       */
      private void processPropertyPlaceHolders() {
        Map<String, PropertyResourceConfigurer> prcs = applicationContext.getBeansOfType(PropertyResourceConfigurer.class);
    
        if (!prcs.isEmpty() && applicationContext instanceof ConfigurableApplicationContext) {
          BeanDefinition mapperScannerBean = ((ConfigurableApplicationContext) applicationContext).getBeanFactory()
              .getBeanDefinition(beanName);
    
          // PropertyResourceConfigurer does not expose any methods to explicitly perform
          // property placeholder substitution. Instead, create a BeanFactory that just
          // contains this mapper scanner and post process the factory.
          DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
          factory.registerBeanDefinition(beanName, mapperScannerBean);
    
          for (PropertyResourceConfigurer prc : prcs.values()) {
            prc.postProcessBeanFactory(factory);
          }
    
          PropertyValues values = mapperScannerBean.getPropertyValues();
    
          this.basePackage = updatePropertyValue("basePackage", values);
          this.sqlSessionFactoryBeanName = updatePropertyValue("sqlSessionFactoryBeanName", values);
          this.sqlSessionTemplateBeanName = updatePropertyValue("sqlSessionTemplateBeanName", values);
          this.lazyInitialization = updatePropertyValue("lazyInitialization", values);
        }
        this.basePackage = Optional.ofNullable(this.basePackage).map(getEnvironment()::resolvePlaceholders).orElse(null);
        this.sqlSessionFactoryBeanName = Optional.ofNullable(this.sqlSessionFactoryBeanName)
            .map(getEnvironment()::resolvePlaceholders).orElse(null);
        this.sqlSessionTemplateBeanName = Optional.ofNullable(this.sqlSessionTemplateBeanName)
            .map(getEnvironment()::resolvePlaceholders).orElse(null);
        this.lazyInitialization = Optional.ofNullable(this.lazyInitialization).map(getEnvironment()::resolvePlaceholders)
            .orElse(null);
      }
    
      private Environment getEnvironment() {
        return this.applicationContext.getEnvironment();
      }
    
      private String updatePropertyValue(String propertyName, PropertyValues values) {
        PropertyValue property = values.getPropertyValue(propertyName);
    
        if (property == null) {
          return null;
        }
    
        Object value = property.getValue();
    
        if (value == null) {
          return null;
        } else if (value instanceof String) {
          return value.toString();
        } else if (value instanceof TypedStringValue) {
          return ((TypedStringValue) value).getValue();
        } else {
          return null;
        }
      }
    
    }
    

    相关文章

      网友评论

        本文标题:spring-mybatis分析

        本文链接:https://www.haomeiwen.com/subject/wnpsvctx.html