前言

首先我们建立一个SpringBoot工程,导入mybatis-spring-boot-starter依赖。

<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>

导入后发现这个依赖其实就是帮助我们导入了mybatis需要的依赖,其中和自动配置相关最重要的一个就是mybatis-spring-boot-autoconfigure

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency>
</dependencies>

MyBatis自动配置中是如何工作的

如上面分析自动配置的关键类我们就从mybatis-spring-boot-autoconfigure开始着手分析。

spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

从spring.factories文件看到这里通过SPI机制加载了两个类

  • MybatisAutoConfiguration
  • MybatisLanguateDriverAutoConfiguration.

MybatisAutoConfiguration

//表示这是一个Spring配置类
@Configuration
//这个类需要在classpath中存在SqlSessionFactory和SqlSessionFactoryBean时才生效
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) --
//这个类需要有一个DataSource的Canidate注册到Spring容器中
@ConditionalOnSingleCandidate(DataSource.class)
//使MybatisProperties注解类生效
@EnableConfigurationProperties({MybatisProperties.class})
//需要在DataSourceAutoConfiguration和MybatisLanguageDriverAutoConfiguration自动配置之后执行
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
public class MybatisAutoConfiguration implements InitializingBean { }

MybatisAutoConfiguration#sqlSessionFactory

@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
//创建一个SqlSessionFactoryBean, 在mybatis-spring项目下
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
} //应用Configuration对象
this.applyConfiguration(factory);
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
} 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 (this.properties.getTypeAliasesSuperType() != null) {
factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
} if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
} if (!ObjectUtils.isEmpty(this.typeHandlers)) {
factory.setTypeHandlers(this.typeHandlers);
} if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
} Set<String> factoryPropertyNames = (Set)Stream.of((new BeanWrapperImpl(SqlSessionFactoryBean.class)).getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet());
Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
factory.setScriptingLanguageDrivers(this.languageDrivers);
if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
defaultLanguageDriver = this.languageDrivers[0].getClass();
}
} //设置默认的语言驱动类
if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
} //这里默认会返回一个DefaultSqlSessionFactory对象
return factory.getObject();
}

MybatisAutoConfiguration#sqlSessionTemplate

@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
return executorType != null ? new SqlSessionTemplate(sqlSessionFactory, executorType) : new SqlSessionTemplate(sqlSessionFactory);
}

到这里也就知道了MyBatis自动配置其实就是替我们完成了SqlSessionFactory和SqlSessionTempate的创建, 省去了自己导入相关依赖和配置相关Bean的麻烦.

MybatisLanguageDriverAutoConfiguration

这个类的配置是对各个语言的支持,比如Thymeleaf, Velocity,LegacyVelociy, FreeMarker等视图组件的支持。

@Configuration
@ConditionalOnClass({LanguageDriver.class})
public class MybatisLanguageDriverAutoConfiguration {
private static final String CONFIGURATION_PROPERTY_PREFIX = "mybatis.scripting-language-driver"; public MybatisLanguageDriverAutoConfiguration() {
} @Configuration
@ConditionalOnClass({ThymeleafLanguageDriver.class})
public static class ThymeleafConfiguration {
public ThymeleafConfiguration() {
}
} @Configuration
@ConditionalOnClass({VelocityLanguageDriver.class, VelocityLanguageDriverConfig.class})
public static class VelocityConfiguration {
public VelocityConfiguration() {
}
} @Configuration
@ConditionalOnClass({Driver.class})
@ConditionalOnMissingClass({"org.mybatis.scripting.velocity.VelocityLanguageDriverConfig"})
public static class LegacyVelocityConfiguration {
public LegacyVelocityConfiguration() {
}
} @Configuration
@ConditionalOnClass({FreeMarkerLanguageDriver.class, FreeMarkerLanguageDriverConfig.class})
public static class FreeMarkerConfiguration {
public FreeMarkerConfiguration() {
}
} @Configuration
@ConditionalOnClass({FreeMarkerLanguageDriver.class})
@ConditionalOnMissingClass({"org.mybatis.scripting.freemarker.FreeMarkerLanguageDriverConfig"})
public static class LegacyFreeMarkerConfiguration {
public LegacyFreeMarkerConfiguration() {
}
}
}

