SpringBoot
一、SpringBoot 概述
SpringBoot 概念 SpringBoot提供了一种快速使用Spring的方式,基于约定优于配置的思想,可以让开发人员不必在配置与逻 辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。
1.1、Spring 缺点
-
配置繁琐
虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。 -
依赖繁琐
项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导 入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发 进度。
1.2、SpringBoot 功能
-
自动配置
Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定 Spring配置应该用哪个,不该用哪个。该过程是SpringBoot自动完成的。 -
起步依赖/依赖传递
起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。 -
辅助功能
提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标,健康检测、外部配置等。 Spring Boot 并不是对 Spring 功能上的增强,而是提供了一种快速使用 Spring 的方式。
二、SpringBoot 配置
SpringBoot是基于约定的,所以很多配置都有默认值,但如果想使用自己的配置替换默认配置的话,就可以使用application.properties或者application.yml(application.yaml)进行配置。
配置文件分类
- properties
- yml/yaml
默认配置文件名称:application
在同一级目录下优先级为:properties > yml > yaml
不同配置文件中相同配置按照加载优先级相互覆盖,不同配置文件中不同配置全部保留
2.1、yaml基本语法
YAML(YAML Ain’t Markup Language),一种数据序列化格式
优点:
- 容易阅读
- 容易与脚本语言交互
- 以数据为核心,重数据轻格式
YAML文件扩展名
- .yml(主流)
- .yaml
2.2、yaml语法规则
- 大小写敏感
- 属性层级关系使用多行描述,每行结尾使用冒号结束
- 使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)
- 属性值前面添加空格(属性名与属性值之间使用冒号+空格作为分隔)
#
表示注释- 核心规则:数据前面要加空格与冒号隔开
2.2.1、字面值表示方式
## TRUE, True, true, FALSE, False, false均可
boolean: TRUE
## 3.14e+5 支持科学计数法
float: 3.14
## 支持二进制、八进制、十六进制
int: 123
## 使用~表示null
null: ~
## 字符串可以直接书写
string: Java666
## 可以使用双引号包裹特殊字符
string2: "Hello World"
## 日期必须使用yyyy-MM-dd格式
date: 2021-12-17
## 时间和日期之间使用T连接,最后使用+代表时区
datetime: 2021-11-17T14:03:00+08:00
2.2.2、数组表示方式
在属性名书写位置的下方使用减号作为数据开始符号,每行书写一个数据,减号与数据间空格分隔
subject:
- Java
- PHP
- Python
- C++
- C#
## 数组书写缩略格式
habby: [吃饭,睡觉,上班]
## 对象数组格式
users1:
- name: zhangsan
age: 20
- name: lisi
age: 18
## 对象数组格式二
users2:
-
name: zhangsan
age: 20
-
name: lisi
age: 18
## 对象数组缩略格式
users3: [ { name:zhangsan , age:20 } , { name:lisi , age:18 } ]
数组获取
@Value("${subject[1]}")
2.2.3、yaml数据读取
1、使用@Value读取单个数据
属性名引用方式:${一级属性名.二级属性名……}
server:
port: 9090
name: zhangsan
subject:
- Java
- PHP
- Python
- C++
- C#
@RequestMapping("/")
@RestController
public class TestController {
@Value("${name}")
private String name;
@Value("${server.port}")
private String port;
@Value("${subject[1]}")
private String[] subject;
@GetMapping("/getValue")
public void getValue() {
System.out.println("name = " + name);
System.out.println("port = " + port);
System.out.println("subject = " + Arrays.toString(subject));
}
}
在配置文件中可以使用属性名引用方式引用属性
baseUrl: /opt/home
software:
redis: ${baseUrl}/redis
mysql: ${baseUrl}/mysql
@Value("${name}")
private String name;
@Value("${server.port}")
private String port;
@Value("${subject[1]}")
private String[] subject;
@Value("${baseUrl}")
private String baseUrl;
@Value("${software.redis}")
private String redis;
@Value("${software.mysql}")
private String mysql;
属性值中如果出现转移字符,需要使用双引号包裹
content: "你好\t我好\n大家好"
2、封装全部数据到Environment对象
@Autowired
private Environment environment;
@GetMapping("/getValue")
public void getValue() {
...
System.out.println(environment.getProperty("name"));
System.out.println(environment.getProperty("server.port"));
System.out.println(environment.getProperty("subject[1]"));
}
3、ConfigurationProperties 通过自定义对象封装指定数据
user:
userName: zhangsan
age: 18
habby:
- 井空
- 小泽
- 波多
@Data
@Component //必须要交给spring管理
@ConfigurationProperties(prefix = "user")
public class User {
private String userName;
private Integer age;
private String[] habby;
}
@Autowired
private User user;
总结
- 使用@ConfigurationProperties注解绑定配置信息到封装类中
- 封装类需要定义为Spring管理的bean,否则无法进行属性注入
2.3、多环境开发
多数项目都会有开发环境、测试环境、生产环境,各个环境配置可能都会不一样,于是在构建时,会涉及到环境配置的切换。来回手工修改配置,效率低下,容易出错。可以配置多个含有不同环境配置的Profile,在构建时指定构建环境,达到多环境下快速灵活构建的目的,profile功能就是来进行动态配置切换的。在SpringBoot之前,采用的是maven多环境配置。
2.3.1、maven配置方式
<profiles>
<profile>
<!--开发环境,这个id就是名称-->
<id>dev</id>
<properties>
<!--这个标签就是定义你要设定的properties中的属性-->
<isFlag>true</isFlag>
</properties>
</profile>
<profile>
<!--生产环境-->
<id>test</id>
<properties>
<isFlag>true</isFlag>
</properties>
</profile>
<profile>
<!--生产环境-->
<id>pro</id>
<properties>
<isFlag>true</isFlag>
</properties>
</profile>
</profiles>
2.3.2、springboot配置方式
profile是用来完成不同环境下,配置动态切换功能的。
2.3.2.1、yml方式
# 启动指定环境
spring:
profiles:
active: pro
# 设置生产环境
---
spring:
profiles: pro
server:
port: 80
# 设置开发环境
---
spring:
profiles: dev
server:
port: 81
# 设置测试环境
---
spring:
profiles: test
server:
port: 82
2.3.2.2、properties方式
主启动配置文件application.properties
spring.profiles.active=dex
环境分类配置文件application-dex.properties
server.port=80
环境分类配置文件application-test.properties
server.port=81
环境分类配置文件application-pro.properties
server.port=82
2.3.2.3、多环境开发独立配置文件
方式一:
根据功能对配置文件中的信息进行拆分,并制作成独立的配置文件,命名规则如下
- application-devDB.yml
- application-devRedis.yml
- application-devMVC.yml
使用include属性在激活指定环境的情况下,同时对多个环境进行加载使其生效,多个环境间使用逗号分隔
spring:
profiles:
active: dev
include: devDB,devRedis,devMVC
当主环境dev与其他环境有相同属性时,主环境属性生效;其他环境中有相同属性时,最后加载的环境属性生效
方式二:
- 从Spring2.4版开始使用group属性替代include属性,降低了配置书写量
- 使用group属性定义多种主环境与子环境的包含关系
spring:
profiles:
active: dev
group:
"dev": devDB,devRedis,devMVC
"pro": proDB,proRedis,proMVC
"test": testDB,testRedis,testMVC
2.3.3、Maven与SpringBoot多环境兼容
1、Maven中设置多环境属性
<profiles>
<profile>
<id>dev_env</id>
<properties>
<profile.active>dev</profile.active>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>pro_env</id>
<properties>
<profile.active>pro</profile.active>
</properties>
</profile>
<profile>
<id>test_env</id>
<properties>
<profile.active>test</profile.active>
</properties>
</profile>
</profiles>
2、SpringBoot中引用Maven属性
spring:
profiles:
active: @profile.active@
3、执行Maven打包指令,并在生成的boot打包文件.jar文件中查看对应信息
总结
- 当Maven与SpringBoot同时对多环境进行控制时,以Mavn为主,SpringBoot使用@…@占位符读取Maven对应的配置属性值
- 基于SpringBoot读取Maven配置属性的前提下,如果在Idea下测试工程时pom.xml每次更新需要手动compile方可生效
2.3.4、总结
profile配置方式
- 多profile文件方式
- yml多文档方式
profile激活方式
- 配置文件
- 虚拟机参数
- 命令行参数
profile配置方式
-
多profile文件方式:提供多个配置文件,每个代表一种环境。
application-dev.properties/yml 开发环境
application-test.properties/yml 测试环境
application-pro.properties/yml 生产环境 -
yml多文档方式
在yml中使用 — 分隔不同配置 -
profile激活方式
- 配置文件: 再配置文件中配置:spring.profiles.active=dev
- 虚拟机参数:在VM options 指定:-Dspring.profiles.active=dev
- 命令行参数:java –jar xxx.jar --spring.profiles.active=dev
2.4、内部配置加载顺序
Springboot程序启动时,会从以下4级加载配置文件:
- 1级: file :config/application.yml 【最高】
- 2级: file :application.yml
- 3级:classpath:config/application.yml
- 4级:classpath:application.yml 【最低】
作用
- 1级与2级留做系统打包后设置通用属性,1级常用于运维经理进行线上整体项目部署方案调控
- 3级与4级用于系统开发阶段设置通用属性,3级常用于项目经理进行整体项目属性调控
加载顺序为上文的排列顺序,高优先级配置的属性会生效。
配置文件分为4种
- 项目类路径配置文件:服务于开发人员本机开发与测试
- 项目类路径config目录中配置文件:服务于项目经理整体调控
- 工程路径配置文件:服务于运维人员配置涉密线上环境
- 工程路径config目录中配置文件:服务于运维经理整体调控
多层级配置文件间的属性采用叠加并覆盖的形式作用于程序
三、日志
3.1、日志基础操作
日志(log)作用
- 编程期调试代码
- 运营期记录信息
- 记录日常运营重要信息(峰值流量、平均响应时长……)
- 记录应用报错信息(错误堆栈)
- 记录运维过程数据(扩容、宕机、报警……)
3.2、代码中使用日志工具记录日志
1、添加日志记录操作
@RestController
@RequestMapping("/books")
public class BookController {
private static final Logger log = LoggerFactory.getLogger(BookController.class);
@GetMapping
public String getById() {
System.out.println("springboot is running...");
log.debug("debug ...");
log.info("info ...");
log.warn("warn ...");
log.error("error ...");
return "springboot is running...";
}
}
日志级别
- TRACE:运行堆栈信息,使用率低
- DEBUG:程序员调试代码使用
- INFO:记录运维过程数据
- WARN:记录运维过程报警数据
- ERROR:记录错误堆栈信息
- FATAL:灾难信息,合并计入ERROR
2、设置日志输出级别
# 开启 debug 模式,输出调试信息,常用于检查系统运行状况
debug: true
# 设置日志级别, root 表示根节点,即整体应用日志级别
logging:
level:
root: debug
3、设置日志组,控制指定包对应的日志输出级别,也可以直接控制指定包对应的日志输出级别
logging:
# 设置日志组
group:
# 自定义组名,设置当前组中所包含的包
ebank: com.java521.controller
level:
root: warn
# 为对应组设置日志级别
ebank: debug
# 为对包设置日志级别
com.java521.controller: debug
小结
- 日志用于记录开发调试与运维过程消息
- 日志的级别共6种,通常使用4种即可,分别是DEBUG,INFO,WARN,ERROR
- 可以通过日志组或代码包的形式进行日志显示级别的控制
优化日志对象创建代码
使用lombok提供的注解@Slf4j简化开发,减少日志对象的声明操作
@Slf4j
@RestController
@RequestMapping("/books")
public class BookController {
@GetMapping
public String getById() {
System.out.println("springboot is running...");
log.debug("debug ...");
log.info("info ...");
log.warn("warn ...");
log.error("error ...");
return "springboot is running...";
}
}
3.3、日志输出格式控制
PID:进程ID,用于表明当前操作所处的进程,当多服务同时记录日志时,该值可用于协助程序员调试程序
所属类/接口名:当前显示信息为SpringBoot重写后的信息,名称过长时,简化包名书写为首字母,甚至直接删除
设置日志输出格式
logging:
pattern:
console: "%d - %m%n"
- %d:日期
- %m:消息
- %n:换行
logging:
pattern:
console: "%d %clr(%p) --- [%16t] %clr(%-40.40c){cyan} : %m %n"
3.4、日志文件
logging:
file:
name: server.log
日志文件详细配置
logging:
file:
name: server.log
logback:
rollingpolicy:
max-file-size: 3KB
file-name-pattern: server.%d{yyyy-MM-dd}.%i.log
四、整合第三方技术
4.1、整合JUnit
@SpringBootTest
class ApplicationTests {
@Autowired
private User user;
@Test
public void contextLoads() {
System.out.println(user);
}
}
- 名称:@SpringBootTest
- 类型:测试类注解
- 位置:测试类定义上方
- 作用:设置JUnit加载的SpringBoot启动类
- 范例:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
class ApplicationTests {
- 相关属性
classes:设置SpringBoot启动类
如果测试类在SpringBoot启动类的包或子包中,可以省略启动类的设置,也就是省略classes的设定
4.2、整合Redis
省略
4.3、整合Mybatis
省略
五、SpringBoot 自动配置
5.1、Condition
Condition 是在Spring 4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建Bean操作。
SpringBoot是如何知道要创建哪个Bean的?比如SpringBoot是如何知道要创RedisTemplate的?
新建SpringBoot项目,添加依赖。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
启动类
@SpringBootApplication
public class SpringbootConditionApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
Object redisTemplate = context.getBean("redisTemplate");
System.out.println("redisTemplate = " + redisTemplate);
}
}
启动项目,报错
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
启动项目,成功
添加依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
案例:需求
在 Spring 的 IOC 容器中有一个 User 的 Bean,现要求:
- 导入Jedis坐标后,加载该Bean,没导入,则不加载。
- 将类的判断定义为动态的。判断哪个字节码文件存在可以动态指定。
添加依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>
User实体
public class User {
}
UserConfig
@Configuration
public class UserConfig {
@Bean
@Conditional(ClassCondition.class)
public User user(){
return new User();
}
}
ClassCondition
public class ClassCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
try {
Class<?> aClass = Class.forName("redis.clients.jedis.Jedis");
return true;
} catch (ClassNotFoundException e) {
e.printStackTrace();
return false;
}
}
}
修改启动类
@SpringBootApplication
public class SpringbootConditionApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
//Object redisTemplate = context.getBean("redisTemplate");
//System.out.println("redisTemplate = " + redisTemplate);
Object user = context.getBean("user");
System.out.println("user = " + user);
}
}
分别测试添加Jedis坐标和不添加Jedis坐标
添加Jedis坐标
没有添加Jedis坐标
改造:判断是否添加任意的类
添加:ConditionOnClass
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ClassCondition.class)
public @interface ConditionOnClass {
String[] value();
}
UserConfig
@Configuration
public class UserConfig {
@Bean
//@Conditional(ClassCondition.class)
@ConditionOnClass("redis.clients.jedis.Jedis")
public User user(){
return new User();
}
}
分别测试添加和不添加Jedis坐标
public class ClassCondition implements Condition {
/**
* @param context 上下文对象。用于获取环境,IOC容器,ClassLoader对象
* @param metadata 注解元对象。 可以用于获取注解定义的属性值
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//2.需求: 导入通过注解属性值value指定坐标后创建Bean
/* try {
Class<?> aClass = Class.forName("redis.clients.jedis.Jedis");
return true;
} catch (ClassNotFoundException e) {
e.printStackTrace();
return false;
}*/
//2.需求: 导入通过注解属性值value指定坐标后创建Bean
Map<String, Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
System.out.println(map);
String[] value = (String[]) map.get("value");
boolean falg = true;
try {
for (String clasName : value) {
Class<?> aClass = Class.forName(clasName);
}
} catch (ClassNotFoundException e) {
falg = falg;
}
return falg;
}
}
可用springboot提供 @ConditionalOnProperty 当存在配置才会创建bean
java=helloworld
UserConfig
@Configuration
public class UserConfig {
@Bean
//@Conditional(ClassCondition.class)
//@ConditionOnClass("redis.clients.jedis.Jedis")
public User user() {
return new User();
}
@Bean
@ConditionalOnProperty(name = "java", havingValue = "helloworld")
public User user2() {
return new User();
}
}
SpringbootConditionApplication
SpringBootApplication
public class SpringbootConditionApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
//Object redisTemplate = context.getBean("redisTemplate");
//System.out.println("redisTemplate = " + redisTemplate);
Object user2 = context.getBean("user2");
System.out.println("user2 = " + user2);
}
}
Condition – 小结
- 自定义条件:
- 定义条件类:自定义类实现Condition接口,重写 matches 方法,在 matches 方法中进行逻辑判断,返回boolean值 。 matches 方法两个参数:
- context:上下文对象,可以获取属性值,获取类加载器,获取BeanFactory等。
- metadata:元数据对象,用于获取注解属性。
- 判断条件: 在初始化Bean时,使用 @Conditional(条件类.class)注解
- 定义条件类:自定义类实现Condition接口,重写 matches 方法,在 matches 方法中进行逻辑判断,返回boolean值 。 matches 方法两个参数:
- SpringBoot 提供的常用条件注解:
- ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化Bean
- ConditionalOnClass:判断环境中是否有对应字节码文件才初始化Bean
- ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean
5.2、切换内置web服务器
SpringBoot的web环境中默认使用tomcat作为内置服务器,其实SpringBoot提供了4中内置服务器供我们选择,我们可以很方便的进行切换。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--排除tomcat依赖-->
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!--引入jetty的依赖-->
<dependency>
<artifactId>spring-boot-starter-jetty</artifactId>
<groupId>org.springframework.boot</groupId>
</dependency>
5.3、@Enable*注解
SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的。而其底层原理是使用@Import注解导入一些配置类,实现Bean的动态加载。
SpringBoot 工程是否可以直接获取jar包中定义的Bean?
创建springboot-enable 和 springboot-enable-other 不导入任何坐标
springboot-enable-other添加
public class User {
}
UserConfig
@Configuration
public class UserConfig {
@Bean
public User user(){
return new User();
}
}
springboot-enable 添加依赖
<dependency>
<groupId>com.java521</groupId>
<artifactId>spirngboot-bean-other</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
springboot-enable修改启动类
@SpringBootApplication
public class SpringbootEnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
Object user = context.getBean("user");
System.out.println("user = " + user);
}
}
启动springboot-enable 报错 原因是因为springboot 不能直接获取其它工程中定义的bean
com.java521.springbootenable
com.java521.config
@ComponentScan 扫描的是当前类所在包和其子包
1.使用@ComponentScan扫描com.itheima.config包
2.可以使用@Import注解,加载类。这些类都会被Spring创建,并放入IOC容器
3.可以对Import注解进行封装。
SpringbootEnableApplication
@SpringBootApplication
@ComponentScan("com.java521.config")
public class SpringbootEnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
Object user = context.getBean("user");
System.out.println("user = " + user);
}
}
启动测试:
@SpringBootApplication
//@ComponentScan("com.java521.config")
@Import(UserConfig.class)
public class SpringbootEnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
Object user = context.getBean("user");
System.out.println("user = " + user);
}
}
spirngboot-enable-other 添加自定义注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(UserConfig.class)
public @interface EnableUser {
}
springboot-enable 修改启动类
@SpringBootApplication
//@ComponentScan("com.java521.config")
//@Import(UserConfig.class)
@EnableUser
public class SpringbootEnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
Object user = context.getBean("user");
System.out.println("user = " + user);
}
}
5.4、@Import注解
@Enable*底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。而@Import提供4中用法:
- 导入Bean
- 导入配置类
- 导入 ImportSelector 实现类。一般用于加载配置文件中的类
- 导入 ImportBeanDefinitionRegistrar 实现类
1、导入Bean
@SpringBootApplication
//@ComponentScan("com.java521.config")
//@Import(UserConfig.class)
//@EnableUser
@Import(User.class)
public class SpringbootEnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
// Object user = context.getBean("user");
// System.out.println("user = " + user);
//导入后不一定还叫user 可以通过bean获取
User user = context.getBean(User.class);
System.out.println("user = " + user);
//获取bean的名称
Map<String, User> map = context.getBeansOfType(User.class);
System.out.println(map);
}
}
2、导入配置类
spirngboot-enable-other 添加
public class Role {
}
修改UserConfig
@Configuration
public class UserConfig {
@Bean
public User user(){
return new User();
}
@Bean
public Role role(){
return new Role();
}
}
修改springboot-enable启动类
@SpringBootApplication
//@ComponentScan("com.java521.config")
//@Import(UserConfig.class)
//@EnableUser
@Import(UserConfig.class)
public class SpringbootEnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
// Object user = context.getBean("user");
// System.out.println("user = " + user);
//导入后不一定还叫user 可以通过bean获取
User user = context.getBean(User.class);
System.out.println("user = " + user);
//获取bean的名称
// Map<String, User> map = context.getBeansOfType(User.class);
// System.out.println(map);
Role role = context.getBean(Role.class);
System.out.println("role = " + role);
}
}
使用@Import导入 配置类上的注解可以不加
public class UserConfig {
@Bean
public User user(){
return new User();
}
@Bean
public Role role(){
return new Role();
}
}
3、导入 ImportSelector 实现类。一般用于加载配置文件中的类
spirngboot-enable-other 添加ImportSelector 的实现类
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.java521.domain.User", "com.java521.domain.Role"};
}
}
启动类
@SpringBootApplication
//@ComponentScan("com.java521.config")
//@Import(UserConfig.class)
//@EnableUser
//@Import(UserConfig.class)
@Import(MyImportSelector.class)
public class SpringbootEnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
// Object user = context.getBean("user");
// System.out.println("user = " + user);
//导入后不一定还叫user 可以通过bean获取
User user = context.getBean(User.class);
System.out.println("user = " + user);
//获取bean的名称
// Map<String, User> map = context.getBeansOfType(User.class);
// System.out.println(map);
Role role = context.getBean(Role.class);
System.out.println("role = " + role);
}
}
4、导入 ImportBeanDefinitionRegistrar 实现类。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry);
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
registry.registerBeanDefinition("user",beanDefinition);
}
}
修改启动类
@SpringBootApplication
//@ComponentScan("com.java521.config")
//@Import(UserConfig.class)
//@EnableUser
//@Import(UserConfig.class)
//@Import(MyImportSelector.class)
@Import( MyImportBeanDefinitionRegistrar.class)
public class SpringbootEnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
// Object user = context.getBean("user");
// System.out.println("user = " + user);
//导入后不一定还叫user 可以通过bean获取
User user = context.getBean(User.class);
System.out.println("user = " + user);
//获取bean的名称
// Map<String, User> map = context.getBeansOfType(User.class);
// System.out.println(map);
Role role = context.getBean(Role.class);
System.out.println("role = " + role);
}
}
user成功 role失败 因为配置类中没有role
使用名称导入
@SpringBootApplication
//@ComponentScan("com.java521.config")
//@Import(UserConfig.class)
//@EnableUser
//@Import(UserConfig.class)
//@Import(MyImportSelector.class)
@Import( MyImportBeanDefinitionRegistrar.class)
public class SpringbootEnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
Object user = context.getBean("user");
System.out.println("user = " + user);
//导入后不一定还叫user 可以通过bean获取
// User user = context.getBean(User.class);
// System.out.println("user = " + user);
//获取bean的名称
// Map<String, User> map = context.getBeansOfType(User.class);
// System.out.println(map);
// Role role = context.getBean(Role.class);
// System.out.println("role = " + role);
}
}
5.5、@EnableAutoConfiguration 注解
- @EnableAutoConfiguration 注解内部使用 @Import(AutoConfigurationImportSelector.class)来加载配置类。
- 配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当 SpringBoot 应用启动时,会自动加载这些配置类,初始化Bean
- 并不是所有的Bean都会被初始化,在配置类中使用Condition来加载满足条件的Bean
5.6、自定义starter
案例:需求
自定义redis-starter。要求当导入redis坐标时,SpringBoot自动创建Jedis的Bean。
案例:实现步骤
1、创建 redis-spring-boot-autoconfigure 模块
2、创建 redis-spring-boot-starter 模块,依赖 redis-spring- boot-autoconfigure的模块
<dependency>
<groupId>com.java521</groupId>
<artifactId>redis-spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
3、在 redis-spring-boot-autoconfigure 模块中初始化 Jedis 的Bean。并定义META-INF/spring.factories 文件
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>
RedisProperties
@ConfigurationProperties(prefix = "reids")
public class RedisProperties {
private String host = "localhost";
private int port = 6379;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}
RedisAutoConfiguration
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {
//提供jedis的bean
@Bean
public Jedis jedis(RedisProperties redisProperties) {
return new Jedis(redisProperties.getHost(), redisProperties.getPort());
}
}
resources下创建META-INF/spring.factories, 添加内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.java521.redis.config.RedisAutoConfiguration
4、在测试模块中引入自定义的 redis-starter 依赖,测试获取Jedis 的Bean,操作 redis。
springboot-enable添加依赖
<dependency>
<groupId>com.java521</groupId>
<artifactId>redis-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
SpringbootEnableApplication
@SpringBootApplication
public class SpringbootEnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
Jedis jedis = context.getBean(Jedis.class);
System.out.println("jedis = " + jedis);
jedis.set("name","java");
String name = jedis.get("name");
System.out.println("name = " + name);
}
}
5、启动测试
5.7、Java 监听机制
SpringBoot 的监听机制,其实是对Java提供的事件监听机制的封装。
Java中的事件监听机制定义了以下几个角色:
- 事件:Event,继承 java.util.EventObject 类的对象
- 事件源:Source ,任意对象Object
- 监听器:Listener,实现 java.util.EventListener 接口 的对象
SpringBoot 在项目启动时,会对几个监听器进行回调,我们可以实现这些监听器接口,在项目启动时完成一些操作。
添加依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
5.7.1、ApplicationContextInitializer
@Component
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("ApplicationContextInitializer....initialize");
}
}
5.7.2、SpringApplicationRunListener
/**
* 当项目启动后执行run方法。
*/
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("ApplicationRunner...run");
System.out.println(Arrays.asList(args.getSourceArgs()));
}
}
5.7.3、CommandLineRunner
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("CommandLineRunner...run");
System.out.println(Arrays.asList(args));
}
}
5.7.4、ApplicationRunner
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
public MySpringApplicationRunListener(SpringApplication application, String[] args) {
}
@Override
public void starting() {
System.out.println("starting...项目启动中");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
System.out.println("environmentPrepared...环境对象开始准备");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("contextPrepared...上下文对象开始准备");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("contextLoaded...上下文对象开始加载");
}
@Override
public void started(ConfigurableApplicationContext context) {
System.out.println("started...上下文对象加载完成");
}
@Override
public void running(ConfigurableApplicationContext context) {
System.out.println("running...项目启动完成,开始运行");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("failed...项目启动失败");
}
}
5.8、SpringBoot 启动流程分析
5.9、SpringBoot 监控
5.9.1、SpringBoot 监控概述
SpringBoot自带监控功能Actuator,可以帮助实现对程序内部运行情况监控,比如监控状况、Bean加载情况、配置属性、日志信息等。
路径 | 描述 |
---|---|
/beans | 描述应用程序上下文里全部的Bean,以及它们的关系 |
/env | 获取全部环境属性 |
/env/{name} | 根据名称获取特定的环境属性值 |
/health | 报告应用程序的健康指标,这些值由HealthIndicator的实现类提供 |
/info | 获取应用程序的定制信息,这些信息由info打头的属性提供 |
/mappings | 描述全部的URI路径,以及它们和控制器(包含Actuator端点)的映射关系 |
/metrics | 报告各种应用程序度量信息,比如内存用量和HTTP请求计数 |
/metrics/{name} | 报告指定名称的应用程序度量值 |
/trace | 提供基本的HTTP请求跟踪信息(时间戳、HTTP头等) |
5.9.2、SpringBoot 监控使用
1、导入依赖坐标
<!--springboot版本-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2、访问http://localhost:8080/acruator,返回的是JSON数据,我这里安装了浏览器JSON插件。
3、Info相关
修改配置文件
info.name=zhangsan
info.age=18
4、health相关
5、开启健康检查的完整信息
info.name=zhangsan
info.age=18
# 开启健康检查的完整信息
management.endpoint.health.show-details=always
6、检查第三方组件 比如redis,默认连接本地redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
关闭redis,重启项目
7、将所有的监控endpoints暴露出来,修改配置文件。
info.name=zhangsan
info.age=18
# 开启健康检查的完整信息
management.endpoint.health.show-details=always
# 将所有的监控endpoints暴露出来
management.endpoints.web.exposure.include=*
访问:http://localhost:8080/actuator
8、查看所有的url路径:http://localhost:8080/actuator/mappings
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/findAll")
public String findAll(){
return "success";
}
}
5.9.3、Spring Boot Admin
- Spring Boot Admin是一个开源社区项目,用于管理和监控SpringBoot应用程序。
- Spring Boot Admin 有两个角色,客户端(Client)和服务端(Server)。
- 应用程序作为Spring Boot Admin Client向为Spring Boot Admin Server注册
- pring Boot Admin Server 的UI界面将Spring Boot Admin Client的Actuator Endpoint上的
5.9.3.1、admin-server:
1、创建 admin-server 模块
2、导入依赖坐标 admin-starter-server
<?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.5.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.java521</groupId>
<artifactId>springboot-admin</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-admin</name>
<description>springboot-admin</description>
<properties>
<java.version>1.8</java.version>
<spring-boot-admin.version>2.4.3</spring-boot-admin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-dependencies</artifactId>
<version>${spring-boot-admin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3、在引导类上启用监控功能@EnableAdminServer
@EnableAdminServer
@SpringBootApplication
public class SpringbootAdminApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootAdminApplication.class, args);
}
}
4、编写配置文件
server:
port: 8080
5.9.3.2、admin-client:
1、创建 admin-client 模块
2、导入依赖坐标 admin-starter-client
<?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.5.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.java521</groupId>
<artifactId>springboot-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-client</name>
<description>springboot-client</description>
<properties>
<java.version>1.8</java.version>
<spring-boot-admin.version>2.4.3</spring-boot-admin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-dependencies</artifactId>
<version>${spring-boot-admin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3、配置相关信息:server地址等
server:
port: 8081
# admin server 的地址
spring:
boot:
admin:
client:
url: http://localhost:8080
# 开放端点, 用于SpringBoot Admin的监控
management:
endpoint:
health:
show-details: always
endpoints:
web:
exposure:
include: '*'
4、启动server和client服务,访问server http://localhost:8080/
5、写一个Controller模拟一个普通的接口
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/findAll")
public String findAll(){
return "success";
}
}
6、测试,访问:http://localhost:8081/user/findAll,通过浏览器访问这个接口就会打印日志
7、环境配置信息
5.10、SpringBoot 项目部署
- jar包(官方推荐)
- war包