天天看點

SpringBoot核心知識梳理基礎篇一.SpringBoot的自動配置原理是什麼?二.Spring Boot 中如何解決跨域問題 ?六.在SpringBoot中Mybatis自動配置源碼分析

目錄

基礎篇

一.SpringBoot的自動配置原理是什麼?

二.Spring Boot 中如何解決跨域問題 ?

源碼篇

四.源碼剖析-自動配置

Spring Boot到底是如何進行自動配置的,都把哪些元件進行了自動配置?

@SpringBootApplication

@SpringBootConfiguration

@EnableAutoConfiguration

@AutoConfigurationPackage

六.在SpringBoot中Mybatis自動配置源碼分析

基礎篇

一.SpringBoot的自動配置原理是什麼?

   主要是Spring Boot的啟動類上的核心注解SpringBootApplication注解主配置類,有了這個主配置類啟動時就會為SpringBoot開啟一個@EnableAutoConfiguration注解自動配置功能。

有了這個EnableAutoConfiguration的話就會:

       1. 從配置檔案META_INF/Spring.factories加載可能用到的自動配置類

       2. 去重,并将exclude和excludeName屬性攜帶的類排除

       3. 過濾,将滿足條件(@Conditional)的自動配置類傳回

二.Spring Boot 中如何解決跨域問題 ?

     跨域可以在前端通過 JSONP 來解決,但是 JSONP 隻可以發送 GET 請求,無法發送其他類型的請求,在 RESTful 風格的應用中,就顯得非常雞肋,是以我們推薦在後端通過 (CORS,Cross-origin resource sharing) 來解決跨域問題。這種解決方案并非 Spring Boot 特有的,在傳統的SSM 架構中,就可以通過 CORS 來解決跨域問題,隻不過之前我們是在 XML 檔案中配置 CORS ,現在可以通過實作WebMvcConfigurer接口然後重寫addCorsMappings方法解決跨域問題。

@Configuration

public class CorsConfig implements WebMvcConfigurer {

  @Override

  public void addCorsMappings(CorsRegistry registry) {

    registry.addMapping("

private WebApplicationType deduceWebApplicationType() {

 //classpath下必須存在org.springframework.web.reactive.DispatcherHandler

 if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)

     && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)

      && !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null))

{

   return WebApplicationType.REACTIVE;

}

 for (String className : WEB_ENVIRONMENT_CLASSES) {

   if (!ClassUtils.isPresent(className, null)) {

     return WebApplicationType.NONE;

   }

}

  //classpath環境下存在javax.servlet.Servlet或者

org.springframework.web.context.ConfigurableWebApplicationContext

 return WebApplicationType.SERVLET;

}

傳回類型是WebApplicationType的枚舉類型, WebApplicationType 有三個枚舉,三個枚舉的解釋如其中注釋

具體的判斷邏輯如下:

WebApplicationType.REACTIVE classpath下存在

org.springframework.web.reactive.DispatcherHandler

WebApplicationType.SERVLET classpath下存在javax.servlet.Servlet或者

org.springframework.web.context.ConfigurableWebApplicationContext

WebApplicationType.NONE 不滿足以上條件。

setInitializers((Collection)

getSpringFactoriesInstances(ApplicationContextInitializer.class));

初始化classpath下 META-INF/spring.factories中已配置的ApplicationContextInitializer

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {

  return getSpringFactoriesInstances(type, new Class<?>[]{});

}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,

                           Class<?>[] parameterTypes,

Object... args) {

  ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

  // Use names and ensure unique to protect against duplicates

  //通過指定的classLoader從 META-INF/spring.factories 的資源檔案中,

  //讀取 key 為 type.getName() 的 value

  Set<String> names = new LinkedHashSet<>

(SpringFactoriesLoader.loadFactoryNames(type, classLoader));

  //建立Spring工廠執行個體

  List<T> instances = createSpringFactoriesInstances(type, parameterTypes,

      classLoader, args, names);

  //對Spring工廠執行個體排序(org.springframework.core.annotation.Order注解指定的順序)

  AnnotationAwareOrderComparator.sort(instances);

  return instances;

}

看看 getSpringFactoriesInstances 都幹了什麼,看源碼,有一個方法很重要 loadFactoryNames()這個方法很重要,這個方法是spring-core中提供的從META-INF/spring.factories中擷取指定的類(key)的同一入口方法。在這裡,擷取的是key為 org.springframework.context.ApplicationContextInitializer 的類。