MybatisLanguageDriverAutoConfiguration类在org.mybatis.spring.boot.autoconfigure包下,我删掉了内部静态类下的代码,为了保持这个类看起来更直观

自定义Mapper是如何被扫描的

业务开发中,我们是声明接口(Mapper),那么我们自定义的Mapper是如何被扫描的呢, 我们继续顺着MybatisAutoConfiguration代码分析,其内部包含了一个AutoConfiguredMapperScannerRegistrar的内部静态类.

AutoConfiguredMapperScannerRegistrar

registerBeanDefinitions

public static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware, ImportBeanDefinitionRegistrar {
private BeanFactory beanFactory; public AutoConfiguredMapperScannerRegistrar() {
} public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (!AutoConfigurationPackages.has(this.beanFactory)) { } else {
//1.获取到SpringBoot的基础包路径
List<String> packages = AutoConfigurationPackages.get(this.beanFactory); //2.生成一个BeanDefinition的构造器,用于构建MapperScannerConfigurer的 //BeanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
//3.设置@Mapper注解的接口才会被当成Mapper接口
builder.addPropertyValue("annotationClass", Mapper.class); builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
//4.获取MapperScannerConfigurer的属性名称
Set<String> propertyNames = (Set)Stream.of(beanWrapper.getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet());
if (propertyNames.contains("lazyInitialization")) {
builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
} if (propertyNames.contains("defaultScope")) {
builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}");
}
//5.这里添加一个MapperScannerConfigurer的BeanDefinition对象,也就是注入一个
//MapperScannerConfigurer对象
registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
}
} public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
}

AutoConfiguredMapperScannerRegistrar类是MybatisAutoConfiguration的内部静态类,位于包org.mybatis.spring.boot.autoconfigure下。

可以看到这个类实现了ImportBeanDefinitionRegistrar接口,ImportBeanDefinitionRegistrar接口是Spring用来动态注册Bean的,也就是会向Spring容器中注入一个BeanDefinition, 这个BeanDefinition就是MapperScannerConfigurer。

ImportBeanDefinitionRegistrar实现类只能通过其他类@Import的方式来加载,通常是配置类或者启动类,所以MybatisAutoConfiguration类下还有一个内部类MapperScannerRegistrarNotFoundConfiguration如下。

@Configuration
@Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})
@ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean { }

这个方法里最后会调用接口BeanDefinitionRegistry.registerBeanDefinition, beanName是"org.mybatis.spring.mapper.MapperScannerConfigurer", registerBeanDefinition方法实际会调用DefaultListableBeanFactory.registerBeanDefinition。DefaultListableBeanFactory是BeanDefinitionRegistry接口的实现类。

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory,
BeanDefinitionRegistry,
Serializable { }

AutoConfiguredMapperScannerRegistrar类和MapperScanner注解的作用是一样的,如果你没有通过以下三种配置方式扫描Mapper接口的包路径

  • 配置MapperScannerConfigurer扫描器类型的Spring Bean
  • @Mapper注解
  • <mybatis: scan/>标签

那么这里就会通过AutoConfiguredMapperScannerRegistrar类添加一个MapperScannerConfigurer扫描器对象,去扫描SpringBoot包设置的基础包路径,也就是启动类的同级目录。 如果设置了@Mapper注解,则会当成Mapper接口解析,那么这里自动配置则不生效。

MapperScannerConfigurer

MapperScannerConfigurer

MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor 接口又继承了BeanFactoryPostProcessor接口, 也就是说在MapperScannerConfigurer类里需要实现这两个接口的方法。

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
} @FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}

在MapperScannerConfigurer类里可以看到这里只实现了postProcessBeanDefinitionRegistry。

BeanDefinitionRegistryPostProcessor

