尚硅谷 SpringBoot2 学习笔记,官方连接。
<parent>
<artifactId>spring-boot-starter-parentartifactId>
<groupId>org.springframework.bootgroupId>
<version>2.3.4.RELEASEversion>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<version>2.3.4.RELEASEversion>
dependency>
dependencies>
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);
}
}
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!";
}
}
SpringBoot 程序中的所有配置信息都可以写在此文件中,不必像 SSM 阶段那样配置好几个 xml 文件了,体现了 SpringBoot 简化配置的特性。例如:
resources 包下新建 application.properties , 更改端口号为 8888
server.port=8888
配置不会写可以参照官方文档
<packaging>jarpackaging>
...
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.4.RELEASEversion>
parent>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>2.3.4.RELEASEversion>
parent>
<properties>
<mysql.version>5.4.1mysql.version>
<xxx.version>1.0.0xxx.version>
properties>
<dependencies>
<dependency>
<groupId>com.xxx.xxxgroupId>
<artifactId>xxxartifactId>
dependency>
dependencies>
官方很多启动器命名为:spring-boot-start-* *就是某种场景
例如:spring-boot-start-web spring-boot-start-amqp
所有官方场景启动器参见官方文档
只要项目中引入了场景启动器,启动器中的依赖都会自动引入项目
官方推荐自定义的场景启动器(包括第三方场景启动器)命名格式为:xxx-spring-boot-start
所有场景启动器都依赖 spring-boot-start (SpringBoot自动配置的核心依赖)
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<version>2.3.4.RELEASEversion>
<scope>compilescope>
dependency>
使用 boot 时,引入依赖一般可以不写版本号,因为有自动版本仲裁机制,但是引入的不是 spring 官方支持的 jar 时还是需要自定义版本号。
自动配置好了 Tomcat
自动配好了 SpringMVC
自动配好了 web 开发常见的功能,如:字符编码问题
默认的包结构
主程序类所在的包及其子包都在扫描范围内
不需要配置包扫描了(SpringMVC 中的 @ComponentScan 注解或者 xml 文件中的 bean 标签等)
可以在核心注解 @SpringBootApplication 的属性中指定扫描包
@SpringBootApplication(scanBasePackages="com.xxx")
还可以使用
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.xxx")
// 上面三个注解就相当于
@SpringBootApplication
各种配置拥有默认值
按需加载所有自动配置项
…
原来在 Spring 中配置一个组件要写一个 bean.xml 文件
<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);
条件装配:满足 Conditional 指定的条件,则进行条件注入
@Conditional 注解有很多派生注解(idea中按H查看类的继承树)
比如:
示例:
@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 组件将被装配进入容器。
此注解允许我们使用 Spring 中 xml 文件配置组件的方式注入容器。
只有在容器中的组件,才能使用 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!";
}
}
注意:修改配置文件后要重启项目。
核心注解:@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
loadSpringFactories() 方法会从 META-INF/spring.factories 这个位置来加载一个文件,也就是 SpringBoot 默认扫描我们当前系统里面所有 META-INF/spring.factories 位置的文件。
spring-boot-autoconfigure-2.3.4.RELEASE.jar 包里面也有 META-INF/spring.factories 文件。
因为文件中写死了要加载的配置类,所以 SpringBoot 一启动,就会加载所有的 127 个配置类。
虽然我们 127 个场景的所有自动配置启动的时候默认全部加载,但是得益于 @Conditional 等条件装配注解的规则,SpringBoot 最终会按需加载配置。
/*
下面这段代码来自 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 先加载所有的自动配置类(XxxxAutoConfiguration)。
每个自动配置类按照条件生效,不是全部生效;默认都会绑定配置文件指定的值,XxxxProperties 文件里面拿,XxxProperties 和配置文件进行了绑定。
生效的配置类就会给容器中装配很多的组件。
只要容器中有这些组件,就相当于这些功能就有了。
只要有用户自定义的配置,就以自定义的优先。
定制化配置
XxxxAutoConfiguration —> 组件 —> XxxProperties 里面拿值 —> appliction.properties
SpringBoot 中一般改 application.properties 文件就能修改所有的默认值。
引入场景依赖
参见官方文档。
查看自动配置了哪些(选做)
是否需要修改配置项
参照官方文档修改配置项。
自己分析 XxxxProperties 绑定了配置文件的哪些前缀。
举例,替换 SpringBoot 默认的启动图片:
先去官网查询前缀,然后修改 application.properties 配置文件。
spring.banner.image.location=classpath:javadeveloper.jpg
自定义加入或者替换组件
@Bean @Component
自定义器 XXXCustomizer(将已有组件的行为自定义)
…
简化 JavaBean 开发(使用注解简化 getter、setter、toString等方法的开发),Lombok 会在编译时加入需要的 getter、setter 等方法,SpringBoot 对 Lombok 有很好的支持。
使用:
导入依赖:
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
安装插件
使用
@Data 注解相当于 getter、setter方法
@NoArgsConstractor 注解相当于无参构造器
@AllArgsConstractor 注解相当于全部参数参构造器
@ToString 注解
@EqualsAndHashCode 注解
@Slf4j 注解是日志注解,在方法中配合 log.info(“hello”); 可以在程序运行时控制台打印日志。
官方文档引入依赖。
加入依赖之后,每次在项目中做更改就不用重启项目了,只需要点编译按钮或者快捷键 Ctrl + F9。
但这样每次还要编译一次,并不是实时的热部署,不过对于开发静态页面来说就不需要重启了。