debug看看都擷取到了哪些

SpringBoot核心知識梳理基礎篇一.SpringBoot的自動配置原理是什麼?二.Spring Boot 中如何解決跨域問題 ?六.在SpringBoot中Mybatis自動配置源碼分析

上面說了,是從classpath下 META-INF/spring.factories中擷取,我們驗證一下:

發現在

SpringBoot核心知識梳理基礎篇一.SpringBoot的自動配置原理是什麼?二.Spring Boot 中如何解決跨域問題 ?六.在SpringBoot中Mybatis自動配置源碼分析
SpringBoot核心知識梳理基礎篇一.SpringBoot的自動配置原理是什麼?二.Spring Boot 中如何解決跨域問題 ?六.在SpringBoot中Mybatis自動配置源碼分析

上圖所示的兩個工程中找到了debug中看到的結果。

ApplicationContextInitializer 是Spring架構的類, 這個類的主要目的就是在

ConfigurableApplicationContext 調用refresh()方法之前,回調這個類的initialize方法。

通過 ConfigurableApplicationContext 的執行個體擷取容器的環境Environment,進而實作對配置檔案的修改完善等工作。

etListeners((Collection)

getSpringFactoriesInstances(ApplicationListener.class));

初始化classpath下 META-INF/spring.factories中已配置的 ApplicationListener。

ApplicationListener 的加載過程和上面的 ApplicationContextInitializer 類的加載過程是一樣的。

不多說了,至于 ApplicationListener 是spring的事件監聽器,典型的觀察者模式,通過

ApplicationEvent 類和 ApplicationListener 接口,可以實作對spring容器全生命周期的監聽,當然也可以自定義監聽事件

總結

關于 SpringApplication 類的構造過程,到這裡我們就梳理完了。縱觀 SpringApplication 類的執行個體化過程,我們可以看到,合理的利用該類,我們能在spring容器建立之前做一些預備工作,和定制化的需求。

比如,自定義SpringBoot的Banner,比如自定義事件監聽器,再比如在容器refresh之前通過自定義ApplicationContextInitializer 修改配置一些配置或者擷取指定的bean都是可以的

六.在SpringBoot中Mybatis自動配置源碼分析

1、springboot項目最核心的就是自動加載配置,該功能則依賴的是一個注解

@SpringBootApplication中的@EnableAutoConfiguration。

2、EnableAutoConfiguration主要是通過AutoConfigurationImportSelector類來加載

以mybatis為例,*selector通過反射加載spring.factories中指定的java類,也就是加載

MybatisAutoConfiguration類(該類有Configuration注解,屬于配置類。

SpringBoot核心知識梳理基礎篇一.SpringBoot的自動配置原理是什麼?二.Spring Boot 中如何解決跨域問題 ?六.在SpringBoot中Mybatis自動配置源碼分析
SpringBoot核心知識梳理基礎篇一.SpringBoot的自動配置原理是什麼?二.Spring Boot 中如何解決跨域問題 ?六.在SpringBoot中Mybatis自動配置源碼分析

@org.springframework.context.annotation.Configuration

@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class

})

@ConditionalOnBean(DataSource.class)

@EnableConfigurationProperties(MybatisProperties.class)

@AutoConfigureAfter(DataSourceAutoConfiguration.class)

public class MybatisAutoConfiguration {

 private static final Logger logger =

LoggerFactory.getLogger(MybatisAutoConfiguration.class);

  // //與mybatis配置檔案對應

 private final MybatisProperties properties;

 private final Interceptor[] interceptors;

 private final ResourceLoader resourceLoader;

 private final DatabaseIdProvider databaseIdProvider;

 private final List<ConfigurationCustomizer> configurationCustomizers;

 public MybatisAutoConfiguration(MybatisProperties properties,

                 ObjectProvider<Interceptor[]>

interceptorsProvider,

                 ResourceLoader resourceLoader,

                 ObjectProvider<DatabaseIdProvider>

databaseIdProvider,

 ObjectProvider<List<ConfigurationCustomizer>>

configurationCustomizersProvider) {

  this.properties = properties;

  this.interceptors = interceptorsProvider.getIfAvailable();

 this.resourceLoader = resourceLoader;

  this.databaseIdProvider = databaseIdProvider.getIfAvailable();

  this.configurationCustomizers =

configurationCustomizersProvider.getIfAvailable();

}

 //postConstruct作用是在建立類的時候先調用, 校驗配置檔案是否存在