Spring里有两个用来动态注册Bean到容器中(BeanDefinitionRegistryPostProcessor和ImportBeanDefinitionRegistrar)。ImportBeanDefinitionRegistrar上文中有提到。

BeanDefinitionRegistryPostProcessor接口实现了BeanFactoryPostProcessor接口,是Spring框架的BeanDefinitionRegistry的后置处理器,用来注册额外的BeanDefinition,postProcessBeanDefinitionRegistry方法会在所有的beanDefinition被加载了,但是所有的Bean还没创建前调用。BeanDefinitionRegistryPostProcessor经常被用来注册BeanFactoryPostProcessor的BeanDefinition。

postProcessBeanDefinitionRegistry

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {

    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
this.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(this.lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(this.lazyInitialization));
} if (StringUtils.hasText(this.defaultScope)) {
scanner.setDefaultScope(this.defaultScope);
} scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
}
}

MapperScannerConfigurer在包org.mybatis.spring.mapper下.

这里会调用ClassPathMapperScanner.scan(),而ClassPathMapperScanner又继承了ClassPathBeanDefinitionScanner,所以这里scan()会调用ClassPathBeanDefinitionScanner.scan(), 而ClassPathBeanDefinitionScanner.scan() 第二句代码又调用了this.doScan(basePackages), this.doScan()又调用了ClassPathMapperScanner.doScan(), 而这个方法第一句代码又调用了super.doScan(basePackages),父子类来回互相调用,有点晕头转向的。

org.mybatis.spring.mapper.ClassPathMapperScanner

org.springframework.context.annotation.ClassPathBeanDefinitionScanner这个类在spring-context.jar

ClassPathMapperScanner

ClassPathBeanDefinitionScanner#scan

public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
this.doScan(basePackages);
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
} return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
}

ClassPathMapperScanner#doScan()

这个方法里在mybatis自动配置算比较重要的一个方法,也就是帮助我们自动配置MapperFactoryBean, 会把根据basePackage注册进Spring容器的BeanDefinition的beanClass设置成MapperFactoryBean。

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> {
return "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.";
});
} else {
//这是一个关键方法,会处理已经注册进Spring容器的beanDefinition,也就是会把
//已经注册进Spring容器的beanDefinitiond的beanClass为MapperFactoryBean
this.processBeanDefinitions(beanDefinitions);
} return beanDefinitions;
}

ClassPathBeanDefinitionScanner#doScan()

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet();
String[] var3 = basePackages;
int var4 = basePackages.length; for(int var5 = 0; var5 < var4; ++var5) {
String basePackage = var3[var5];
//这个方法会扫描指定basePackage下被@Mapper注解标注的接口
Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage);
Iterator var8 = candidates.iterator(); while(var8.hasNext()) {
BeanDefinition candidate = (BeanDefinition)var8.next();
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
//这里获取beanName, 默认值是类名首字母小写
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
this.postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName);
} if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate);
}
//检查对应的Mapper接口是否被注册进Spring容器中。
if (this.checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
this.registerBeanDefinition(definitionHolder, this.registry);
}
}
} //这个集合返回以后 Spring容器会将里面的所有内容注册到容器中
return beanDefinitions;
}

