SpringBoot
学习目标
1.掌握原理,自己手写starter
2.yaml语法,操作数据库
3.连接前端
4.补充技能,前后端分离,swagger, 异步,富文本编辑器
5.异步任务,定时任务,邮件任务等等
- src 路径下的文件 在编译后都会放到 WEB-INF/classes 路径下。默认classpath 就是指这里。
- 用maven构建 项目时,resources 目录就是默认的classpath
什么是spring
现代化的java开发主要就是面向Spring开发:
Spring是一个开源框架,2003年起来的,可以简化我们的企业级开发
Spring是如何简化开发的
1.java bean 代表原来要一个个new的对象
2.IOC DI 控制反转,所有的对象都是从容器中获取的
3.AOP 面向切面编程,降低入侵性
4.xxxTemplate: 原来手动需要编写步骤,比如jdbcTemplate ,现在直接使用注解即可
什么是SpringBoot
SpringBoot: 自动配置简化开发
1.自动配置
2.不是一个新东西,是spring的增强版
3.约定大于配置(
开发人员仅需规定应用中不符合约定的部分
在没有规定配置的地方,采用默认配置,以力求最简配置为核心思想上面
两条都遵循了推荐默认配置的思想。当存在特殊需求的时候,自定义配置即可。这样可以大大的减少配置工作,这就是所谓的“约定”。)
4.继承了市面上所有常见的依赖,而且帮你自动管理版本依赖(可以从maven依赖库中寻找,地址是:https://mvnrepository.com/)
5.内置web容器
构建一个springBoot应用
1.new Project–Spring initialize --> next等
2.启动测试 有一个默认请求error
对比一下传统的开发
SSM框架:
1.tomcat
2.web.xml中注册springmvc
3.添加或者扫描我们需要的依赖是否存在,不存在就需要配置
4.编写spring springmvc mybatis等的配置
5.编写无关的业务代码 控制器等
6.整体框架搭建完毕之后,才开始梳理编写业务
7.测试,调整
SpringBoot:
1.构建项目 一键生成
2.编写业务
3.启动测试
如何发布
1.使用package打包即可 需要maven依赖
初探原理
分析思路: 只要是maven项目,都需要先分析pom.xml
pom.xml中,parent是父依赖,然后下一个是gav坐标 然后下面是具体的依赖 build是插件
1.父依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
点击进去之后就可以看到:z这就是springboot的配置文件
<includes>
<include>**/application*.yml</include>
<include>**/application*.yaml</include>
<include>**/application*.properties</include>
</includes>
我们发现这个还有一个父依赖 这个是版本控制和依赖控制中心,以后导入的依赖都不需要编写版本号了,如果自动依赖配置中不存在的话,物品,恶魔才需要手动导入
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
2.spring-boot-starter启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
作用:
1.导入依赖
2.存在哪些启动器:https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/html/using-spring-boot.html#using-boot-starter
3.插件
<!-- 这个插件,可以将应用打包成一个可执行的jar包;-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
启动类
@SpringBootApplication
public class SpringbootFirstApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootFirstApplication.class, args);
System.out.println("hello wprld");
}
核心:一个注解,一个方法 是使用图来画
@SpringBootApplication注解
作用:标注了这个主角,就代表这是我们的一个程序的启动类, 是一个配置类,一个组件
java中元注解有四个: @Retention @Target @Document @Inherited;
@Retention:注解的保留位置
@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Target:注解的作用目标
@Target(ElementType.TYPE) //接口、类、枚举
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR) //构造函数
@Target(ElementType.LOCAL_VARIABLE)//局部变量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) ///包
@Document:说明该注解将被包含在javadoc中 @Inherited:说明子类可以继承父类中的该注解
@Target({ElementType.TYPE}) //接口、类、枚举
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到@Target:注解的作用目标
@Documented 说明该注解将被包含在javadoc中
@Inherited 说明子类可以继承父类中的该注解
@SpringBootConfiguration 是一个配置类
@EnableAutoConfiguration
@ComponentScan(//扫描当前类所在的包中的下面的所有的bean,然后将这些bean注入到容器中
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
- @SpringBootConfiguration 是一个配置类 是一个组件
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
2.@EnableAutoConfiguration 自动配置的注解 是一个重点
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
AutoConfigurationPackage 注解:
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
//注册了一个包名,表示的是主要是在这个包名下面就会被注册到容器中
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
@Import({AutoConfigurationImportSelector.class})
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
//重点
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
如何分析一个类: 构造器和初始化常用方法 ,如果有继承,就重点查看重写的方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
META-INF/spring.factories
我们可以找到这个文件在
然后打开这个文件,就可以i看到自动配置的类
我们可以点击一个配置类:
那么这个文件是如何作用的呢?
主配置类中的run方法
首先找到SpringApplication这个类,然后看这个类的构造方法:
构造器
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//推断当前的应用是否是web应用
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//加载初始化器
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设置监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//推断main方法
this.mainApplicationClass = this.deduceMainApplicationClass();
}
SpringApplication:
1.推断当前的应用是否是web应用
2.加载初始化器
3.设置监听器
4.推断main方法
/**
2 * Run the Spring application, creating and refreshing a new
3 * {@link ApplicationContext}.
4 *
5 * @param args the application arguments (usually passed from a Java main method)
6 * @return a running {@link ApplicationContext}
7 *
8 * 运行spring应用,并刷新一个新的 ApplicationContext(Spring的上下文)
9 * ConfigurableApplicationContext 是 ApplicationContext 接口的子接口。在 ApplicationContext
10 * 基础上增加了配置上下文的工具。 ConfigurableApplicationContext是容器的高级接口
11 */
12 public ConfigurableApplicationContext run(String... args) {
13 //记录程序运行时间
14 StopWatch stopWatch = new StopWatch();
15 stopWatch.start();
16 // ConfigurableApplicationContext Spring 的上下文
17 ConfigurableApplicationContext context = null;
18 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
19 configureHeadlessProperty();
20 //从META-INF/spring.factories中获取监听器
21 //1、获取并启动监听器
22 SpringApplicationRunListeners listeners = getRunListeners(args);
23 listeners.starting();
24 try {
25 ApplicationArguments applicationArguments = new DefaultApplicationArguments(
26 args);
27 //2、构造应用上下文环境
28 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
29 //处理需要忽略的Bean
30 configureIgnoreBeanInfo(environment);
31 //打印banner 也可以自定义启动logo,比如在resources路径下创建一个banner.txt文件,将你想打印的图标放入其中
32 Banner printedBanner = printBanner(environment);
33 ///3、初始化应用上下文
34 context = createApplicationContext();
35 //实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
36 exceptionReporters = getSpringFactoriesInstances(
37 SpringBootExceptionReporter.class,
38 new Class[]{ConfigurableApplicationContext.class}, context);
39 //4、刷新应用上下文前的准备阶段
40 prepareContext(context, environment, listeners, applicationArguments, printedBanner);
41 //5、刷新应用上下文
42 refreshContext(context);
43 //刷新应用上下文后的扩展接口
44 afterRefresh(context, applicationArguments);
45 //时间记录停止
46 stopWatch.stop();
47 if (this.logStartupInfo) {
48 new StartupInfoLogger(this.mainApplicationClass)
49 .logStarted(getApplicationLog(), stopWatch);
50 }
51 //发布容器启动完成事件
52 listeners.started(context);
53 callRunners(context, applicationArguments);
54 } catch (Throwable ex) {
55 handleRunFailure(context, ex, exceptionReporters, listeners);
56 throw new IllegalStateException(ex);
57 }
58
59 try {
60 listeners.running(context);
61 } catch (Throwable ex) {
62 handleRunFailure(context, ex, exceptionReporters, null);
63 throw new IllegalStateException(ex);
64 }
65 return context;
66 }
具体的每一行代码的含义请看注释,我们在这先总结一下启动过程中的重要步骤:
第一步:获取并启动监听器
第二步:构造应用上下文环境
第三步:初始化应用上下文
第四步:刷新应用上下文前的准备阶段
第五步:刷新应用上下文
第六步:刷新应用上下文后的扩展接口
第一步:获取并启动监听器
事件机制在Spring是很重要的一部分内容,通过事件机制我们可以监听Spring容器中正在发生的一些事件,同样也可以自定义监听事件。Spring的事件为Bean和Bean之间的消息传递提供支持。当一个对象处理完某种任务后,通知另外的对象进行某些处理,常用的场景有进行某些操作后发送通知,消息、邮件等情况。
1 private SpringApplicationRunListeners getRunListeners(String[] args) {
2 Class<?>[] types = new Class<?>[]{SpringApplication.class, String[].class};
3 return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
4 SpringApplicationRunListener.class, types, this, args));
5 }
在这里面是不是看到一个熟悉的方法:getSpringFactoriesInstances(),可以看下下面的注释,前面的博文我们已经详细介绍过该方法是怎么一步步的获取到META-INF/spring.factories中的指定的key的value,获取到以后怎么实例化类的。
(javascript:void(0)😉
1 /**
2 * 通过指定的classloader 从META-INF/spring.factories获取指定的Spring的工厂实例
3 * @param type
4 * @param parameterTypes
5 * @param args
6 * @param <T>
7 * @return
8 */
9 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
10 Class<?>[] parameterTypes, Object... args) {
11 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
12 // Use names and ensure unique to protect against duplicates
13 //通过指定的classLoader从 META-INF/spring.factories 的资源文件中,
14 //读取 key 为 type.getName() 的 value
15 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
16 //创建Spring工厂实例
17 List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
18 classLoader, args, names);
19 //对Spring工厂实例排序(org.springframework.core.annotation.Order注解指定的顺序)
20 AnnotationAwareOrderComparator.sort(instances);
21 return instances;
22 }
[](javascript:void(0)😉
回到run方法,debug这个代码 SpringApplicationRunListeners listeners = getRunListeners(args); 看一下获取的是哪个监听器:
EventPublishingRunListener监听器是Spring容器的启动监听器。
listeners.starting(); 开启了监听事件。
四、第二步:构造应用上下文环境
应用上下文环境包括什么呢?包括计算机的环境,Java环境,Spring的运行环境,Spring项目的配置(在SpringBoot中就是那个熟悉的application.properties/yml)等等。
首先看一下prepareEnvironment()方法。
[](javascript:void(0)😉
1 private ConfigurableEnvironment prepareEnvironment(
2 SpringApplicationRunListeners listeners,
3 ApplicationArguments applicationArguments) {
4 // Create and configure the environment
5 //创建并配置相应的环境
6 ConfigurableEnvironment environment = getOrCreateEnvironment();
7 //根据用户配置,配置 environment系统环境
8 configureEnvironment(environment, applicationArguments.getSourceArgs());
9 // 启动相应的监听器,其中一个重要的监听器 ConfigFileApplicationListener 就是加载项目配置文件的监听器。
10 listeners.environmentPrepared(environment);
11 bindToSpringApplication(environment);
12 if (this.webApplicationType == WebApplicationType.NONE) {
13 environment = new EnvironmentConverter(getClassLoader())
14 .convertToStandardEnvironmentIfNecessary(environment);
15 }
16 ConfigurationPropertySources.attach(environment);
17 return environment;
18 }
[](javascript:void(0)😉
看上面的注释,方法中主要完成的工作,首先是创建并按照相应的应用类型配置相应的环境,然后根据用户的配置,配置系统环境,然后启动监听器,并加载系统配置文件。
4.1、 ConfigurableEnvironment environment = getOrCreateEnvironment();
看看getOrCreateEnvironment()干了些什么。
[](javascript:void(0)😉
1 private ConfigurableEnvironment getOrCreateEnvironment() {
2 if (this.environment != null) {
3 return this.environment;
4 }
5 //如果应用类型是 SERVLET 则实例化 StandardServletEnvironment
6 if (this.webApplicationType == WebApplicationType.SERVLET) {
7 return new StandardServletEnvironment();
8 }
9 return new StandardEnvironment();
10 }
[](javascript:void(0)😉
通过代码可以看到根据不同的应用类型初始化不同的系统环境实例。前面咱们已经说过应用类型是怎么判断的了,这里就不在赘述了。
从上面的继承关系可以看出,StandardServletEnvironment是StandardEnvironment的子类。这两个对象也没什么好讲的,当是web项目的时候,环境上会多一些关于web环境的配置。
4.2、 configureEnvironment(environment, applicationArguments.getSourceArgs());
[](javascript:void(0)😉
1 protected void configureEnvironment(ConfigurableEnvironment environment,
2 String[] args) {
3 // 将main 函数的args封装成 SimpleCommandLinePropertySource 加入环境中。
4 configurePropertySources(environment, args);
5 // 激活相应的配置文件
6 configureProfiles(environment, args);
7 }
[](javascript:void(0)😉
在执行完方法中的两行代码后,debug的截图如下
如下图所示,我在spring的启动参数中指定了参数:–spring.profiles.active=prod(关于这个参数的用法,点我,其实就是启动多个实例用的)
在configurePropertySources(environment, args);中将args封装成了SimpleCommandLinePropertySource并加入到了environment中。
configureProfiles(environment, args);根据启动参数激活了相应的配置文件。
话不多说,debug一遍就明白了。
4.3、 listeners.environmentPrepared(environment);
进入到方法一路跟下去就到了SimpleApplicationEventMulticaster类的multicastEvent()方法。
查看getApplicationListeners(event, type)执行结果,发现一个重要的监听器ConfigFileApplicationListener。
先看看这个类的注释
[](javascript:void(0)😉
1 /**
2 * {@link EnvironmentPostProcessor} that configures the context environment by loading
3 * properties from well known file locations. By default properties will be loaded from
4 * 'application.properties' and/or 'application.yml' files in the following locations:
5 * <ul>
6 * <li>classpath:</li>
7 * <li>file:./</li>
8 * <li>classpath:config/</li>
9 * <li>file:./config/:</li>
10 * </ul>
11 * <p>
12 * Alternative search locations and names can be specified using
13 * {@link #setSearchLocations(String)} and {@link #setSearchNames(String)}.
14 * <p>
15 * Additional files will also be loaded based on active profiles. For example if a 'web'
16 * profile is active 'application-web.properties' and 'application-web.yml' will be
17 * considered.
18 * <p>
19 * The 'spring.config.name' property can be used to specify an alternative name to load
20 * and the 'spring.config.location' property can be used to specify alternative search
21 * locations or specific files.
22 * <p>
23 * 从默认的位置加载配置文件,并将其加入 上下文的 environment变量中
24 */
[](javascript:void(0)😉
这个监听器默认的从注释中
- 标签所示的几个位置加载配置文件,并将其加入 上下文的 environment变量中。当然也可以通过配置指定。
debug跳过 listeners.environmentPrepared(environment); 这一行,查看environment属性,果真如上面所说的,配置文件的配置信息已经添加上来了。
五、第三步:初始化应用上下文
在SpringBoot工程中,应用类型分为三种,如下代码所示。
[](javascript:void(0)😉
1 public enum WebApplicationType {
2 /**
3 * 应用程序不是web应用,也不应该用web服务器去启动
4 */
5 NONE,
6 /**
7 * 应用程序应作为基于servlet的web应用程序运行,并应启动嵌入式servlet web(tomcat)服务器。
8 */
9 SERVLET,
10 /**
11 * 应用程序应作为 reactive web应用程序运行,并应启动嵌入式 reactive web服务器。
12 */
13 REACTIVE
14 }
[](javascript:void(0)😉
对应三种应用类型,SpringBoot项目有三种对应的应用上下文,我们以web工程为例,即其上下文为AnnotationConfigServletWebServerApplicationContext。
[](javascript:void(0)😉
1 public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
2 + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
3 public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
4 + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
5 public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
6 + "annotation.AnnotationConfigApplicationContext";
7
8 protected ConfigurableApplicationContext createApplicationContext() {
9 Class<?> contextClass = this.applicationContextClass;
10 if (contextClass == null) {
11 try {
12 switch (this.webApplicationType) {
13 case SERVLET:
14 contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
15 break;
16 case REACTIVE:
17 contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
18 break;
19 default:
20 contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
21 }
22 } catch (ClassNotFoundException ex) {
23 throw new IllegalStateException(
24 "Unable create a default ApplicationContext, "
25 + "please specify an ApplicationContextClass",
26 ex);
27 }
28 }
29 return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
30 }
[](javascript:void(0)😉
我们先看一下AnnotationConfigServletWebServerApplicationContext的设计。
关于他的继承体系,我们在前面的博客中<Spring IoC容器与应用上下文的设计与实现>已经详细介绍了,在此不再赘述。
应用上下文可以理解成IoC容器的高级表现形式,应用上下文确实是在IoC容器的基础上丰富了一些高级功能。
应用上下文对IoC容器是持有的关系。他的一个属性beanFactory就是IoC容器(DefaultListableBeanFactory)。所以他们之间是持有,和扩展的关系。
接下来看GenericApplicationContext类
[](javascript:void(0)😉
1 public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
2 private final DefaultListableBeanFactory beanFactory;
3 ...
4 public GenericApplicationContext() {
5 this.beanFactory = new DefaultListableBeanFactory();
6 }
7 ...
8 }
[](javascript:void(0)😉
beanFactory正是在AnnotationConfigServletWebServerApplicationContext实现的接口GenericApplicationContext中定义的。在上面createApplicationContext()方法中的, BeanUtils.instantiateClass(contextClass) 这个方法中,不但初始化了AnnotationConfigServletWebServerApplicationContext类,也就是我们的上下文context,同样也触发了GenericApplicationContext类的构造函数,从而IoC容器也创建了。仔细看他的构造函数,有没有发现一个很熟悉的类DefaultListableBeanFactory,没错,DefaultListableBeanFactory就是IoC容器真实面目了。在后面的refresh()方法分析中,DefaultListableBeanFactory是无处不在的存在感。
debug跳过createApplicationContext()方法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OLX7pbFe-1586245906728)(D:\艾编程的高级课程\资料\3.14 SpringBoot入门\springboot基础.assets\1635748-20190613154908779-1708037422.png)]
如上图所示,context就是我们熟悉的上下文(也有人称之为容器,都可以,看个人爱好和理解),beanFactory就是我们所说的IoC容器的真实面孔了。细细感受下上下文和容器的联系和区别,对于我们理解源码有很大的帮助。在系列文章中,我们也是将上下文和容器严格区分开来的。
YAML学习
Springboot默认使用全局的配置文件:
application*.properties key=value
application*.yaml/application*.yml key: 空格 value
配置文件的作用,修改springboot自动配置的默认值,但是其实很多配置的springboot都已经配置好了
yaml语法
YAML是"YAML Ain’t a Markup Language"(YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言),但为了强调这种语言以数据做为中心,而不是以标记语言为重点,而用反向缩略语重命名。
注入配置文件
加载指定的配置文件
@PropertySource("classpath:application.yaml") //加载资源的注解
yaml配置文件可以编写一些随机数, 占位符
person:
lastName: 张三${random.int}
三元运算符也是支持的
dog:
name: ${person.boss:true}12 如果存在这个值就是这个值,如果不存在的话就是true
在springboot底层所有的配置类都使用的是@ConfigurationProperties
最佳实践
1.假设配置类十分简单,就是使用@Value 如果只获取个别值,那么@Value 依旧是最高效的
2.配置繁琐的话,使用@ConfigurationProperties
多环境配置
什么是多环境配置
在真是开发的时候,配置文件一定不止一个,比如:
application-dev.yaml
application-test.yaml
application-prod.yaml
传统的多环境配置
spring.profiles.active=dev 表示启动的是application-dev.properties文件
spring.profiles.active=test 表示启动的是application-test.properties文件
使用yaml多环境配置
不需要要多个配置文件,只需要多文档快 使用—来代表一块 当然也可以使用多个文档来配置的,同上
---
server:
port: 8080
spring:
profiles:
active: 什么也不写表示的是默认的,协商dev就代表启动的是dev文档快
# 使用---来分割文档快
---
server:
port: 8081
spring:
profiles:
active:dev
---
server:
port: 8082
spring:
profiles:
active:test
自动配置原理
我们可以配置的文件内容十分之多,到底是怎么配置的呢?
原理
@Configuration
@EnableConfigurationProperties({HttpProperties.class}) 对应我们编写的配置文件 假设配置文件中有就用配置文件中的值,没有就默认的
@ConditionalOnWebApplication( spring的注解 判断类型是不是servlet
type = Type.SERVLET
)
@ConditionalOnClass({CharacterEncodingFilter.class}) 判断是否存在这个类
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
精髓
1.springboot启动会加载大量的自动配置类,从spring.factories中读取出来的
2.需要判断我们的类是否存在这里面,如如果不存在的话需要手动导入,如果存在导入启动器就可以了
3.我们的配置文件之所以可以自动配置生效:
xxxAutoConfiguration 自动配置类
xxxProperties封装配置文件中的相关属性
4.我们给容器中自动配置类配置属性的时候,会通过xxxProperties类来获取某些用户配置文件中的属性,如果没有的话则使用默认的,如果有则使用自动配置的
提高:自己定义一个starter
我们分析了源码以及自动装配的过程,我们可以尝试来自己开发一个启动器玩玩
说明
启动器:jar包
命名规则:
官方命名: spring-boot-starter-xxx.jar
自己命名:
xxx-spring-boot-starter
mybatis-spring-boot-starter
自己开发一个starter步骤
1.启动器其实是两个相互依赖的项目,一个是控制版本的(基础设施),一个是提供服务的
2.构建一个empty项目,里面有两个module,一个是maven项目,表示的是starter,一个是springboot项目表示的是autoconfigure,当然了两个项目既可以都是maven项目,也可以是springboot项目,一个maven,一个springboot也是可以的。
这样就创建好了两个module
第三步:将第二个的autoconfigure的pom/xml中的自己的标识放进第一个starter中,表示这个starter的配置依赖是autoconfigure.
在configure项目中pom.xml中需要剔除一个打包用的插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
我们不能使用springboot的方式打包,而是使用maven原始的方式打包,因为我们现在做的是依赖,而不是sprinboot项目,所以我们需要遵照maven的规则来。
在configure项目中删掉test目录及其下面所有的东西,这个我们不需要的
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>hl-spring-boot-starter-autoconfigure</groupId>
<artifactId>hl-spring-boot-starter-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>hl-spring-boot-starter-autoconfigure</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>hl-spring-boot-starter</groupId>
<artifactId>hl-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>hl-spring-boot-starter-autoconfigure</groupId>
<artifactId>hl-spring-boot-starter-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
starter文件里面需要改的地方就是pom.xml文件,只需要将configure项目当作依赖导入进来即可。
编写UserInfoProperties类 这个类来和配置文件相关联:
@ConfigurationProperties(
prefix = "user.info"
)
public class UserInfoProperties {
private String name;
private int age;
private String role;
....setter和getter方法
}
编写Service类:
@Component
public class UserInfoService {
private UserInfoProperties userInfoProperties;
public UserInfoService(UserInfoProperties userInfoProperties){
this.userInfoProperties = userInfoProperties;
}
public String getName(){
return userInfoProperties.getName();
}
public String getRole(){
return userInfoProperties.getRole();
}
public int getAge(){
return userInfoProperties.getAge();
}
}
编写autoConfigure类:
@Configuration
@ConditionalOnClass({UserInfoService.class}) 当类路径classpath下有指定的类的情况下进行自动配置
@EnableConfigurationProperties({UserInfoProperties.class}) 将配置类关联进来
public class UserInfoAutoConfiguration {
@Autowired
UserInfoProperties userInfoProperties;
@Bean
@ConditionalOnMissingBean(UserInfoService.class)
public UserInfoService getLogonInfo(){
UserInfoService u = new UserInfoService(userInfoProperties);
return u;
}
}
然后在autoconfigure项目的resource中新建一个META-INF文件夹,创建spring.factories文件,在该文件中配置自己的自动配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=hlspringboot.hlspringbootstarterautoconfigure.test.UserInfoAutoConfiguration 就是xxxAutoConfiguration的路径
然后打开Maven菜单:将这两个module导报一下:
然后我们打开自己的仓库就可以看到这两个jar包了:
如果想要其他的项目访问这两个jar包的话,那么需要有一个前提就是这两个的maven仓库是一样的,就是需要在idea中配置maven的信息: 红框中的地址需要一致,但是如果别人使用的,那么你可以创建一个maven私库,然后你们两个都去访问这个私库就可以访问的到了,前提是你将自己写的starter上传上去了
我们新建一个sprinboot项目然后在pom.xml中引入我们的依赖:
<dependency>
<groupId>hl-spring-boot-starter</groupId>
<artifactId>hl-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
然后编写一个controller来测试一下:
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
UserInfoService userInfoService;
@RequestMapping("/getUserInfo")
public String getUserInfo(){
return "现在登陆的人名称:"+userInfoService.getName()+",年龄是:"+userInfoService.getAge()+"岁,角色是:"+userInfoService.getRole();
}
}
然后在application.yaml中添加上自己需要的东西:
user:
info:
name: zhangsan
age: 12
role: 管理员
然后启动项目,然后访问http://localhost:8080/test/getUserInfo得到结果:
这样就可以了,完美完成。