• 【知识积累】利用BeanDefinitionRegistryPostProcessor修改Mybatis的mapper代理对象


    1、配置MyMapperScannerConfigurer

    1. package com.darren.spring;
    2. import org.springframework.context.annotation.Bean;
    3. import org.springframework.context.annotation.Configuration;
    4. /**
    5. *

      mybatis

    6. *

    7. *
    8. * @author : Darren
    9. * @date : 2022年07月31日 01:17:52
    10. **/
    11. @Configuration
    12. public class MyBatisConfig {
    13. private static final String BASE_PACKAGE = "com.darren.dao1";
    14. @Bean
    15. public MyMapperScannerConfigurer myMapperScannerConfigurer(){
    16. MyMapperScannerConfigurer myMapperScannerConfigurer = new MyMapperScannerConfigurer();
    17. myMapperScannerConfigurer.setBasePackage(BASE_PACKAGE);
    18. return myMapperScannerConfigurer;
    19. }
    20. }

    2、修改postProcessBeanDefinitionRegistry方法

    1. /**
    2. * Copyright 2010-2020 the original author or authors.
    3. *
    4. * Licensed under the Apache License, Version 2.0 (the "License");
    5. * you may not use this file except in compliance with the License.
    6. * You may obtain a copy of the License at
    7. *
    8. * http://www.apache.org/licenses/LICENSE-2.0
    9. *
    10. * Unless required by applicable law or agreed to in writing, software
    11. * distributed under the License is distributed on an "AS IS" BASIS,
    12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13. * See the License for the specific language governing permissions and
    14. * limitations under the License.
    15. */
    16. package com.darren.spring;
    17. import org.apache.ibatis.session.SqlSessionFactory;
    18. import org.mybatis.spring.SqlSessionTemplate;
    19. import org.springframework.beans.PropertyValue;
    20. import org.springframework.beans.PropertyValues;
    21. import org.springframework.beans.factory.BeanNameAware;
    22. import org.springframework.beans.factory.InitializingBean;
    23. import org.springframework.beans.factory.config.BeanDefinition;
    24. import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    25. import org.springframework.beans.factory.config.PropertyResourceConfigurer;
    26. import org.springframework.beans.factory.config.TypedStringValue;
    27. import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    28. import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
    29. import org.springframework.beans.factory.support.BeanNameGenerator;
    30. import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    31. import org.springframework.context.ApplicationContext;
    32. import org.springframework.context.ApplicationContextAware;
    33. import org.springframework.context.ConfigurableApplicationContext;
    34. import org.springframework.core.env.Environment;
    35. import org.springframework.util.StringUtils;
    36. import java.lang.annotation.Annotation;
    37. import java.util.Map;
    38. import java.util.Optional;
    39. import static org.springframework.util.Assert.notNull;
    40. /**
    41. * BeanDefinitionRegistryPostProcessor that searches recursively starting from a base package for interfaces and
    42. * registers them as {@code MapperFactoryBean}. Note that only interfaces with at least one method will be registered;
    43. * concrete classes will be ignored.
    44. *

    45. * This class was a {code BeanFactoryPostProcessor} until 1.0.1 version. It changed to
    46. * {@code BeanDefinitionRegistryPostProcessor} in 1.0.2. See https://jira.springsource.org/browse/SPR-8269 for the
    47. * details.
    48. *

    49. * The {@code basePackage} property can contain more than one package name, separated by either commas or semicolons.
    50. *

    51. * This class supports filtering the mappers created by either specifying a marker interface or an annotation. The
    52. * {@code annotationClass} property specifies an annotation to search for. The {@code markerInterface} property
    53. * specifies a parent interface to search for. If both properties are specified, mappers are added for interfaces that
    54. * match either criteria. By default, these two properties are null, so all interfaces in the given
    55. * {@code basePackage} are added as mappers.
    56. *

    57. * This configurer enables autowire for all the beans that it creates so that they are automatically autowired with the
    58. * proper {@code SqlSessionFactory} or {@code SqlSessionTemplate}. If there is more than one {@code SqlSessionFactory}
    59. * in the application, however, autowiring cannot be used. In this case you must explicitly specify either an
    60. * {@code SqlSessionFactory} or an {@code SqlSessionTemplate} to use via the bean name properties. Bean names
    61. * are used rather than actual objects because Spring does not initialize property placeholders until after this class
    62. * is processed.
    63. *

    64. * Passing in an actual object which may require placeholders (i.e. DB user password) will fail. Using bean names defers
    65. * actual object creation until later in the startup process, after all placeholder substitution is completed. However,
    66. * note that this configurer does support property placeholders of its own properties. The
    67. * basePackage and bean name properties all support ${property} style substitution.
    68. *

    69. * Configuration sample:
    70. *
    71. *
    72. * {@code
    73. *
    74. *
    75. *
    76. *
    77. *
    78. * }
    79. *
  • *
  • * @author Hunter Presnall
  • * @author Eduardo Macarron
  • *
  • * @see MapperFactoryBean
  • */
  • public class MyMapperScannerConfigurer
  • 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 Classextends Annotation> annotationClass;
  • private Class markerInterface;
  • private Classextends MyMapperFactoryBean> 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.
  • *

  • * You can set more than one package by using a semicolon or comma as a separator.
  • *

  • * 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.
  • *

  • * Default is {@code false}.
  • *

  • *
  • * @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.
  • *

  • * The scanner will register all interfaces in the base package that also have the specified annotation.
  • *

  • * Note this can be combined with markerInterface.
  • *
  • * @param annotationClass
  • * annotation class
  • */
  • public void setAnnotationClass(Class annotationClass) {
  • this.annotationClass = annotationClass;
  • }
  • /**
  • * This property specifies the parent that the scanner will search for.
  • *

  • * The scanner will register all interfaces in the base package that also have the specified interface class as a
  • * parent.
  • *

  • * 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.
  • *

  • *
  • * @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.
  • *

  • * 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.
  • *

  • *
  • * @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.
  • *

  • * 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.
  • *

  • * 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 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();
  • }
  • MyClassPathMapperScanner scanner = new MyClassPathMapperScanner(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 prcs = applicationContext.getBeansOfType(PropertyResourceConfigurer.class,
  • false, false);
  • 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;
  • }
  • }
  • }
  • // 实现自己的MyMapperFactoryBean
    private Class mapperFactoryBeanClass;
    /**
         * {@inheritDoc}
         *
         * @since 1.0.2
         */
        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
            if (this.processPropertyPlaceHolders) {
                processPropertyPlaceHolders();
            }
    
            MyClassPathMapperScanner scanner = new MyClassPathMapperScanner(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));
        }
    
    注:
    MyMapperScannerConfigurer是实现了BeanDefinitionRegistryPostProcessor,BeanDefinitionRegistryPostProcessor由继承了BeanFactoryPostProcessor,在spring容器启动的时候会调用所有BeanFactoryPostProcessor接口的实现的postProcessBeanDefinitionRegistry方法,MyMapperScannerConfigurer在postProcessBeanDefinitionRegistry方法用来修改BeanDefinition。

    3、MyClassPathMapperScanner的doScan方法种调用父类的doScan方法

    1. /**
    2. * Copyright 2010-2019 the original author or authors.
    3. *
    4. * Licensed under the Apache License, Version 2.0 (the "License");
    5. * you may not use this file except in compliance with the License.
    6. * You may obtain a copy of the License at
    7. *
    8. * http://www.apache.org/licenses/LICENSE-2.0
    9. *
    10. * Unless required by applicable law or agreed to in writing, software
    11. * distributed under the License is distributed on an "AS IS" BASIS,
    12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13. * See the License for the specific language governing permissions and
    14. * limitations under the License.
    15. */
    16. package org.mybatis.spring.mapper;
    17. import org.apache.ibatis.session.SqlSessionFactory;
    18. import org.mybatis.logging.Logger;
    19. import org.mybatis.logging.LoggerFactory;
    20. import org.mybatis.spring.SqlSessionTemplate;
    21. import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
    22. import org.springframework.beans.factory.config.BeanDefinition;
    23. import org.springframework.beans.factory.config.BeanDefinitionHolder;
    24. import org.springframework.beans.factory.config.RuntimeBeanReference;
    25. import org.springframework.beans.factory.support.AbstractBeanDefinition;
    26. import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    27. import org.springframework.beans.factory.support.GenericBeanDefinition;
    28. import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
    29. import org.springframework.core.type.filter.AnnotationTypeFilter;
    30. import org.springframework.core.type.filter.AssignableTypeFilter;
    31. import org.springframework.util.StringUtils;
    32. import java.lang.annotation.Annotation;
    33. import java.util.Arrays;
    34. import java.util.Set;
    35. /**
    36. * A {@link ClassPathBeanDefinitionScanner} that registers Mappers by {@code basePackage}, {@code annotationClass}, or
    37. * {@code markerInterface}. If an {@code annotationClass} and/or {@code markerInterface} is specified, only the
    38. * specified types will be searched (searching for all interfaces will be disabled).
    39. *

    40. * This functionality was previously a private class of {@link MapperScannerConfigurer}, but was broken out in version
    41. * 1.2.0.
    42. *
    43. * @author Hunter Presnall
    44. * @author Eduardo Macarron
    45. *
    46. * @see MapperFactoryBean
    47. * @since 1.2.0
    48. */
    49. public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
    50. private static final Logger LOGGER = LoggerFactory.getLogger(ClassPathMapperScanner.class);
    51. private boolean addToConfig = true;
    52. private boolean lazyInitialization;
    53. private SqlSessionFactory sqlSessionFactory;
    54. private SqlSessionTemplate sqlSessionTemplate;
    55. private String sqlSessionTemplateBeanName;
    56. private String sqlSessionFactoryBeanName;
    57. private Classextends Annotation> annotationClass;
    58. private Class markerInterface;
    59. private Classextends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
    60. public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
    61. super(registry, false);
    62. }
    63. public void setAddToConfig(boolean addToConfig) {
    64. this.addToConfig = addToConfig;
    65. }
    66. public void setAnnotationClass(Class annotationClass) {
    67. this.annotationClass = annotationClass;
    68. }
    69. /**
    70. * Set whether enable lazy initialization for mapper bean.
    71. *

    72. * Default is {@code false}.
    73. *

    74. *
    75. * @param lazyInitialization
    76. * Set the @{code true} to enable
    77. * @since 2.0.2
    78. */
    79. public void setLazyInitialization(boolean lazyInitialization) {
    80. this.lazyInitialization = lazyInitialization;
    81. }
    82. public void setMarkerInterface(Class markerInterface) {
    83. this.markerInterface = markerInterface;
    84. }
    85. public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    86. this.sqlSessionFactory = sqlSessionFactory;
    87. }
    88. public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
    89. this.sqlSessionTemplate = sqlSessionTemplate;
    90. }
    91. public void setSqlSessionTemplateBeanName(String sqlSessionTemplateBeanName) {
    92. this.sqlSessionTemplateBeanName = sqlSessionTemplateBeanName;
    93. }
    94. public void setSqlSessionFactoryBeanName(String sqlSessionFactoryBeanName) {
    95. this.sqlSessionFactoryBeanName = sqlSessionFactoryBeanName;
    96. }
    97. /**
    98. * @deprecated Since 2.0.1, Please use the {@link #setMapperFactoryBeanClass(Class)}.
    99. */
    100. @Deprecated
    101. public void setMapperFactoryBean(MapperFactoryBean mapperFactoryBean) {
    102. this.mapperFactoryBeanClass = mapperFactoryBean == null ? MapperFactoryBean.class : mapperFactoryBean.getClass();
    103. }
    104. /**
    105. * Set the {@code MapperFactoryBean} class.
    106. *
    107. * @param mapperFactoryBeanClass
    108. * the {@code MapperFactoryBean} class
    109. * @since 2.0.1
    110. */
    111. public void setMapperFactoryBeanClass(Class mapperFactoryBeanClass) {
    112. this.mapperFactoryBeanClass = mapperFactoryBeanClass == null ? MapperFactoryBean.class : mapperFactoryBeanClass;
    113. }
    114. /**
    115. * Configures parent scanner to search for the right interfaces. It can search for all interfaces or just for those
    116. * that extends a markerInterface or/and those annotated with the annotationClass
    117. */
    118. public void registerFilters() {
    119. boolean acceptAllInterfaces = true;
    120. // if specified, use the given annotation and / or marker interface
    121. if (this.annotationClass != null) {
    122. addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
    123. acceptAllInterfaces = false;
    124. }
    125. // override AssignableTypeFilter to ignore matches on the actual marker interface
    126. if (this.markerInterface != null) {
    127. addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
    128. @Override
    129. protected boolean matchClassName(String className) {
    130. return false;
    131. }
    132. });
    133. acceptAllInterfaces = false;
    134. }
    135. if (acceptAllInterfaces) {
    136. // default include filter that accepts all classes
    137. addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
    138. }
    139. // exclude package-info.java
    140. addExcludeFilter((metadataReader, metadataReaderFactory) -> {
    141. String className = metadataReader.getClassMetadata().getClassName();
    142. return className.endsWith("package-info");
    143. });
    144. }
    145. /**
    146. * Calls the parent search that will search and register all the candidates. Then the registered objects are post
    147. * processed to set them as MapperFactoryBeans
    148. */
    149. @Override
    150. public Set doScan(String... basePackages) {
    151. Set beanDefinitions = super.doScan(basePackages);
    152. if (beanDefinitions.isEmpty()) {
    153. LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
    154. + "' package. Please check your configuration.");
    155. } else {
    156. processBeanDefinitions(beanDefinitions);
    157. }
    158. return beanDefinitions;
    159. }
    160. private void processBeanDefinitions(Set beanDefinitions) {
    161. GenericBeanDefinition definition;
    162. for (BeanDefinitionHolder holder : beanDefinitions) {
    163. definition = (GenericBeanDefinition) holder.getBeanDefinition();
    164. String beanClassName = definition.getBeanClassName();
    165. LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
    166. + "' mapperInterface");
    167. // the mapper interface is the original class of the bean
    168. // but, the actual class of the bean is MapperFactoryBean
    169. definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
    170. definition.setBeanClass(this.mapperFactoryBeanClass);
    171. definition.getPropertyValues().add("addToConfig", this.addToConfig);
    172. boolean explicitFactoryUsed = false;
    173. if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
    174. definition.getPropertyValues().add("sqlSessionFactory",
    175. new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
    176. explicitFactoryUsed = true;
    177. } else if (this.sqlSessionFactory != null) {
    178. definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
    179. explicitFactoryUsed = true;
    180. }
    181. if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
    182. if (explicitFactoryUsed) {
    183. LOGGER.warn(
    184. () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
    185. }
    186. definition.getPropertyValues().add("sqlSessionTemplate",
    187. new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
    188. explicitFactoryUsed = true;
    189. } else if (this.sqlSessionTemplate != null) {
    190. if (explicitFactoryUsed) {
    191. LOGGER.warn(
    192. () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
    193. }
    194. definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
    195. explicitFactoryUsed = true;
    196. }
    197. if (!explicitFactoryUsed) {
    198. LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
    199. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    200. }
    201. definition.setLazyInit(lazyInitialization);
    202. }
    203. }
    204. /**
    205. * {@inheritDoc}
    206. */
    207. @Override
    208. protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    209. return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    210. }
    211. /**
    212. * {@inheritDoc}
    213. */
    214. @Override
    215. protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) {
    216. if (super.checkCandidate(beanName, beanDefinition)) {
    217. return true;
    218. } else {
    219. LOGGER.warn(() -> "Skipping MapperFactoryBean with name '" + beanName + "' and '"
    220. + beanDefinition.getBeanClassName() + "' mapperInterface" + ". Bean already defined with the same name!");
    221. return false;
    222. }
    223. }
    224. }

    /**
         * Calls the parent search that will search and register all the candidates. Then the registered objects are post
         * processed to set them as MapperFactoryBeans
         */
        @Override
        public Set doScan(String... basePackages) {
            Set beanDefinitions = super.doScan(basePackages);

            if (beanDefinitions.isEmpty()) {
                LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
                        + "' package. Please check your configuration.");
            } else {
                processBeanDefinitions(beanDefinitions);
            }

            return beanDefinitions;
        }

    调用父类ClassPathBeanDefinitionScanner的doScan方法,返回所有扫描到的BeanDefinition对象

    4、ClassPathBeanDefinitionScanner开始扫描maper接口,并注册到spring工厂中

    注册:其实就是添加到BeanFactory(默认实现:DefaultListableBeanFactory)里面的Map beanDefinitionMap

    1. /**
    2. * Perform a scan within the specified base packages.
    3. * @param basePackages the packages to check for annotated classes
    4. * @return number of beans registered
    5. */
    6. public int scan(String... basePackages) {
    7. int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    8. doScan(basePackages);
    9. // Register annotation config processors, if necessary.
    10. if (this.includeAnnotationConfig) {
    11. AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    12. }
    13. return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
    14. }

    doScan是真正扫描的地方,并注册的地方,传入的basePackages就是MyBatisConfig传入的要扫描的包路径

    5、处理父类ClassPathBeanDefinitionScanner返回的所有扫描到的BeanDefinition对象,遍历修改

    // 实现自己的MyMapperFactoryBean
    private Class mapperFactoryBeanClass = MyMapperFactoryBean.class;

    private void processBeanDefinitions(Set beanDefinitions) {
            GenericBeanDefinition definition;
            for (BeanDefinitionHolder holder : beanDefinitions) {

                // 将拿到的BeanDefinition转换为普通的BeanDefinition
                definition = (GenericBeanDefinition) holder.getBeanDefinition();

                // 拿到bean的名称
                String beanClassName = definition.getBeanClassName();
                LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
                        + "' mapperInterface");

                // the mapper interface is the original class of the bean
                // but, the actual class of the bean is MapperFactoryBean
                // 构造器参数中传入mapper接口的名称

     definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59

                // 设置自己的实现的MyMapperFactoryBean
                definition.setBeanClass(this.mapperFactoryBeanClass);

               // 添加一个addToConfig的参数

                definition.getPropertyValues().add("addToConfig", this.addToConfig);

                boolean explicitFactoryUsed = false;
                if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
                    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;
                }

                if (!explicitFactoryUsed) {
                    LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");

                    // 设置自动装配的模式
                    definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
                }

                // 设置懒加载
                definition.setLazyInit(lazyInitialization);
            }
        }

    6、Spring在实例化Bean的时候会判断Bean是否是FactoryBean接口的实现,如果是通过FactoryBean的T getObject()方法返回用户自己实例化好的对象

    1. /**
    2. * Copyright 2010-2019 the original author or authors.
    3. *
    4. * Licensed under the Apache License, Version 2.0 (the "License");
    5. * you may not use this file except in compliance with the License.
    6. * You may obtain a copy of the License at
    7. *
    8. * http://www.apache.org/licenses/LICENSE-2.0
    9. *
    10. * Unless required by applicable law or agreed to in writing, software
    11. * distributed under the License is distributed on an "AS IS" BASIS,
    12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13. * See the License for the specific language governing permissions and
    14. * limitations under the License.
    15. */
    16. package com.darren.spring;
    17. import static org.springframework.util.Assert.notNull;
    18. import org.apache.ibatis.executor.ErrorContext;
    19. import org.apache.ibatis.io.Resources;
    20. import org.apache.ibatis.session.Configuration;
    21. import org.apache.ibatis.session.SqlSession;
    22. import org.apache.ibatis.session.SqlSessionFactory;
    23. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    24. import org.mybatis.spring.SqlSessionTemplate;
    25. import org.mybatis.spring.support.SqlSessionDaoSupport;
    26. import org.springframework.beans.factory.FactoryBean;
    27. import java.io.IOException;
    28. import java.io.InputStream;
    29. /**
    30. * BeanFactory that enables injection of MyBatis mapper interfaces. It can be set up with a SqlSessionFactory or a
    31. * pre-configured SqlSessionTemplate.
    32. *

    33. * Sample configuration:
    34. *
    35. *
    36. * {@code
    37. *
    38. *
    39. *
    40. *
    41. *
    42. *
    43. *
    44. *
    45. *
    46. *
    47. *
    48. * }
    49. *
  • *

  • * Note that this factory can only inject interfaces, not concrete classes.
  • *
  • * @author Eduardo Macarron
  • *
  • * @see SqlSessionTemplate
  • */
  • public class MyMapperFactoryBean implements FactoryBean {
  • private SqlSessionFactory sqlSessionFactory;
  • private Class mapperInterface;
  • private boolean addToConfig = true;
  • public MyMapperFactoryBean() {
  • // intentionally empty
  • }
  • public MyMapperFactoryBean(Class mapperInterface) {
  • this.mapperInterface = mapperInterface;
  • }
  • /**
  • * {@inheritDoc}
  • */
  • @Override
  • public T getObject() throws Exception {
  • this.getSqlSessionFactory();
  • SqlSession sqlSession = sqlSessionFactory.openSession();
  • return sqlSession.getMapper(this.mapperInterface);
  • }
  • private void getSqlSessionFactory(){
  • // 根据全局配置文件创建出SqlSessionFactory
  • // SqlSessionFactory:负责创建SqlSession对象的工厂
  • // SqlSession:表示跟数据库建议的一次会话
  • String resource = "mybatis-config.xml";
  • InputStream inputStream = null;
  • try {
  • inputStream = Resources.getResourceAsStream(resource);
  • } catch (IOException e) {
  • e.printStackTrace();
  • }
  • sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  • }
  • /**
  • * {@inheritDoc}
  • */
  • @Override
  • public Class 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 mapperInterface) {
  • this.mapperInterface = mapperInterface;
  • }
  • /**
  • * Return the mapper interface of the MyBatis mapper
  • *
  • * @return class of the interface
  • */
  • public Class 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.
  • *

  • * If it is true, the mapper will be added to MyBatis in the case it is not already registered.
  • *

  • * By default addToConfig is true.
  • *
  • * @param addToConfig
  • * a flag that whether add mapper to MyBatis or not
  • */
  • 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;
  • }
  • }
  • /**
         * {@inheritDoc}
         */
        @Override
        public T getObject() throws Exception {

           // 初始化SqlSessionFactory
            this.getSqlSessionFactory();

            // 打开一个SqlSession
            SqlSession sqlSession = sqlSessionFactory.openSession();

            // 返回mapper接口的代理对象
            return sqlSession.getMapper(this.mapperInterface);
        }

        private void getSqlSessionFactory(){
            // 根据全局配置文件创建出SqlSessionFactory
            // SqlSessionFactory:负责创建SqlSession对象的工厂
            // SqlSession:表示跟数据库建议的一次会话
            String resource = "mybatis-config.xml";
            InputStream inputStream = null;
            try {
                inputStream = Resources.getResourceAsStream(resource);
            } catch (IOException e) {
                e.printStackTrace();
            }
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        }

    7、测试

    1. package com.darren;
    2. import com.darren.bean.Emp;
    3. import com.darren.dao.EmpDao;
    4. import com.darren.dao1.EmpDao1;
    5. import org.junit.Test;
    6. import org.junit.runner.RunWith;
    7. import org.springframework.beans.factory.annotation.Autowired;
    8. import org.springframework.test.context.ContextConfiguration;
    9. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    10. import java.util.List;
    11. /**
    12. *

      mybatis

    13. *

    14. *
    15. * @author : Darren
    16. * @date : 2022年07月24日 18:30:32
    17. **/
    18. @ContextConfiguration(locations = {"classpath:spring.xml"})
    19. @RunWith(SpringJUnit4ClassRunner.class)
    20. public class MyBatisSpringTest {
    21. @Autowired
    22. private EmpDao dao;
    23. @Autowired
    24. private EmpDao1 dao1;
    25. @Test
    26. public void test01(){
    27. List empByEmpno = dao.selectAll();
    28. empByEmpno.stream().forEach(System.out::println);
    29. List empByEmpno1 = dao1.selectAll();
    30. empByEmpno1.stream().forEach(System.out::println);
    31. }
    32. }

     // 没有修改过的

    Creating a new SqlSession
    SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7203c7ff] was not registered for synchronization because synchronization is not active
    JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@59252cb6] will not be managed by Spring
    Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7203c7ff]
    Emp{empno=7369, ename='Darren', job='CEO', mgr=1000000, hiredate=Wed Jun 10 08:00:00 CST 2015, sal=1.0E7, common=null, deptno=0}
    Emp{empno=7379, ename='Darren1', job='CEO', mgr=1000000, hiredate=Wed Jun 10 08:00:00 CST 2015, sal=1.0E7, common=null, deptno=0}

    // 自己修改的(配置文件开启了logImpl,所以打了sql日志)
    Opening JDBC Connection
    Created connection 1033348658.
    Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3d97a632]
    ==>  Preparing: select * from emp
    ==> Parameters: 
    <==    Columns: empno, ename, job, mgr, hiredate, sal, common, deptno, create_time, update_time
    <==        Row: 7369, Darren, CEO, 1000000, 2015-06-10 00:00:00, 10000000.00, null, 0, 2022-07-24 09:38:43, 2022-07-24 09:38:43
    <==        Row: 7379, Darren1, CEO, 1000000, 2015-06-10 00:00:00, 10000000.00, null, 0, 2022-07-24 17:11:34, 2022-07-24 17:11:34
    <==      Total: 2

    Emp{empno=7369, ename='Darren', job='CEO', mgr=1000000, hiredate=Wed Jun 10 08:00:00 CST 2015, sal=1.0E7, common=null, deptno=0}
    Emp{empno=7379, ename='Darren1', job='CEO', mgr=1000000, hiredate=Wed Jun 10 08:00:00 CST 2015, sal=1.0E7, common=null, deptno=0}

  • 相关阅读:
    Spark和MR的本质区别
    openEuler 24.03 LTS - 华为欧拉开源版(华为 RHEL 兼容发行版)
    2023 ICPC 网络赛 第一场(补题:F)
    flutter 数组筛查功能实现
    win11系统点开图片几秒后就显示“此处没有任何要显示的内容
    竞赛 深度学习大数据物流平台 python
    排序(前篇)
    K8s 搭建一主一从
    docker安装单机nacos、rocketmq、reids、xxl-job、minio、elasticsearch、kibana、gogs、nginx
    lower_bound 和 upper_bound
  • 原文地址:https://blog.csdn.net/axin1240101543/article/details/126087079