1.第一个helloword程序
- 创建一个maven项目(不通过模板创建),将pom.xml配置文件中加上下列配置
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
</parent>
- 添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
- 创建主程序
//相当于将该类的所有方法都添加了@ResponseBody注解
@RestController
//springboot在添加了jar包的依赖自动配置spring的配置文件
@EnableAutoConfiguration
public class MyApplication {
@RequestMapping("/")
String home() {
return "Hello World!";
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
出现了下面的界面就表示运行成功,并且通过标记我们可以发现Tomcat的端口号,根据端口号加上需要访问的控制器,我们直接在浏览器中进行访问即可
- 导入对应的插件,我们可以将jar包打包成可以执行的方式
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
我们可以在命令行使用java -jar 的命令执行jar包,执行成功后我们也可以在浏览器中打开对应界面
2.自动配置
1.springboot特点
1.依赖管理
- 父项目对依赖进行统一的管理
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
org.springframework.boot的父项目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
- 开发导入场景启动器
1、spring-boot-starter-* : *就代表某种场景(web场景、数据库场景等)
2、只要引入相应场景的starter依赖,springboot就会自动将该场景所需要的依赖自动导入
3、SpringBoot所有支持的场景
https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter
4、见到的 *-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。
5、spring-boot-starter是所有场景启动器最底层的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
-
依赖的版本号自动配置
- 引入底层已有的依赖无需表示版本号
- 引入底层没有的依赖可以表示版本号
-
可以修改默认的版本号
1、查看spring-boot-dependencies里面规定当前依赖的版本用的key。
2、在当前项目里面重写配置
<properties>
<mysql.version>5.1.43</mysql.version>
</properties>
2.自动配置
- Tomcat
- 自动配置Tomcat
- 自动引入Tomcat的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
- SpringMVC
- 自动配置SpringMVC()
- 自动配置SpringMVC常用的组件
- web常用功能(乱码问题)
- SpringMVC自动配置web开发的常见场景
在主函数下编写下面的代码可以查看配置的所有组件:
ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);
String[] beanDefinitionNames = run.getBeanDefinitionNames(); //将所有注册的bean构造成一个数组
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
- 默认的包结构
- 主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来
- 无需以前的包扫描配置
- 想要改变扫描路径,@SpringBootApplication(scanBasePackages=“xxx”)或者@ComponentScan 指定扫描路径
@SpringBootApplication
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.xxx")
- 各种配置拥有默认值
- 默认配置最终都是映射到某个类上,如:MultipartProperties
- 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象
- 按需加载所有自动配置项
- 底层配置有多种的应用场景
- 引入了相应的场景这个场景的自动配置才会开启
- SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面
2.容器功能
2.1、组件添加
2.1.1、@Configuration
- 作用:告诉SpringBoot这是一个配置类 == xml配置文件
- full和lite两种模式的应用场景
- 配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
- 配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式
/**
* 1.配置类里面使用@Bean标注在方法上给容器添加组件,默认单实例
* 2.配置类本身也是组件(MyConfiguration也是一个组件)
* 3.proxyBeanMethods:表示代理bean的方法
* full(proxyBeanMethods = true)->产生组件依赖(某个组件的成员为其他组件)
* 外部无论对配置类中的某个组件注册方法调用多少次获取的都是之前注册容器的单实例对象,即每次调用都会到容器中来查找
* lite(proxyBeanMethods = false)->相对于full模式速度更快
* 每次调用配置类的组件注册方法并不会到容器中来进行查找,直接注册一个新对象
*/
@Configuration//告诉SpringBoot这是一个配置类 == xml配置文件
public class MyConfiguration {
@Bean//给容器中添加组件。方法名为组件id,返回类型为组件类型,返回值就是组件在容器中的实例
public User user01(){
return new User("张三",18);
}
@Bean("tom")
public Pet tomcat(){
return new Pet("tomcat");
}
}
//启动类测试(full模式)
public static void main(String[] args) {
//1.返回IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);
String[] beanDefinitionNames = run.getBeanDefinitionNames();
//2.获取IOC容器中的所有容器
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
//3.从容器中获取组件
Pet tom01 = run.getBean("tom",Pet.class);
Pet tom02 = run.getBean("tom",Pet.class);
System.out.println(tom01 == tom02);
MyConfiguration bean = run.getBean(MyConfiguration.class);
System.out.println(bean);
//4.使用代理对象调用方法
User user01 = bean.user01();
User user02 = bean.user01();
System.out.println(user01 == user02);
}
//输出:true
//启动类测试(lite模式)
public static void main(String[] args) {
//1.返回IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);
String[] beanDefinitionNames = run.getBeanDefinitionNames();
//2.获取IOC容器中的所有容器
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
//3.从容器中获取组件
Pet tom01 = run.getBean("tom",Pet.class);
Pet tom02 = run.getBean("tom",Pet.class);
System.out.println(tom01 == tom02);
MyConfiguration bean = run.getBean(MyConfiguration.class);
System.out.println(bean);
//4.使用代理对象调用方法
User user01 = bean.user01();
User user02 = bean.user01();
System.out.println(user01 == user02);
}
//输出:false
2.1.2、@Import、@ComponentScan
@Import作用:给容器中自动创建相应类型的组件,默认组件名为全类名
@ComponentScan:主要就是定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中
@Configuration(proxyBeanMethods = false)//告诉SpringBoot这是一个配置类 == xml配置文件
public class MyConfiguration {
@Bean//给容器中添加组件。方法名为组件id,返回类型为组件类型,返回值就是组件在容器中的实例
public User user01(){
return new User("张三",18);
}
@Bean("tom")
public Pet tomcat(){
return new Pet("tomcat");
}
}
//启动类
@RestController
//自动配置程序
@SpringBootApplication
//将"com.zhou.boot"路径下的所有需要自动装配的类装配到spring的bean容器中
@ComponentScan("com.zhou.boot")
public class MyApplication {
public static void main(String[] args) {
//1.返回IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);
String[] beanDefinitionNames = run.getBeanDefinitionNames();
//2.获取IOC容器中的所有容器
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
//3.从容器中获取组件
Pet tom01 = run.getBean("tom",Pet.class);
Pet tom02 = run.getBean("tom",Pet.class);
System.out.println(tom01 == tom02);
MyConfiguration bean = run.getBean(MyConfiguration.class);
System.out.println(bean);
//4.使用代理对象调用方法
User user01 = bean.user01();
User user02 = bean.user01();
System.out.println(user01 == user02);
String[] beanNamesForType = run.getBeanNamesForType(User.class);
System.out.println("=======");
for (String s : beanNamesForType) {
System.out.println(s);
}
System.out.println("=======");
AsyncAppender bean1 = run.getBean(AsyncAppender.class);
System.out.println(bean1);
}
}
=======
com.zhou.boot.bean.User->默认全类名(Import创建)
user01->根据bean注解创建的对应组件
=======
ch.qos.logback.classic.AsyncAppender[null]->默认全类名(Import创建)
2.1.3、@Conditional
条件装配:满足Conditional指定的条件,则进行组件注入
//注解用于某个方法
@Configuration(proxyBeanMethods = false)//告诉SpringBoot这是一个配置类 == xml配置文件
//@Import({User.class, AsyncAppender.class})
public class MyConfiguration {
@ConditionalOnBean(name = "tom22")//如果名为tom22的bean存在,user01才会被注册成bean
@Bean//给容器中添加组件。方法名为组件id,返回类型为组件类型,返回值就是组件在容器中的实例
public User user01(){
return new User("张三",18);
}
@Bean("tom")
public Pet tomcat(){
return new Pet("tomcat");
}
}
//实现类:
public static void main(String[] args) {
//1.返回IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);
String[] beanDefinitionNames = run.getBeanDefinitionNames();
//2.获取IOC容器中的所有容器
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
boolean user01 = run.containsBean("user01");
System.out.println("user01组件:" + user01);
}
//输出:user01组件:false
//注解用于某个类
@Configuration(proxyBeanMethods = false)//告诉SpringBoot这是一个配置类 == xml配置文件
//@Import({User.class, AsyncAppender.class})
@ConditionalOnBean(name = "tom22")//如果名为tom22的bean存在,类中的所有bean注解才会被生效
public class MyConfiguration {
@Bean//给容器中添加组件。方法名为组件id,返回类型为组件类型,返回值就是组件在容器中的实例
public User user01(){
return new User("张三",18);
}
@Bean("tom")
public Pet tomcat(){
return new Pet("tomcat");
}
}
//实现类:
public static void main(String[] args) {
//1.返回IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);
String[] beanDefinitionNames = run.getBeanDefinitionNames();
//2.获取IOC容器中的所有容器
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
boolean user01 = run.containsBean("user01");
System.out.println("user01组件:" + user01);
boolean tom01 = run.containsBean("tom");
System.out.println("tom组件:" + tom01);
}
//输出: user01组件:false
// tom组件:false
@conditional的继承树
@ConditionalOnBean():某个bean存在生效
@ConditionalOnMissingBean():某个bean不存在存在生效
等等
2.资源导入
2.2.1、@ImportSource
导入原生的配置文件
<?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="u" class="com.zhou.boot.bean.User">
<property name="name" value="xz"></property>
<property name="age" value="11"></property>
</bean>
<bean id="v" class="com.zhou.boot.bean.Pet">
<property name="name" value="tom"></property>
</bean>
</beans>
//未使用importSource注解
boolean u = run.containsBean("u");
System.out.println("u组件:" + u);
boolean v = run.containsBean("v");
System.out.println("v组件:" + v);
//输出:u组件:false
// v组件:false
//使用importSource注解
@Configuration(proxyBeanMethods = false)
//@Import({User.class, AsyncAppender.class})
@ImportResource("classpath:beans.xml")
public class MyConfiguration {...}
public static void main(String[] args) {
...;
boolean u = run.containsBean("u");
System.out.println("u组件:" + u);
boolean v = run.containsBean("v");
System.out.println("v组件:" + v);
}
//输出:u组件:true
// v组件:true
3.配置绑定
2.3.1、@ConfigurationProperties + @Component
mycar.name=BMW
mycar.speed=200km/h
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
@ConfigurationProperties(prefix = "mycar") //prefix表示前缀
public class Car {
private String name;
private String speed;
}
2.3.2、@ConfigurationProperties + @Component
实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
@ConfigurationProperties(prefix = "mycar")
public class Car {
private String name;
private String speed;
}
配置类:
@EnableConfigurationProperties(Car.class)
public class MyConfiguration {...}
3.自动配置原理
3.1、引导加载自动配置类
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication{}
======================
1、@SpringBootConfiguration
@Configuration。代表当前是一个配置类
2、@ComponentScan
指定扫描哪些,Spring注解;
3、@EnableAutoConfiguration
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
1、@AutoConfigurationPackage
自动配置包?指定了默认的包规则
@Import(AutoConfigurationPackages.Registrar.class) //给容器中导入一个组件
Registrar 中的registerBeanDefinitons方法的metadate参数指的是注解源,new PackaImports(metadata)导入包中的组件,getPackageNames()获得包名,toArray封装到数组中。
最终注册进去
public @interface AutoConfigurationPackage {}
//利用Registrar给容器中导入一系列组件
//将指定的一个包下的所有组件导入进来?MainApplication 所在包下。
2、@Import(AutoConfigurationImportSelector.class)
1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
2、调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
3、利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
4、从META-INF/spring.factories位置来加载一个文件。
默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
3.2、按需开启自动配置项
虽然我们127个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration
按照条件装配规则(@Conditional),最终会按需配置。
3.3、修改默认配置
@Bean
@ConditionalOnBean(MultipartResolver.class) //容器中有这个类型组件
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字 multipartResolver 的组件
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// 给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
// SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范
// 以方法注入组件,在不单独设置名字的基础上,方法名multipartResolver会作为组件名字注入容器中
return resolver;
}
给容器中加入了文件上传解析器;
SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了则以用户的优先
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
}
总结:
-
SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
-
每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties(由@EnableConfigurationProperties注解导入的)里面拿。xxxProperties和配置文件进行了绑定
-
生效的配置类就会给容器中装配很多组件
-
只要容器中有这些组件,相当于这些功能就有了
-
定制化配置
-
- 用户直接自己@Bean替换底层的组件
- 用户去看这个组件是获取的配置文件什么值就去修改。通过修改application.properties配置文件中的所需key对应的value进行自定义配置
xxxxxAutoConfiguration —> 组件 —> xxxxProperties里面拿值 ----> application.properties
ver;
}
给容器中加入了文件上传解析器;
**SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了则以用户的优先**
```java
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
}
总结:
-
SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
-
每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties(由@EnableConfigurationProperties注解导入的)里面拿。xxxProperties和配置文件进行了绑定
-
生效的配置类就会给容器中装配很多组件
-
只要容器中有这些组件,相当于这些功能就有了
-
定制化配置
-
- 用户直接自己@Bean替换底层的组件
- 用户去看这个组件是获取的配置文件什么值就去修改。通过修改application.properties配置文件中的所需key对应的value进行自定义配置
xxxxxAutoConfiguration —> 组件 —> xxxxProperties里面拿值 ----> application.properties