ClassPathMapperScanner#processBeanDefinitions

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
BeanDefinitionRegistry registry = this.getRegistry();
Iterator var4 = beanDefinitions.iterator(); while(var4.hasNext()) {
BeanDefinitionHolder holder = (BeanDefinitionHolder)var4.next();
AbstractBeanDefinition definition = (AbstractBeanDefinition)holder.getBeanDefinition();
boolean scopedProxy = false;
if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
definition = (AbstractBeanDefinition)Optional.ofNullable(((RootBeanDefinition)definition).getDecoratedDefinition()).map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> {
return new IllegalStateException("The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]");
});
scopedProxy = true;
} String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> {
return "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' mapperInterface";
});
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
//1.这里把普通接口设置成MapperFactoryBean
definition.setBeanClass(this.mapperFactoryBeanClass);
//2.是否把Mapper接口加入到Mybatis的Config当中去, 这里设置为true
definition.getPropertyValues().add("addToConfig", this.addToConfig);
definition.setAttribute("factoryBeanObjectType", beanClassName);
boolean explicitFactoryUsed = false; //2.从核心容器里获取SqlSessionFactory赋值给MapperFactoryBean
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;
} //3.从核心容器里获取SqlSessionTemplate赋值给MapperFactoryBean
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
LOGGER.warn(() -> {
return "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(() -> {
return "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.";
});
} definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
} if (!explicitFactoryUsed) {
LOGGER.debug(() -> {
return "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.";
});
definition.setAutowireMode(2);
} definition.setLazyInit(this.lazyInitialization);
if (!scopedProxy) {
if ("singleton".equals(definition.getScope()) && this.defaultScope != null) {
definition.setScope(this.defaultScope);
} if (!definition.isSingleton()) {
BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
registry.removeBeanDefinition(proxyHolder.getBeanName());
} registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
}
}
}
}



再看spring.factories文件

上面只是完成了MyBatis 自动配置的工作,那么这些自动配置是如何在SpringBoot启动时执行呢? 还记得SpringBoot的EnableAutoConfiguration注解吧,它是利用SpringFactoriesLoader机制加载所有的AutoConfiguration类,所以我们需要把编写好的自动配置类放在META-INF/spring.factories文件中,这也就是我们一开始分析的mybatis-spring-boot-starter-autoconfigure的文件结构的原因。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

总结

  1. mybatis-spring-boot-starter将mybatis需要的依赖全部引入。
  2. starter通过SPI机制引入了一个配置的Class(MyBatisAutoConfiguration)。它负责注册SqlSessionFactory和SqlSessionTemplate到Spring容器中,使用MyBatis开发时绝大部分功能要使用这两个类来完成。
  3. 注入了AutoConfiguredMapperScannerRegistrar这个Bean到Spring容器,它负责将MapperScanner引入到Spring容器,然后MapperScanner会将工程中指定package下的Mapper转化为BeanDefinition并且注册到Spring容器中。
  4. 在开发中使用某个具体的Mapper时,Spring能够从容器中找到这个Mapper对应的BeanDefinition,将其实例化并且注入,这样开发者就可以使用了。
  5. SpringBoot+MyBatis自动配置涉及到Spring中两个自动注册Bean的关键接口(BeanDefinitionRegistryPostProcessor和ImportBeanDefinitionRegistrar),也是我们业务开发中需要重点关注的地方。

参考链接

https://www.cnblogs.com/nullifier/p/11967659.html

https://zhuanlan.zhihu.com/p/30123517

https://www.cnblogs.com/daxin/p/3545040.html

MyBaits自动配置原理的更多相关文章

  1. spring boot实战(第十三篇)自动配置原理分析

    前言 spring Boot中引入了自动配置,让开发者利用起来更加的简便.快捷,本篇讲利用RabbitMQ的自动配置为例讲分析下Spring Boot中的自动配置原理. 在上一篇末尾讲述了Spring ...

  2. SpringBoot自动配置原理

    前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 回顾前面Spring的文章(以学习的顺序排好): S ...

  3. SpringBoot的自动配置原理过程解析

    SpringBoot的最大好处就是实现了大部分的自动配置,使得开发者可以更多的关注于业务开发,避免繁琐的业务开发,但是SpringBoot如此好用的 自动注解过程着实让人忍不住的去了解一番,因为本文的 ...

  4. 3. SpringBoot ——自动配置原理浅析

    SpringBoot的功能之所以强大,离不开它的自动配置这一大特色.但估计很多人只是知其然而不知其所以然.下面本人对自动配置原理做一个分析: 在使用SpringBoot时我们通过引入不同的Starte ...

  5. spring boot 自动配置原理

    1).spring boot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration,先看一下启动类的main方法 public ConfigurableApplic ...

  6. Spring Boot 自动配置原理(精髓)

    一.自动配置原理(掌握) SpringBoot启动项目会加载主配置类@SpringBootApplication,开启@EnableAutoConfiguration自动配置功能 @EnableAut ...

  7. Spring Boot自动配置原理、实战

    Spring Boot自动配置原理 Spring Boot的自动配置注解是@EnableAutoConfiguration, 从上面的@Import的类可以找到下面自动加载自动配置的映射. org.s ...

  8. 5. SprigBoot自动配置原理

      配置文件到底能写什么?怎么写? 都可以在SpringBoot的官方文档中找到: 配置文件能配置的属性参照   1.自动配置原理: 1).SpringBoot启动的时候加载主配置类,开启了自动配置功 ...

  9. 3springboot:springboot配置文件(外部配置加载顺序、自动配置原理,@Conditional)

    1.外部配置加载顺序 SpringBoot也可以从以下位置加载配置: 优先级从高到低 高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置  1.命令行参数 所有的配置都可以在命令行上进行指定 ...

