SpringBoot-1入门
尚硅谷 SpringBoot2 学习笔记,官方连接。
一、构建一个 SpringBoot 项目,浏览器发送 /hello 请求,响应 Hello,SpringBoot2!
1. 创建 Maven 工程,不使用骨架
2.pom.xml 文件中添加父工程
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.3.4.RELEASE</version>
</parent>
3.pom.xml 文件中添加 web 项目依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
</dependencies>
4.创建项目包和 SpringBoot 应用程序的主类 com.atguigu.boot.MainApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/**
* @Classname MainApplication
* @Description SpringBoot 项目的主程序类
@SpringBootApplication 此注解表示类是 SpringBoot 项目的主类
*/
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class,args);
}
}
5.创建一个 controller
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @Classname HelloController
* @Description
@ResponseBody 此注解表示直接返回数据给浏览器,boot2 中还可以使用 @RestController 注解代替 @Controller 和 @ResponseBody
*/
@Controller
public class HelloController {
@ResponseBody
@RequestMapping("/hello")
public String hello() {
return "hello,SpringBoot2!";
}
}
6.运行 SpringBoot 应用程序并测试效果
二、application.properties
SpringBoot 程序中的所有配置信息都可以写在此文件中,不必像 SSM 阶段那样配置好几个 xml 文件了,体现了 SpringBoot 简化配置的特性。例如:
更改项目的端口号
resources 包下新建 application.properties , 更改端口号为 8888
server.port=8888
重启 SpringBoot 服务器测试效果
配置不会写可以参照官方文档
三、将 SpringBoot 应用程序打 jar 包并运行
1.pom 文件中设置打包方式,添加 maven 插件(打包用的)
<packaging>jar</packaging>
...
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.打包
四、自动版本仲裁和自动配置原理
1.依赖管理
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<!--它的父项目-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<!--依赖管理和自动版本仲裁,几乎声明了开发中所有依赖的版本号-->
<!--如果真的需要自定义的版本或者使用自定义的类库,可以使用如下方法-->
<!--
1.查看 spring-boot-dependencies 里面规定当前以来的版本,用的 key。
2.在当前项目里面重写配置
-->
<properties>
<mysql.version>5.4.1</mysql.version>
<xxx.version>1.0.0</xxx.version>
</properties>
<dependencies>
<dependency>
<groupId>com.xxx.xxx</groupId>
<artifactId>xxx</artifactId>
</dependency>
</dependencies>
2. spring-boot-start 场景启动器(一组依赖的集合)
-
官方很多启动器命名为:spring-boot-start-* *就是某种场景
例如:spring-boot-start-web spring-boot-start-amqp
所有官方场景启动器参见官方文档
-
只要项目中引入了场景启动器,启动器中的依赖都会自动引入项目
-
官方推荐自定义的场景启动器(包括第三方场景启动器)命名格式为:xxx-spring-boot-start
-
所有场景启动器都依赖 spring-boot-start (SpringBoot自动配置的核心依赖)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.3.4.RELEASE</version> <scope>compile</scope> </dependency>
-
使用 boot 时,引入依赖一般可以不写版本号,因为有自动版本仲裁机制,但是引入的不是 spring 官方支持的 jar 时还是需要自定义版本号。
3.自动配置
-
自动配置好了 Tomcat
-
自动配好了 SpringMVC
-
引入 SpringMVC 全套组件
-
自动配好 SpringMVC 常用组件(常用功能)
-
-
自动配好了 web 开发常见的功能,如:字符编码问题
- SpringBoot 帮我们配置好了所有 web 开发的常见场景
-
默认的包结构
-
主程序类所在的包及其子包都在扫描范围内
-
不需要配置包扫描了(SpringMVC 中的 @ComponentScan 注解或者 xml 文件中的 bean 标签等)
-
可以在核心注解 @SpringBootApplication 的属性中指定扫描包
@SpringBootApplication(scanBasePackages="com.xxx")
-
还可以使用
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan("com.xxx") // 上面三个注解就相当于 @SpringBootApplication
-
-
各种配置拥有默认值
- 默认配置最终都映射到 MultipartProperties
- 配置文件的值最终会绑定到每个类上,这个类会在容器中创建对象
-
按需加载所有自动配置项
-
引入了哪些 starter (场景),就启动这个场景的自动配置
-
SpringBoot 所有的自动配置功能都在 spring-boot-autoconfigure 包里面
-
-
…
五、容器功能
1.组件添加
原来在 Spring 中配置一个组件要写一个 bean.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user01" class="com.atguigu.boot.bean.User">
<property name="name" value="zhangsan"></property>
<property name="age" value="14"></property>
</bean>
<bean id="user02" class="com.atguigu.boot.bean.User">
<property name="name" value="lisi"></property>
<property name="age" value="15"></property>
</bean>
</beans>
在 SpringBoot 中可以使用 @Configuration 注解使一个类变成配置类
import com.atguigu.boot.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Classname MyConfig
* @Description TODO
*/
@Configuration // 告诉 SpringBoot 这是一个配置类,以前的 xml 配置文件
public class MyConfig {
@Bean // 给容器中添加组件,以方法名作为组件的 id。返回值就是组件类型,返回的是容器中的实例
public User user01() {
return new User("zhangsan",18);
}
@Bean("tom")
public Pet tomcat() {
return new Pet("tomcat");
}
// 从容器中获取组件,能看到上面的两个自定义组件
}
组件默认是单例模式的,不管获取多少次,都是同一个对象。
System.out.println("======================");
// 下面是两种获取方法
Object user01 = run.getBean("user01", String.class, Integer.class); // 指定参数
Object user02 = run.getBean("user01", User.class); // 指定类型
System.out.println(user01==user02);
- 配置类本身也是一个组件!
2. @Configuration 注解
- @Configuration(proxyBeanMethod = true) 代理 bean 的方法。 SpringBoot 总会检查这个组件是否在容器中存在(保持组件单实例)。
- Full(proxyBeanMethod = true)
- Lite(proxyBeanMethod = false)
- 使用场景:
- 配置类组件之间无依赖关系可以使用 Lite 模式,此时 SpringBoot 启动运行时不会检查容器中的所有组件,因此启动速度更快
- 配置类组件之间有依赖关系时,需要使用 Full 模式,保证容器中的组件是单例模式
3. @Bean @Component @Controller @Service @Repository
- 以前 Spring 中的注解都能用。
4. @Import
- 用法:@Import({User.class,DBHelper.class})
- 作用:给容器中自动创建这两个类型的组件,组件默认的名字是类的全限定名
5. @Conditional
-
条件装配:满足 Conditional 指定的条件,则进行条件注入
-
@Conditional 注解有很多派生注解(idea中按H查看类的继承树)
比如:
- ConditionalOnBean 当容器中存在指定组件时,做 … 事情
- ConditionalOnMissingBean 当容器中不存在指定组件时,做 … 事情
- ConditionalOnBeClass 当容器中存在某个类时,做 … 事情
- ConditionalOnMissingClass 当容器中不存在某个类时,做 … 事情
示例:
@Configuration // 告诉 SpringBoot 这是一个配置类,以前的 配置文件 public class MyConfig { @Bean // 给容器中添加组件,以方法名作为组件的 id。返回值就是组件类型,返回的是容器中的实例 @ConditionalOnBean(name = "tom") // 如果容器中有这个组件,就将 user01 注入容器 public User user01() { User user = new User("zhangsan",18); user.setCat(tomcat()); return user; } // @Bean("tom") // 普通方法,不注册进入容器 public Pet tomcat() { return new Pet("tomcat"); } }
@SpringBootApplication() public class MainApplication { public static void main(String[] args) { // 返回 IOC 容器 ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); boolean tom = run.containsBean("tom"); // 看看容器中有没有 tom 这个组件 System.out.println("容器中是否有tom组件:" + tom); boolean user01 = run.containsBean("user01"); // 看看容器中有没有 user01 这个组件 System.out.println("容器中是否有user01组件:" + user01); } }
结果:
-
@Conditional 注解及其派生注解可以标注在方法和类上
-
标注在方法上表示要满足某些条件这个组件才会注入容器
-
标注在类上表示要满足这个条件类中的所有组件才会注入容器
-
示例:
@Configuration // 告诉 SpringBoot 这是一个配置类,以前的 配置文件 @ConditionalOnBean(name = "tom22") // 如果容器中有这个组件,就将类中的 注入容器 public class MyConfig { @Bean // 给容器中添加组件,以方法名作为组件的 id。返回值就是组件类型,返回的是容器中的实例 public User user01() { User user = new User("zhangsan",18); user.setCat(tomcat()); return user; } @Bean("tom22") public Pet tomcat() { return new Pet("tomcat"); } // 从容器中获取组件,能看到上面的两个自定义组件 }
@SpringBootApplication() public class MainApplication { public static void main(String[] args) { // 返回 IOC 容器 ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); boolean tom = run.containsBean("tom"); // 看看容器中有没有 tom 这个组件 System.out.println("容器中是否有tom组件:" + tom); boolean user01 = run.containsBean("user01"); // 看看容器中有没有 user01 这个组件 System.out.println("容器中是否有user01组件:" + user01); boolean tom22 = run.containsBean("tom22"); // 看看容器中有没有 tom22 这个组件 System.out.println("容器中是否有tom22组件:" + tom22); } }
结果:
SpringBoot 先按条件装配检查了容器中没有 tom22 这个组件,然后下面的自动装配 tom22 的 @Bean 注解就不生效了。
如果将 @ConditionalOnBean 改成 @ConditionalOnMissingBean,那么 user01 和 tom22 组件将被装配进入容器。
-
6. @ImportResources(“classpath:beans.xml”)
此注解允许我们使用 Spring 中 xml 文件配置组件的方式注入容器。
7. @ConfigurationProperties
只有在容器中的组件,才能使用 SpringBoot 提供的功能!
首先创建实体类 Car
/**
* @Classname Car
* @Description 测试 @ConfigurationProperties 注解,此注解是 SpringBoot 提供的功能,只对容器中的组件生效。
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component // 注入容器
@ConfigurationProperties(prefix = "mycar") // prefix 属性用于设置配置文件的前缀
public class Car {
private String brand; // 品牌
private Integer price; // 价格
}
在 application.properties 配置文件中配置如下属性:
mycar.brand=BYD
mycar.price=100
测试:
@RestController
public class HelloController {
@Autowired
private Car car;
@RequestMapping("/car")
public Car car() {
return car;
}
@RequestMapping("/hello")
public String hello() {
return "hello,SpringBoot2!";
}
}
注意:修改配置文件后要重启项目。
8. @EnableConfigurationProperties(Car.class)
- 可以开启实体类的属性配置功能(将组件直接注入容器也能达到同样的效果),相当于在 Car 类上使用注解 @Component
- 把 Car 这个组件自动注册到容器中
六、自动配置原理
6.1 引导加载自动配置类
核心注解:@SpringBootApplication 相当于三个注解:@SpringBootConfiguration、@EnableAutoConfiguration 和 @ComponentScan(“com.xxx.xxx”)
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
-
@SpringBootConfiguration
@Configuration 代表当前类是一个配置类(说明 MainApplication 主程序类也是一个配置类,所以也可以称其为核心配置类)
-
@ComponentScan
指定扫描哪些包,这是个 Spring 注解
-
@EnableAutoConfiguration *
是一个合成注解,最重要的两个:
-
@AutoConfigurationPackage 自动配置包 -> @Import 注解导入了 Registrar.class,这个类中的 registerBeanDefinitions 方法会批量向容器中批量注册组件
registerBeanDefinitions 方法的参数 metadata 相当于注解的元信息,元信息会获取到是哪个类使用了此注解。因为 @AutoConfigurationPackage 是一层一层合成起来的,所以最终相当于标注在了 MainApplication 核心配置类上面。
源码第 124 行使用元信息获取到了核心配置类的完整包路径,并生成了一个数组,最终将 MainApplicaiton 类所在的包及其子包中的组件批量注册到容器中。
@AutoConfigurationPackage 注解,也就是 registerBeanDefinitions 方法最终做的事就是将 MainApplicaiton 类所在的包及其子包中所有的组件批量的注册到容器。
-
@Import(AutoConfigurationImportSelector.class)
-
利用 Select 机制,也就是源码第 99 行 getAutoConfigurationEntry(annotationMetadata) 方法,给容器中批量导入一些东西。
-
在 getAutoConfigurationEntry(annotationMetadata) 方法中,通过第 123 行 getCandidateConfigurations() 方法获取到了所有候选的配置类,再移除了一些重复的和注解标明排除的,最后加载了 127 个组件。
-
getCandidateConfigurations() 方法内部通过 Spring 的工厂加载机制 SpringFactoriesLoader 的 Map<String, List> loadSpringFactories(@Nullable ClassLoader classLoader) 方法得到所有组件
-
loadSpringFactories() 方法会从 META-INF/spring.factories 这个位置来加载一个文件,也就是 SpringBoot 默认扫描我们当前系统里面所有 META-INF/spring.factories 位置的文件。
spring-boot-autoconfigure-2.3.4.RELEASE.jar 包里面也有 META-INF/spring.factories 文件。
-
因为文件中写死了要加载的配置类,所以 SpringBoot 一启动,就会加载所有的 127 个配置类。
-
-
6.2 按需开启自动配置项
虽然我们 127 个场景的所有自动配置启动的时候默认全部加载,但是得益于 @Conditional 等条件装配注解的规则,SpringBoot 最终会按需加载配置。
6.3 修改默认配置
/*
下面这段代码来自 spring-boot-autoconfigure-2.3.4.RELEASE.jar
package org.springframework.boot.autoconfigure.web.servlet; 第 99 行。
作用是给容器中添加文件上传解析器。
*/
@Bean
@ConditionalOnBean(MultipartResolver.class) // 此条件判断了容器中有这个类型的组件
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) // 此条件判断容器中有这个名字的组件
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// 给 @Bean 标注的方法传入了对象参数,这个参数的值就会从容器中找。
// 也就是说,如果用户自定义了一个文件上传解析器,但是名字不叫这个,SpringBoot 也能解决这个问题
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
- SpringBoot 默认会在底层配好所有的组件,但是用户自己配置了,会以用户自己的优先。(最常见的就是自定义的 CharacterEncodingFilter)
总结:
-
SpringBoot 先加载所有的自动配置类(XxxxAutoConfiguration)。
-
每个自动配置类按照条件生效,不是全部生效;默认都会绑定配置文件指定的值,XxxxProperties 文件里面拿,XxxProperties 和配置文件进行了绑定。
-
生效的配置类就会给容器中装配很多的组件。
-
只要容器中有这些组件,就相当于这些功能就有了。
-
只要有用户自定义的配置,就以自定义的优先。
-
定制化配置
-
用户自己 @Bean 替换掉底层组件
-
用户去看这个组件是获取的配置文件什么值,就去修改配置文件,例如修改字符编码拦截器:
-
-
XxxxAutoConfiguration —> 组件 —> XxxProperties 里面拿值 —> appliction.properties
-
SpringBoot 中一般改 application.properties 文件就能修改所有的默认值。
6.4 最佳实践
-
引入场景依赖
参见官方文档。
-
查看自动配置了哪些(选做)
-
去 jar 包自己分析。(有些麻烦,而且引入场景对应的自动配置一般都生效了。)
-
在 application.properties 中添加
debug=true
开启 debug 模式,再运行程序会有自动配置报告:Negative matches 为不生效的\不匹配的
Positive matches 为生效的
-
-
是否需要修改配置项
-
参照官方文档修改配置项。
-
自己分析 XxxxProperties 绑定了配置文件的哪些前缀。
-
举例,替换 SpringBoot 默认的启动图片:
先去官网查询前缀,然后修改 application.properties 配置文件。
spring.banner.image.location=classpath:javadeveloper.jpg
-
自定义加入或者替换组件
@Bean @Component
-
自定义器 XXXCustomizer(将已有组件的行为自定义)
-
…
-
七、开发技巧
7.1 Lombok
简化 JavaBean 开发(使用注解简化 getter、setter、toString等方法的开发),Lombok 会在编译时加入需要的 getter、setter 等方法,SpringBoot 对 Lombok 有很好的支持。
使用:
-
导入依赖:
<!--lombok,省略getter、setter等--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
-
安装插件
-
使用
-
@Data 注解相当于 getter、setter方法
-
@NoArgsConstractor 注解相当于无参构造器
-
@AllArgsConstractor 注解相当于全部参数参构造器
-
@ToString 注解
-
@EqualsAndHashCode 注解
-
@Slf4j 注解是日志注解,在方法中配合 log.info(“hello”); 可以在程序运行时控制台打印日志。
7.2 Developer Tools 热更新\热部署
-
官方文档引入依赖。
-
加入依赖之后,每次在项目中做更改就不用重启项目了,只需要点编译按钮或者快捷键 Ctrl + F9。
-
但这样每次还要编译一次,并不是实时的热部署,不过对于开发静态页面来说就不需要重启了。
7.3 Spring Initailzr 项目初始化向导
-
新建项目时选择 Spring Initailzr
-
选择 SpringBoot 版本、勾选需要的开发场景
-
完成创建后,各种开发场景的依赖和父项目会被自动依赖
-
resources 包中自动创建了全局配置文件 application.properties 、静态资源包 static 和 templates 页面包
-
自动创建好了主程序类