SpringBoot笔记
1 SpringBoot概述
1.1 SpringBoot概念
SpringBoot提供了一种快速使用Spring的方式,基于约定大于配置的思想,可以让开发人员不必再配置与逻辑业务之间进行思维的切换,从而大大提高开发速率,一定程度缩短项目周期。
1.2 Spring的缺点
1、配置繁琐
虽然Spring的组件代码是轻量级的,但是它的配置是重量级的。一开始,Spring是用xml配置。Spring2.5引入的基于注解的组件扫描。Spring3.0引入了基于java的配置。
2、依赖繁琐
在环境搭建时,需要分析的要导入哪些库的坐标,而且还要分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖版本,随之而来的不兼容问题会严重阻碍项目的开发进度。
1.3 SpringBoot功能
1、自动配置
2、起步依赖
将具备某种功能的坐标打包到一起,并提供一些默认的功能。
3、辅助功能
如嵌入式服务器,安全,指标,健康检测,外部配置等。
SprigBoot并不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式。
2 SpringBoot快速入门
2.1 实现步骤
1、创建一个maven工程
2、导入SpringBoot起步依赖
3、定义Controller等
4、编写引导类(项目入口,启动main方法就可以运行项目)
5、启动测试。
使用Spring Initializer可以快速创建Spring Boot项目
2.2 起步依赖
spring-boot-starter-parent
spring-boot-starter-web
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
spring-boot-starter-parent中定义了各种技术的版本信息,组合了一套最优搭配的技术版本。
在各种starter中,定义了完成该功能需要的总坐标合集,其中大部分版本信息来自于父工程。
我们的工程继承parent,引入starter后,通过依赖传递,就可以简单方便获得需要的jar包,并且不会存在版本冲突的问题。
3 SpringBoot配置
SpringBoot是基于约定的,所以很多配置都有默认值,想使用自己的配置可以用application.properties或者application.yml(application.yaml)进行配置。
server.port=8081
yml配置文件
server:
port: 8082
同一级目录下优先级为:properties>yml>yaml
3.1 yaml
YAML文件的扩展名可以使用.yml或者.yaml。
基本语法:
1、大小写敏感。
2、数据前必须有空格,作为分隔符。
3、使用空格缩进表示层级关系,相同缩进表示同一级。
yaml语法示例:
name: neo
#map集合
person:
name: ${name}
age: 20
address
-beijing
-shanghai
#行内写法
person2: {name:zhangsan}
#数组
address:
- beijing
- shanghai
#行内写法
address2: [beijing,shanghai]
msg1: 'hello \n world'#单引号忽略转义字符
msg2: "hello \n world"#双引号识别转义字符
@Value注解获取值,
Environment获取整个配置
@RestController
public class HelloController {
@Value("${person.name}")
private String name;
@Value("${address[0]}")
private String address;
@Autowired
private Environment env;
@Autowired
private Person person;
@RequestMapping("/hello")
public String hello(){
System.out.println(name);
System.out.println(address);
System.out.println(env.getProperty("person.name"));
System.out.println("------------------");
System.out.println(person);
return "hello springboot";
}
}
@ConfigurationProperties注解
javabean
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private String age;
private String[] address;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", address=" + Arrays.toString(address) +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String[] getAddress() {
return address;
}
public void setAddress(String[] address) {
this.address = address;
}
}
3.2 profile配置
3.2.1 多profile文件
多个配置文件,每个代表一种环境
application-dev.properties/yml开发环境
application-test.properties/yml测试环境
application-prod.properties/yml生产环境
profile激活方式
1、配置文件:spring.profiles.active=dev
2、虚拟机参数:在VM option指定:-Dspring.profiles.active=dev
3、命令行参数:java -jar xxx.jar --spring.profiles.active=dev
3.2.2 yml多文档方式
在yml中使用 — 来分割不同配置
---
server:
port: 8080
spring:
profiles: dev
---
server:
port: 8081
spring:
profiles: test
---
server:
port: 8082
spring:
profiles: prod
3.2.3 内部配置文件加载顺序
SpringBoot启动后,会从以下位置加载配置文件:
1、file:./config/ 当前项目下的/config目录下
2、file:./ 当前项目的根目录
3、classpath:/config/ classpath的/config目录
4、classpath:/ classpath的根目录
加载顺序为上文的排列顺序,高优先级配置的属性会生效
4 SpringBoot自动配置
4.1 Condition
4.1.1 自定义条件
自定义条件:
1、定义条件类,自定义类实现Condition接口,重写matches方法,在matches方法中进行逻辑判断,返回boolean值,matches的两个参数作用如下。
context:上下文对象,可以获取属性值、类加载器、BeanFactory。
metadata:元数据对象,用于获取注解属性
2、判断条件,在初始话Bean时,使用@conditional(条件类.class)注解
@Configuration
public class UserConfig {
@Bean
@Conditional(ClassCondition.class)
public Person user (){
return new Person();
}
}
public class ClassCondition implements Condition {
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//1.需求:有引入Jedis则返回true
//判断redis.clients.jedis.Jedis.class文件是否存在
boolean flag = true;
try {
Class clazz = Class.forName("redis.clients.jedis.Jedis");
} catch (ClassNotFoundException e) {
flag = false;
e.printStackTrace();
}
return flag;
}
}
4.1.2 自定义条件注解
我们来自定义一个注解@ConditionOnClass
@Configuration
public class UserConfig {
@Bean
//使用自定义注解
@ConditionOnClass("redis.clients.jedis.Jedis")
public Person user (){
return new Person();
}
}
自定义注解:
@Conditional(ClassCondition2.class)
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConditionOnClass {
String[] value();
}
自定义条件类:
public class ClassCondition2 implements Condition {
/**
*
* @param context 上下文对象,用于获取环境、IOC容器、ClassLoader对象
* @param metadata 注解元对象,可以用于获取注解定义的属性值
* @return
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//2.需求:有引入指定的则返回true
boolean flag = true;
try {
//获取注解属性值vlaue
Map<String,Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
String[] values = (String[]) map.get("value");
for (String className : values) {
System.out.println("className="+className);
Class clazz = Class.forName(className);
}
} catch (ClassNotFoundException e) {
flag = false;
e.printStackTrace();
}
return flag;
}
}
SpringBoot提供的常用条件注解:
@ConditionalOnProperty:判断配置文件是否有对应属性和值才初始化Bean;
@ConditionalOnClass:判断环境中是否有对应字节码文件才初始化Bean;
@ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean。
4.2 内置web服务器切换
默认是tomcat服务器,使用其他服务的的方法:
移除tomcat
4.3 Enable*注解原理
SpringBoot中提供了很多@Enable开头的注解,这些注解都是用于动态启用某些功能的。其底层原理是使用@Import注解导入一些配置类,实现Bean的动态加载。
question:SpringBoot工程是否可以直接获取jar包中定义的Bean?
不能。
获取的三种方法示例如下:
方法1、使用@ComponentScan扫描其所在的包。
方法2、可以使用@Import注解,加载类。这些类都会被Spring创建,并放入到IOC容器中。
方法3、可以对Import注解进行封装。
示例如下:
package com.it;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import other.config.EmpConfig;
import other.config.EnableConfig;
/**
* SpringbootInitApplication引导类所在包:com.it;
* EmpConfig配置类所在包:other.config;
* 怎么才能加载到emp的Bean呢
* 方法1 :使用@ComponentScan 扫描other.config包。@ComponentScan 扫描范围:当前引导类所在包及其子包
* 方法2 :可以使用@Import注解,加载类。这些类都会被Spring创建,并放入到IOC容器中
* 方法3 :可以对Import注解进行封装
*/
@SpringBootApplication
//@ComponentScan("other.config")
//@Import(EmpConfig.class)
@EnableConfig
public class SpringbootInitApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootInitApplication.class, args);
Object emp = context.getBean("emp");
System.out.println(emp);
}
}
配置类
package other.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import other.domain.Emp;
@Configuration
public class EmpConfig {
@Bean
public Emp emp(){
return new Emp();
}
}
对Import注解封装
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EmpConfig.class)
public @interface EnableConfig {
}
4.4 Import的四种用法
@Enable*底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器,而@Import提供4种用法:
用法1、导入Bean
用法2、导入配置类
用法3、导入ImportSelector实现类
用法4、导入ImportBeanDefinitionRegistrar实现类
@SpringBootApplication
/**
*@Import的四种用法
* 1、导入Bean
* 2、导入配置类
* 3、导入ImportSelector实现类
* 4、导入ImportBeanDefinitionRegistrar实现类
*/
//@Import(Emp.class)
//@Import(EmpConfig.class)
//@Import(MyImportSelectot.class)
@Import(MyImportBeanDefinitionRegistrar.class)
public class SpringbootInitApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootInitApplication.class, args);
Emp emp = context.getBean(Emp.class);
System.out.println(emp);
// Map<String,Emp> empMap= context.getBeansOfType(Emp.class);//获取bean的名称
// System.out.println(empMap);
}
}
ImportSelector 的实现类
public class MyImportSelectot implements ImportSelector {
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"other.domain.Emp"};
}
}
ImportBeanDefinitionRegistrar的实现类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Emp.class).getBeanDefinition();
registry.registerBeanDefinition("emp",beanDefinition);
}
}
4.5 @EnableAutoConfiguration注解
@EnableAutoConfiguration注解内部使用@Import({AutoConfigurationImportSelector.class})来加载配置类。
配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当SpringBoot应用启动时,会自动加载这些配置类,初始化Bean。
并不是所有的Bean都会被初始化,再配置类中使用Condition来加载满足条件的Bean。
spring.factories里配置了很多需要被加载的类
4.6 starter
案例:我们自定义redis-starter,当导入redis坐标时,SpringBoot自动创建Jedis的Bean。
实现步骤:
1 创建redis-spring-boot-autoconfigure模块
2 创建redis-spring-boot-starter模块,依赖redis-spring-boot-autoconfigure模块
3 在redis-spring-boot-autoconfigure模块中初始化Jedis的Bean。并定义META-INF/spring.factories文件
4 在测试模块中引入自定义的redis-starter依赖,测试获取Jedis的Bean,操作redis。
代码示例如下:
在redis-spring-boot-autoconfigure模块里写一个配置类RedisAutoConfiguration 去加载Bean;
@Configuration
//让RedisProperties被Spring识别
@EnableConfigurationProperties(RedisProperties.class)
//保证Jedis在的时候才加载这个bean
@ConditionalOnClass(Jedis.class)
public class RedisAutoConfiguration {
/**
* 提供Jedis的bean
* 从RedisProperties里动态获取ip跟端口
*/
@Bean
//如果没有一个名称叫jedis的bean时,才去创建这样一个Bean
@ConditionalOnMissingBean(name = "jedis")
public Jedis jedis( RedisProperties redisProperties){
System.out.println("创建了jedis的bean......");
return new Jedis(redisProperties.getHost(),redisProperties.getPort());
}
}
可以从配置文件里获取redis的配置,默认为本地,端口为6379;
//加载以redis开头的配置
@ConfigurationProperties(prefix = "redis")
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;
}
}
新增META-INF/spring.factories文件,为啥要创建这个文件,参考4.5@EnableAutoConfiguration注解的作用。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.itxwz.RedisAutoConfiguration
在redis-spring-boot-starter模块引入依赖
<dependency>
<groupId>com.itxwz</groupId>
<artifactId>redis-spring-boot-configuration</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
在测试模块springboot-init里引入redis-spring-boot-starter
<!--引入自定义的redis的starter-->
<dependency>
<groupId>com.itxwz</groupId>
<artifactId>redis-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
在测试模块里看能否获取到jedis的Bean
@SpringBootApplication
public class SpringbootInitApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootInitApplication.class, args);
Jedis jedis = context.getBean(Jedis.class);
System.out.println(jedis);
jedis.set("name","itcast");
String name = jedis.get("name");
System.out.println(name);
}
//在这里创建一个Jedis的bean,看RedisAutoConfiguration还会创建Jedis的bean嘛(不会再创建了)
@Bean
public Jedis jedis(){
return new Jedis("localhost",6379);
}
}
可以在测试模块的配置文件里配置redis的地址跟端口
redis.host=localhost
redis.port=6379
至此,自定义的redis-starter结束。
springBoot的自动装配里其实已经写好了,如下:
5 SpringBoot事件监听
SpringBoot的佳通机制,其实时对Java提供的时间监听机制的封装。
5.1 java监听机制
java中的事件监听机制定义了以下几个角色:
事件:Event,继承java.util.EventObject类的对象
事件源:Source,任意对象Object
监听器:Listener,实现java.util.EventListener接口的对象
5.2 SpringBoot监听机制
SpringBoot在项目启动时,会对几个监听器进行回调,我们可以实现这些监听器接口,在醒目启动时完成一些操作。
ApplicationContextInitializer、SpringApplicationRunListener、CommandLineRunner、ApplicationRunner
实现ApplicationRunner接口,如下:
/**
* 当项目启动后执行run方法,用处:比如缓存预热,加数据库的数据加载到缓存
*/
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments applicationArguments) throws Exception {
System.out.println("ApplicationRunner...run");
System.out.println(Arrays.asList(applicationArguments.getSourceArgs()));
}
}
实现ApplicationRunner接口,如下:
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... strings) throws Exception {
System.out.println("CommandLineRunner...run");
System.out.println(Arrays.asList(strings));
}
}
实现ApplicationContextInitializer接口,如下:
@Component
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
System.out.println("ApplicationContextInitializer...");
}
}
配置META-INF/spring.factories
org.springframework.context.ApplicationContextInitializer=com.it.listener.MyApplicationContextInitializer
实现SpringApplicationRunListener 接口,如下:
//@Component
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
public MySpringApplicationRunListener(SpringApplication springApplication,String[] args) {
}
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
System.out.println("starting...项目启动中");
}
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, 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...项目启动失败");
}
}
配置META-INF/spring.factories
org.springframework.boot.SpringApplicationRunListener=com.it.listener.MySpringApplicationRunListener
6 SpringBoot监控
6.1 actuator的基本使用
springboot自带监控功能Actuator,可以帮助实现对程序内部运行情况监控,比如监控状况,Bean的加载情况、配置属性、日志信息等
那么使用方法也很简单,加上如下坐标,访问http://localhost:8080/actuator即可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
可以配置查看完整的健康信息
#开启将抗检查的完整信息
management.endpoint.health.show-details=always
6.2 SpringBoot-admin图形化界面使用
示例如下:
快速创建springboot-admin-server模块,选择java Web,Ops下的Spring boot Admin(Server)
模块创建好可以看到下面这个依赖:
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
在引导类开启admin-server
//开启admin-server
@EnableAdminServer
@SpringBootApplication
public class SpringbootAdminServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootAdminServerApplication.class, args);
}
}
在application.properties里配置server的端口
server.port=9000
server创建完成,下面来创建springboot-admin-client模块,选择java Web,Ops下的Spring boot Admin(Client)
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
配置application.properties文件,如下
#执行admin.server地址
spring.boot.admin.client.url=http://localhost:9000
management.endpoint.health.show-details=always
management.endpoints.web.exposure.include=*
admin-server配置了9000的端口,我们呢直接访问http://localhost:9000
server里提供了很多功能,例如,可以看到线程实时状态:
7 SpringBoot的部署
SpringBoot项目开发完成后,支持2种方式部署到服务器:
1、jar包(官方推荐)
2、war包
下面介绍下war包的打包方式。
pom.xml文件里定义打包方式:
<groupId>com.id</groupId>
<artifactId>springboot-deploy</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-deploy</name>
<description>Demo project for Spring Boot</description>
<packaging>war</packaging>
引导类继承SpringBootServletInitializer,重写configure方法
@SpringBootApplication
public class SpringbootDeployApplication extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(SpringbootDeployApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringbootDeployApplication.class);
}
}