 @PostConstruct

 public void checkConfigFileExists() {

  if (this.properties.isCheckConfigLocation() &&

StringUtils.hasText(this.properties.getConfigLocation())) {

   Resource resource =

this.resourceLoader.getResource(this.properties.getConfigLocation());

   Assert.state(resource.exists(), "Cannot find config location: " +

resource

     + " (please add config file or check your Mybatis

configuration)");

 }

}

  //conditionalOnMissingBean作用:在沒有類的時候調用,建立sqlsessionFactory

sqlsessionfactory最主要的是建立并儲存了Configuration類

 @Bean

 @ConditionalOnMissingBean

 public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws

Exception {

  SqlSessionFactoryBean factory = new SqlSessionFactoryBean();

  factory.setDataSource(dataSource);

  factory.setVfs(SpringBootVFS.class);

  if (StringUtils.hasText(this.properties.getConfigLocation())) {

 factory.setConfigLocation(this.resourceLoader.getResource(this.properties.g

etConfigLocation()));

 }

  Configuration configuration = this.properties.getConfiguration();

  if (configuration == null &&

!StringUtils.hasText(this.properties.getConfigLocation())) {

   configuration = new Configuration();

 }

  if (configuration != null &&

!CollectionUtils.isEmpty(this.configurationCustomizers)) {

   for (ConfigurationCustomizer customizer :

this.configurationCustomizers) {

    customizer.customize(configuration);

  }

 }

  factory.setConfiguration(configuration);

  if (this.properties.getConfigurationProperties() != null) {

 factory.setConfigurationProperties(this.properties.getConfigurationProperti

es());

 }

  if (!ObjectUtils.isEmpty(this.interceptors)) {

   factory.setPlugins(this.interceptors);

 }

  if (this.databaseIdProvider != null) {

   factory.setDatabaseIdProvider(this.databaseIdProvider);

 }

if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {

 factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());

 }

  if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {

 factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());

 }

  if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {

   factory.setMapperLocations(this.properties.resolveMapperLocations());

 }

   // //擷取SqlSessionFactoryBean的getObject()中的對象注入Spring容器,也就是

SqlSessionFactory對象

  return factory.getObject();

}

 @Bean

 @ConditionalOnMissingBean

  // 往Spring容器中注入SqlSessionTemplate對象

 public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory

sqlSessionFactory) {

  ExecutorType executorType = this.properties.getExecutorType();

  if (executorType != null) {

   return new SqlSessionTemplate(sqlSessionFactory, executorType);

 } else {

   return new SqlSessionTemplate(sqlSessionFactory);

 }

}

3、MybatisAutoConfiguration:

①類中有個MybatisProperties類,該類對應的是mybatis的配置檔案

②類中有個sqlSessionFactory方法,作用是建立SqlSessionFactory類、Configuration類

(mybatis最主要的類,儲存着與mybatis相關的東西)

③SelSessionTemplate,作用是與mapperProoxy代理類有關

sqlSessionFactory主要是通過建立了一個SqlSessionFactoryBean,這個類實作了FactoryBean接口,是以在Spring容器就會注入這個類中定義的getObject方法傳回的對象。

看一下getObject()方法做了什麼?

