一.整体介绍
1.什么是 Springboot?
Springboot 是一个全新的框架,简化 Spring 的初始搭建和开发过程,使用了特定的方式来进行配置,让开发人员不再需要定义样板化的配置。此框架不需要配置 xml,依赖于 maven 这样的构建系统。
简单、快速、方便的搭建项目;对主流开发框架的无配置集成;极大提高了开发、部署效率。
2.Springboot 的优点
- 提供一个快速的 Spring 项目搭建渠道。
- 开箱即用 ,很少的 Spring 配置就能运行一个 Java EE 项目 。
- 提供了生产级的服务监控方案。
- 内嵌服务器,可以快速部署。
- 提供了一系列非功能性的通用配置。
- 纯 Java 配置,没有代码生成,也不需要 XML 配置。
3.Springboot 的缺点
- 将现有或传统的 Spring Framework 项目转换为 Spring boot 应用程序是一个非常困难和耗时的过程。
- 使用简单, 学习成本高, 精通难。
- 版本迭代速度很快,一些模块改动很大。
- 由于不用自己做配置,报错时很难定位。
- 网上现成的解决方案比较少。
4.Springboot 和 Spring
-
springboot 可以建立独立的 spring 应用程序。 -
内嵌了如 tomcat,Jetty 和 Undertow 这样的容器,也就是说可以直接跑起来,用不着再做部署工作。 -
无需再像 spring 那样写一堆繁琐的 XML 配置文件 -
可以自动配置 spring -
提供的 POM 可以简化 maven 的配置
5.springboot 和 springMVC
- SpringMVC 是基于 spring 的一个 MVC 框架。
- springboot 的基于 spring 的条件注册的一套快速开发整合包。
6.为何可以快速集成?
答:因为启动器 start 包含一些依赖项,配置一下就可以用,所以快
再问:底层原理是啥?
基于观察者模式,启动后会把相关配置加入到 environment 对象,监听到对象后 springboot 去加载实现整体自动化,加载的内容也就是我们常说基于的那三个注解
7.SpringBoot 种启动方式?
1.main 启动
直接main方法启动DemoApplication
2.maven 启动
mvn spring-boot:run
3.jar 包
java -jar tx_demo2-0.0.1-SNAPSHOT.jar
4.docker 启动
FROM openjdk:8-jdk-alpine
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
EXPOSE 10001
ENTRYPOINT ["java","-jar","/app.jar"]
docker run -p 8080:8080 tx_demo2:1.0
8.Spring Boot devtools
Spring Boot 提供了一组开发工具 spring-boot-devtools 可以提高开发者的工作效率,开发者可以将该模块包含在任何项目中,spring-boot-devtools 最方便的地方莫过于热部署了。
要想在项目中加入 devtools 模块,只需添加相关依赖即可,代码如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
注意:这里多了 一个 optional 选项,是为了防止将 devtools 依赖传递到其他模块中。当开发者将应用打包运行后,devtools 会被自动禁用 当开发者将 spring-boot-devtools 引入项目后,只要 classpath 路径下发生变化,项目就会自动重启,这极大地提高了项目的开发速度。如果开发者使用 Eclipse ,那么在修改完代码并保存之后,项目将自动编译井触发重启,而开发者如果使用 IntelliJ IDEA 默认情况下,需要开发者手动编译才会触发重启。手动编译时,单击 Build -> Build Project 菜单或者按 Ctrl+F9 快捷键进行编译,编译成功后就会触发项目重启。当然,使用 IntelliJ IDEA 开发者也可以配置项目自动编译,配置步骤:
单击 File -> settings 菜单,打开 settings 页面,在左边的菜单栏依次找到 Build,Execution,Deployment -> Compile,勾选 Build project automatically 如图所示。
按住 Ctrl+Shift+Alt+/ 快捷捷键调出 Maintenance 页面
选择 Registry,在新打开的 Registry 页面中,勾选 compiler.automake.allow.when.app.running 复选框。配置完成后,若开发者修改了代码则将会自动重启。
注意:classpath 路径下的静态资源或者视图模板等发生变化时,并不会导致项目重启。
9.网络环境
SpringApplication 试图代表您创建正确类型的ApplicationContext 。用于确定WebApplicationType 的算法非常简单:
- 如果存在 Spring MVC,则使用
AnnotationConfigServletWebServerApplicationContext - 如果 Spring MVC 不存在且存在 Spring WebFlux,则使用
AnnotationConfigReactiveWebServerApplicationContext - 否则,使用
AnnotationConfigApplicationContext
这意味着如果您在同一个应用程序中使用 Spring MVC 和来自 Spring WebFlux 的新WebClient ,默认情况下将使用 Spring MVC。您可以通过调用setWebApplicationType(WebApplicationType) 轻松覆盖它。
也可以通过调用setApplicationContextClass(…) 来完全控制使用的ApplicationContext 类型。
10.如何解决循环依赖
从 2.6 版本开始,如果你的项目里还存在循环依赖,SpringBoot 将拒绝启动!
临时开启循环依赖
spring.main.allow-circular-references=true
二.配置
1.父依赖
其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件!
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/>
</parent>
点进去,发现还有一个父依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
这里才是真正管理 SpringBoot 应用里面所有依赖版本的地方,SpringBoot 的版本控制中心;
以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;
在没有父 POM 的情况下使用 Spring Boot
不是每个人都喜欢继承spring-boot-starter-parent POM。您可能拥有自己需要使用的公司标准父级,或者您可能希望明确声明所有 Maven 配置。
如果您不想使用spring-boot-starter-parent ,您仍然可以通过使用scope=import 依赖项来保持依赖项管理(但不是插件管理)的好处,如下所示:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
此时,就可以不用继承 spring-boot-starter-parent 了,但是 Java 的版本、编码的格式等都需要开发者手动配置。 Java 版本的配置很简单,添加一个 plugin 即可:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
编码格式
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncodin>UTF-8</project.reporting.outputEncodin>
</properties>
2.配置文件优先级
加载顺序
bootstrap.yml > bootstrap.properties > application.yml > application.properties
启动命令加 -D 参数名 = 参数值 覆盖配置文件中的配置
- SpringBoot 提供了 2 种配置文件类型:
properteis 和 yml/yaml - 默认配置文件名称:application
- 在同一级目录下优先级为:
properties > yml > yaml
3.导入其他配置类
你不需要将所有@Configuration 放入一个班级。@Import 注释可用于导入其他配置类。或者,您可以使用@ComponentScan 自动选取所有 Spring 组件,包括@Configuration 类。
4.导入 XML 配置
如果您绝对必须使用基于 XML 的配置,我们建议您仍然使用@Configuration 类。然后,您可以使用@ImportResource 注释来加载 XML 配置文件。
5.禁用特定的自动配置类
如果发现正在应用您不需要的特定自动配置类,则可以使用@EnableAutoConfiguration 的 exclude 属性禁用它们,如以下示例所示:
import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.jdbc.*;
import org.springframework.context.annotation.*;
@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class MyConfiguration {
}
如果类不在类路径上,则可以使用注释的excludeName 属性并指定完全限定名称。最后,您还可以使用spring.autoconfigure.exclude 属性控制要排除的自动配置类列表。
6.Spring Beans 和依赖注入
您可以自由使用任何标准 Spring 框架技术来定义 beans 及其注入的依赖项。为简单起见,我们经常发现使用@ComponentScan (找到你的 beans)和使用@Autowired (做构造函数注入)效果很好。
如果按照上面的建议构建代码(在根包中定位应用程序类),则可以添加@ComponentScan 而不带任何参数。您的所有应用程序组件(@Component ,@Service ,@Repository ,@Controller 等)都会自动注册为 Spring Beans。
@ComponentScan可以使用替代@Import({ MyConfig.class, MyAnotherConfig.class })
7.自定义横幅
通过将banner.txt 文件添加到类路径或将spring.banner.location 属性设置为此类文件的位置,可以更改启动时打印的横幅。如果文件的编码不是 UTF-8,则可以设置spring.banner.charset 。除了文本文件,您还可以将banner.gif ,banner.jpg 或banner.png 图像文件添加到类路径或设置spring.banner.image.location 属性。图像将转换为 ASCII 艺术表示,并打印在任何文本横幅上方。
Spring Boot 项目在启动时会打印一个 banner,这个 banner 是可以定制的,在 sources 目录下创建 banner.txt 文件,在这个文件中写入的文本将在项目启动时打印出来 如果想将 TXT 文本设成艺术字体,有以下几个在线网站可供参考
8.配置随机值
RandomValuePropertySource 对于注入随机值(例如,进入秘密或测试用例)非常有用。它可以生成整数,长整数,uuids 或字符串,如以下示例所示:
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}
random.int* 语法为OPEN value (,max) CLOSE ,其中OPEN,CLOSE 为任意字符,value,max 为整数。如果提供max ,那么value 是最小值,max 是最大值(不包括)。
9.特定于配置文件的属性
除了application.properties 文件之外,还可以使用以下命名约定来定义特定于配置文件的属性:application-{profile}.properties 。Environment 有一组默认配置文件(默认情况下为[default] ),如果未设置活动配置文件,则使用这些配置文件。换句话说,如果没有显式激活配置文件,则会加载application-default.properties 中的属性。
特定于配置文件的属性从标准application.properties 的相同位置加载,特定于配置文件的文件始终覆盖非特定文件,无论特定于配置文件的文件是在打包的 jar 内部还是外部。
如果指定了多个配置文件,则应用 last-wins 策略。例如,spring.profiles.active 属性指定的配置文件将在通过SpringApplication API 配置的配置文件之后添加,因此优先。
10.属性中的占位符
application.properties 中的值在使用时通过现有的Environment 进行过滤,因此您可以返回先前定义的值(例如,从系统属性中)。
app.name=MyApp
app.description=${app.name} is a Spring Boot application
11.YAML
- 大小写敏感
- 数据值前边必须有空格,作为分隔符
- 使用缩进表示层级关系
- 缩进时不允许使用 Tab 键,只允许使用空格(各个系统 Tab 对应的空格数目可能不同,导致层次混乱)。
- 缩进的空格数目不重要,只要相同层级的元素左侧收对齐即可
- #表示注释,从这个字符一直到行尾,都会被解析器忽略。
12.配置加载顺序
内部配置加载顺序
Springboot 程序启动时,会从以下位置加载配置文件:
-
file:./config/: 当前项目下的/config 目录下 -
file:./: 当前项目的根目录 -
classpath:/config/: classpath 的/config 目录 -
classpath:/: classpath 的根目录 (上面文档中的方式)
注意: 1、2 是不会打入项目的 jar 包的, 而 3、4 是会最终编入 jar 中。
加载顺序为上文的排列顺序,高优先级配置的属性会生效
指定位置加载配置文件
我们还可以通过 spring.config.location 来改变默认的配置文件位置
项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;这种情况,一般是后期运维做的多,相同配置,外部指定的配置文件优先级最高
java -jar spring-boot-config.jar --spring.config.location=F:/application.properties
外部配置的加载顺序
外部配置优先级高于内部的配置,内部配置是静态的可以看作是默认值,而外部配置是可以动态修改,这样极大提高灵活性。
三.注解
1.选择器注解
- @Conditional,当指定的条件都满足时,组件才被注册
- @ConditionalOnBean,指定 bean 在上下文中时,才注册当前 bean。用在方法上,则默认依赖类为方法的返回类型
- @ConditionalOnClass,指定类在 classpath 上时,才初始化当前 bean。用在方法上,则默认依赖类为方法的返回类型
- @ConditionalOnCloudPlatform,在指定云平台才注册配置
- @ConditionalOnExpression,指定 spel 为 true 时注册配置
- @ConditionalOnJava,在指定 java 版本时注册配置
- @ConditionalOnJndi
- @ConditionalOnMissingBean,指定 bean 不在上下文中时,才初始化当前 bean。用在方法上,则默认依赖类为方法的返回类型
- @ConditionalOnMissingClass,指定类不在 classpath 上时,才初始化当前 bean。用在方法上,则默认依赖类为方法的返回类型
- @ConditionalOnNotWebApplication,不是在 web 环境才注册配置
- @ConditionalOnProperty,配置文件中的值与指定值是否相等,相等才注册配置
- @ConditionalOnResource,指定 resources 都在 classpath 上才注册配置
- @ConditionalOnSingleCandidate,上下文中只有一个候选者 bean 时才注册配置
- @ConditionalOnWebApplication,是在 web 环境才注册配置
2.缓存注解
- @EnableCaching,开启缓存配置,支持子类代理或者 AspectJ 增强
- @CacheConfig,在一个类下,提供公共缓存配置
- @Cacheable,放着方法和类上,缓存方法或类下所有方法的返回值
- @CachePut,每次先执行方法,再将结果放入缓存
- @CacheEvict,删除缓存
- @Caching,可以配置@Cacheable、@CachePut、@CacheEvict
3.定时器注解
- @EnableScheduling,开启定时任务功能
- @Scheduled,按指定执行周期执行方法
- @Schedules,包含多个@Scheduled,可同时运行多个周期配置
- @EnableAsync,开启方法异步执行的能力,通过@Async 或者自定义注解找到需要异步执行的方法。通过实现 AsyncConfigurer 接口的 getAsyncExecutor()和 getAsyncUncaughtExceptionHandler()方法自定义 Executor 和异常处理。
- @Async,标记方法为异步线程中执行
4.注入配置文件
- @EnableConfigurationProperties,启动@ConfigurationProperties 功能
- @ConfigurationProperties,将 properties 文件里的内容,自动注入 bean 对应的属性中
- @DeprecatedConfigurationProperty,用在配置文件的 getter()方法上,标记字段已经过期,并提示替换的字段。一般给 spring-boot-configuration-processor 使用。
- @NestedConfigurationProperty,标记在配置文件的字段上,提示 spring-boot-configuration-processor,配置包含嵌套的配置。
- spring-configuration-metadata.json 提供配置的元信息,在写 properties 配置时,会有语法提示。在项目中引入 spring-boot-configuration-processor 项目,会扫描@ConfigurationProperties 注解,自动生成 spring-configuration-metadata.json
四.常见问题
1.@SpringBootApplication
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}
首先是四个元注解
- @Target(ElementType.TYPE): 标志该注解可以作用于 interface、class、enum。
- @Retention(RetentionPolicy.RUNTIME): 注解的保留策略;
- 当前注解会出现在.class 字节码文件中
- 并且在运行的时候可以通过反射被获得。
- @Documented: 标志该注解可以被 javadoc 工具所引用。
- @Inherited: 标志该注解可以被继承。
核心注解
@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan
@SpringBootConfiguration 它继承了@Configuration;Spring3.0 开始增加了@Configuration、@Bean 注解,
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
@ComponentScan 与@Component 这一类的注解配合使用,相当于以前 xml 配置中开启自动扫描的 context:component-scan 标签,可以配置 Spring 的扫描范围、Bean 的 ID 命名策略等属性;
- 默认扫描范围为当前目录下的所有 class,
- 这也是 SpringBoot 入口类要放在代码根目录的原因。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {}
@ComponentScan 会扫描并装配代码根目录下的,所有的带@Component 注解的 class;但是 SpringBoot 如何加载代码根目录之外的 Bean,比方说 classpath 下 maven 依赖中的; 这个时候就需要了解一下 META-INF/spring.factories 文件的作用了
@import :Spring 底层注解@import , 给容器中导入一个组件
@Import({AutoConfigurationImportSelector.class}) :给容器导入组件 ;
AutoConfigurationImportSelector :自动配置导入选择器
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
单个@SpringBootApplication 注释可用于启用这三个功能,即:
@EnableAutoConfiguration :启用Spring Boot 的自动配置机制@ComponentScan :对应用程序所在的软件包启用@Component 扫描(请参阅最佳实践)@Configuration :允许在上下文中注册额外的 beans 或导入其他配置类
@SpringBootApplication 注释等效于使用@Configuration ,@EnableAutoConfiguration 和@ComponentScan 及其默认属性
META-INF/spring.factories ,核心配置文件,主要加载 maven 引入的包的类
结论: springboot 所有自动配置都是在启动的时候扫描并加载, spring.factories 所有的自动配置类都在这里面
但是不一定生效, 要@ConditionalonXXXaa条件成立
2.加载整体流程?
3.starter 是如何被加载的?
-
springboot 在启动的时候, 从类路径下 META-INF/spring.factories 获取指定的值 -
将这些自动配置的类导入容器, 自动配置就会生效, 帮我们进行自动配置 -
以前是需要自己配置的东西, 现在 springboot 帮忙做 -
整合 javaEE, 解决方案和自动配置的东西都在 spring-boot-autoconfigure-2.0.0.RELEASE.jar 包下 -
它会把所有需要导入的组件, 已类名的方式返回,这些组件就会被添加到容器.
run 方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
listeners.running(context);
return context;
}
获取 Spring 工厂
SpringApplication
这个类主要做了以下四件事情:
- 推断应用的类型是普通的项目还是 Web 项目
- 查找并加载所有可用初始化器 , 设置到 initializers 属性中
- 找出所有的应用程序监听器,设置到 listeners 属性中
- 推断并设置 main 方法的定义类,找到运行的主类
public class SpringApplication {
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();
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
}
SpringFactoriesLoader
public abstract class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
}
在获取到所有的接口之后,就会调用 getSpringFactoriesInstances()创建接口的实现类。
spring.factories 就像是工厂一样配置了大量的接口对应的实现类,我们通过这些配置 + 反射处理就可以拿到相应的实现类。这种类似于插件式的设计方式,只要引入对应的 jar 包,那么对应的 spring.factories 就会被扫描到,对应的实现类也就会被实例化,如果不需要的时候,直接把 jar 包移除即可。
4.Starter 的实现原理?
Starters 是一组方便的依赖描述符,您可以在应用程序中包含这些描述符。您可以获得所需的所有 Spring 和相关技术的一站式服务,而无需搜索示例代码和复制粘贴依赖描述符的负载。例如,如果要开始使用 Spring 和 JPA 进行数据库访问,请在项目中包含spring-boot-starter-data-jpa 依赖项。
启动器包含许多依赖项,这些依赖项是使项目快速启动和运行所需的依赖项,以及一组受支持的托管传递依赖项。
如何命名
所有官方首发都遵循类似的命名模式; spring-boot-starter-* ,其中* 是一种特殊类型的应用程序。此命名结构旨在帮助您找到启动器。许多 IDE 中的 Maven 集成允许您按名称搜索依赖项。例如,安装了适当的 Eclipse 或 STS 插件后,可以在 POM 编辑器中按ctrl-space 并输入“spring-boot-starter”以获取完整列表。
正如“ 创建自己的初学者 ”部分所述,第三方启动者不应以spring-boot 开头,因为它是为官方 Spring Boot 工件保留的。相反,第三方启动器通常以项目名称开头。例如,名为thirdpartyproject 的第三方启动项目通常被命名为thirdpartyproject-spring-boot-starter 。
命名归约:
官方命名:
- 前缀:spring-boot-starter-xxx
- 比如:spring-boot-starter-web…
自定义命名:
- xxx-spring-boot-starter
- 比如:mybatis-spring-boot-starter
启动器:
- 启动器: 说白了就是 springboot 的启动场景(可以理解特定场景的整套解决方案)
- 比如 spring-boot-starter-web, 它就会帮我们自动导入 web 环境所有的依赖
- springboot 会讲所有的功能场景, 都变成一个个的启动器
- 我们要用什么功能, 就只需要找到对应的启动器就可以了.
比如:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
5.自定义 starter
我们的多项目权限是如何做的?只需要一个登录校验即可解决,也就是自定义的 starter
1.需要封装的代码
@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {
@Autowired
HelloProperties helloProperties;
@Bean
public HelloService helloService(){
HelloService service = new HelloService();
service.setHelloProperties(helloProperties);
return service;
}
}
2.spring.factories
META-INF\spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.kuang.HelloServiceAutoConfiguration
作用:EnableAutoConfiguration 它用来记录 jar 包中需要注册到容器中的 Bean 的 qualified name,这样可以通过反射获取到对应的 class
AutoConfigurationImportSelector 它里边有一个方法:getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes),作用是加载这些定义在 spring.factories 中的 Bean。对应@ConditionalOnWebApplication
3.安装到 maven 仓库
6.@Scheduled 不生效
if neither of two is resolvable, a local single-thread default scheduler will be created within in the registrar.
这句话意味着,如果我们不主动配置我们需要的 TaskScheduler,SpringBoot 会默认使用一个单线程的 scheduler 来处理我们用@Scheduled 注解实现的定时任务,到此我们刚才的问题就可以理解了。
因为是单个线程执行所有的定时任务,所有 task2 如果先执行,因为执行中等待了 5s,所以 task2 执行完后,task1 接着继续执行。
|