随机推荐

  1. 攻防世界 reverse Newbie_calculations

    Newbie_calculations Hack-you-2014 题目名百度翻译成新手计算,那我猜应该是个实现计算器的题目.... IDA打开程序,发现一长串的函数反复调用,而且程序没有输入,只有输 ...

  2. java例题_50 题目:有五个学生,每个学生有 3 门课的成绩,从键盘输入以上数据(包括学生号,姓名,三门课成 绩),计算出平均成绩,将原有的数据和计算出的平均分数存放在磁盘文件"stud"中。

    1 /*50 [程序 50 文件 IO] 2 题目:有五个学生,每个学生有 3 门课的成绩,从键盘输入以上数据(包括学生号,姓名,三门课成 3 绩),计算出平均成绩,将原有的数据和计算出的平均分数存放 ...

  3. Android Studio 如何更新到新版本

    •How 打开 Android Studio 选择任务栏中的 File 点击 Setting: 来到如下界面: 依次点击  Appearance & Behavior -> System ...

  4. 为了效率,我们可以用的招数 之 strlen

    如果要你写一个计算字符串长度的函数 strlen,应该怎么写?相信你很容易写出如下实现: 1 int strlen_1(const char* str) { 2 int cnt = 0; 3 4 if ...

  5. Redis生产环境节点宕机问题报错及恢复排错

    Redis故障发现 主观下线 当cluster-node-timeout时间内某节点无法与另一个节点顺利完成ping消息通信时,则将该节点标记为主观下线状态. 客观下线 当某个节点判断另一个节点主观下 ...

  6. c 结构体内存对齐详解

    0x00简介 首先要知道结构体的对齐规制 1.第一个成员在结构体变量偏移量为0的地址处 2.其他成员变量对齐到某个数字的整数倍的地址处 对齐数=编辑器默认的一个对齐数与该成员大小的较小值 vs中默认的 ...

  7. Spring Boot 快速迁移至 Quarkus

    Quarkus 是一个目前非常火的 Java 应用开发框架,定位是轻量级的微服务框架.,Quarkus 提供了优秀的容器化整合能力,相较于传统开发框架(Spring Boot)有着更快的启动速度.更小 ...

  8. CppCon 2019 | Back to Basics: RAII and The Rule of Zero

    本文整理了Arthur O'Dwyer在CppCon 2019上关于RAII的演讲,演讲的slides可以在此链接进行下载. 在C++程序中,我们往往需要管理各种各样的资源.资源通常包括以下几种: A ...

  9. Vue CLI 是如何实现的 -- 终端命令行工具篇

    Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供了终端命令行工具.零配置脚手架.插件体系.图形化管理界面等.本文暂且只分析项目初始化部分,也就是终端命令行工具的实现. 0. 用法 ...

  10. WPF之小米Logo超圆角的实现

    某些新闻:小米logo换新,程序员一行代码(border-radius:19px)实现,目前此行代码价值200万 某程序员内心:所以还是因为我代码写太少了,所以这200万才没有我的份吗? 这事儿也成功 ...