@Override

 public SqlSessionFactory getObject() throws Exception {

  if (this.sqlSessionFactory == null) {

   afterPropertiesSet();

 }

  return this.sqlSessionFactory;

}

 @Override

 public void afterPropertiesSet() throws Exception {

  notNull(dataSource, "Property 'dataSource' is required");

  notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder'

is required");

 state((configuration == null && configLocation == null) || !

(configuration != null && configLocation != null),

    "Property 'configuration' and 'configLocation' can not specified

with together");

  this.sqlSessionFactory = buildSqlSessionFactory();

}

protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

  final Configuration targetConfiguration;

  XMLConfigBuilder xmlConfigBuilder = null;

  if (this.configuration != null) {

   targetConfiguration = this.configuration;

   if (targetConfiguration.getVariables() == null) {

    targetConfiguration.setVariables(this.configurationProperties);

  } else if (this.configurationProperties != null) {

 targetConfiguration.getVariables().putAll(this.configurationProperties);

  }

 } else if (this.configLocation != null) {

   xmlConfigBuilder = new

XMLConfigBuilder(this.configLocation.getInputStream(), null,

this.configurationProperties);

   targetConfiguration = xmlConfigBuilder.getConfiguration();

 } else {

   LOGGER.debug(

    () -> "Property 'configuration' or 'configLocation' not specified,

using default MyBatis Configuration");

   targetConfiguration = new Configuration();

 Optional.ofNullable(this.configurationProperties).ifPresent(targetConfigura

tion::setVariables);

 }

 Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setO

bjectFactory);

 Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguratio

n::setObjectWrapperFactory);

 Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);

  if (hasLength(this.typeAliasesPackage)) {

   scanClasses(this.typeAliasesPackage,

this.typeAliasesSuperType).stream()

    .filter(clazz -> !clazz.isAnonymousClass()).filter(clazz ->

!clazz.isInterface())

    .filter(clazz ->

!clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::

registerAlias);

 }

  if (!isEmpty(this.typeAliases)) {

   Stream.of(this.typeAliases).forEach(typeAlias -> {

 targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);

    LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");

  });

 }

  if (!isEmpty(this.plugins)) {

   Stream.of(this.plugins).forEach(plugin -> {

    targetConfiguration.addInterceptor(plugin);

    LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");

  });

 }

  if (hasLength(this.typeHandlersPackage)) {

   scanClasses(this.typeHandlersPackage,

TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())

    .filter(clazz -> !clazz.isInterface()).filter(clazz ->

!Modifier.isAbstract(clazz.getModifiers()))

    .forEach(targetConfiguration.getTypeHandlerRegistry()::register);

 }

  if (!isEmpty(this.typeHandlers)) {

   Stream.of(this.typeHandlers).forEach(typeHandler -> {

    targetConfiguration.getTypeHandlerRegistry().register(typeHandler);

    LOGGER.debug(() -> "Registered type handler: '" + typeHandler +

"'");

  });

 }

  targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler);

  if (!isEmpty(this.scriptingLanguageDrivers)) {

   Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {

    targetConfiguration.getLanguageRegistry().register(languageDriver);

    LOGGER.debug(() -> "Registered scripting language driver: '" +

languageDriver + "'");

  });

 }

  Optional.ofNullable(this.defaultScriptingLanguageDriver)

   .ifPresent(targetConfiguration::setDefaultScriptingLanguage);

  if (this.databaseIdProvider != null) {// fix #64 set databaseId before

parse mapper xmls

   try {

 targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(thi

s.dataSource));

  } catch (SQLException e) {

    throw new NestedIOException("Failed getting a databaseId", e);

  }

 }

 Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);

  if (xmlConfigBuilder != null) {

   try {

xmlConfigBuilder.parse();

 LOGGER.debug(() -> "Parsed configuration file: '" +

this.configLocation + "'");

  } catch (Exception ex) {

    throw new NestedIOException("Failed to parse config resource: " +

this.configLocation, ex);

  } finally {

    ErrorContext.instance().reset();

  }

 }

  targetConfiguration.setEnvironment(new Environment(this.environment,

    this.transactionFactory == null ? new

SpringManagedTransactionFactory() : this.transactionFactory,

    this.dataSource));

  if (this.mapperLocations != null) {

   if (this.mapperLocations.length == 0) {

    LOGGER.warn(() -> "Property 'mapperLocations' was specified but

matching resources are not found.");

  } else {

    for (Resource mapperLocation : this.mapperLocations) {

     if (mapperLocation == null) {

      continue;

    }

     try {

      XMLMapperBuilder xmlMapperBuilder = new

XMLMapperBuilder(mapperLocation.getInputStream(),

        targetConfiguration, mapperLocation.toString(),

targetConfiguration.getSqlFragments());

      //這個方法已經是mybaits的源碼,初始化流程

      xmlMapperBuilder.parse();

    } catch (Exception e) {

      throw new NestedIOException("Failed to parse mapping resource:

'" + mapperLocation + "'", e);

    } finally {

      ErrorContext.instance().reset();

    }

     LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation +

"'");

   }

  }

 } else {

   LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");

 }

//這個方法已經是mybaits的源碼,初始化流程

  return this.sqlSessionFactoryBuilder.build(targetConfiguration);

}

這個已經很明顯了,實際上就是調用了MyBatis的初始化流程

現在已經得到了SqlSessionFactory了,接下來就是如何掃描到相關的Mapper接口了。

這個需要看這個注解

@MapperScan(basePackages = “com.mybatis.mapper”)

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

@Documented

@Import(MapperScannerRegistrar.class)

