目錄
基礎篇
一.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看看都擷取到了哪些
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwczX0xiRGZkRGZ0Xy9GbvNGL2EzXlpXazxCbKhVWzIlbaZnTYplM5ckYzxmMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLxAjNzIDO1ADM3AzNwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
上面說了,是從classpath下 META-INF/spring.factories中擷取,我們驗證一下:
發現在
上圖所示的兩個工程中找到了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注解,屬于配置類。
@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容器中擷取到了。