@Repeatable(MapperScans.class)

public @interface MapperScan

通過@Import的方式會掃描到MapperScannerRegistrar類。

MapperScannerRegistrar實作了ImportBeanDefinitionRegistrar接口,那麼在spring執行個體化之前就會調用到registerBeanDefinitions方法

public class MapperScannerRegistrar implements

ImportBeanDefinitionRegistrar, ResourceLoaderAware

@Override

 public void registerBeanDefinitions(AnnotationMetadata

importingClassMetadata, BeanDefinitionRegistry registry) {

//拿到MapperScan注解,并解析注解中定義的屬性封裝成AnnotationAttributes對象

  AnnotationAttributes mapperScanAttrs = AnnotationAttributes

.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.get

Name()));

  if (mapperScanAttrs != null) {

   registerBeanDefinitions(importingClassMetadata, mapperScanAttrs,

registry,

     generateBaseBeanName(importingClassMetadata, 0));

 }

}

 void registerBeanDefinitions(AnnotationMetadata annoMeta,

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::hasTex

t).collect(Collectors.toList()));

 basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages"))

.filter(StringUtils::hasText)

   .collect(Collectors.toList()));

 basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClass

es")).map(ClassUtils::getPackageName)

   .collect(Collectors.toList()));

  if (basePackages.isEmpty()) {

   basePackages.add(getDefaultBasePackage(annoMeta));

 }

  String lazyInitialization = annoAttrs.getString("lazyInitialization");

  if (StringUtils.hasText(lazyInitialization)) {

   builder.addPropertyValue("lazyInitialization", lazyInitialization);

 }

  builder.addPropertyValue("basePackage",

StringUtils.collectionToCommaDelimitedString(basePackages));

//把類型為MapperScannerConfigurer的注冊到spring容器中

  registry.registerBeanDefinition(beanName, builder.getBeanDefinition());

}

MapperScannerConfigurer實作了BeanDefinitionRegistryPostProcessor接口,是以接着又會掃

描并調用到postProcessBeanDefinitionRegistry方法。

public class MapperScannerConfigurer

  implements BeanDefinitionRegistryPostProcessor, InitializingBean,

ApplicationContextAware, BeanNameAware

@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));

}

public int scan(String... basePackages) {

int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

doScan(basePackages);

// Register annotation config processors, if necessary.

if (this.includeAnnotationConfig) {

AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);

}

return (this.registry.getBeanDefinitionCount() -

beanCountAtScanStart);

}

@Override

 public Set<BeanDefinitionHolder> doScan(String... basePackages) {

//這個方法主要就注冊掃描basePackages路徑下的mapper接口,然後封裝成一個

BeanDefinition後加入到spring容器中

  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的beanClass類型,修改為MapperFactoryBean

   processBeanDefinitions(beanDefinitions);

 }

  return beanDefinitions;

}

修改了mapper的beanClass類型為MapperFactoryBean

private void processBeanDefinitions(Set<BeanDefinitionHolder>

beanDefinitions) {

  GenericBeanDefinition definition;

  for (BeanDefinitionHolder holder : beanDefinitions) {

   definition = (GenericBeanDefinition) holder.getBeanDefinition();

   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

 definition.getConstructorArgumentValues().addGenericArgumentValue(beanClass

Name); // issue #59

   //修改beanClass類型

   definition.setBeanClass(this.mapperFactoryBeanClass);

   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);

 }

}

上述幾步主要是完成通過

@MapperScan(basePackages = “com.mybatis.mapper”)這個定義,掃描指定包下的

mapper接口,然後設定每個mapper接口的beanClass屬性為MapperFactoryBean類型并加入

到spring的bean容器中。

MapperFactoryBean實作了FactoryBean接口,是以當spring從待執行個體化的bean容器中周遊到這個bean并開始執行執行個體化時傳回的對象實際上是getObject方法中傳回的對象。

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements

FactoryBean<T>

最後看一下MapperFactoryBean的getObject方法,實際上傳回的就是mybatis中通過getMapper拿到的對象,熟悉mybatis源碼的就應該清楚,這個就是mybatis通過動态代理生成的mapper接口實作類'

 @Override

 public T getObject() throws Exception {

  return getSqlSession().getMapper(this.mapperInterface);

}

到此,mapper接口現在也通過動态代理生成了實作類,并且注入到spring的bean容器中了,之後使用者就可以通過@Autowired或者getBean等方式,從spring容器中擷取到了。