我们查看一下 pom.xml
文件中的项目依赖情况:
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.6.6version>
parent>
点进去之后发现其还有父项目:
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>2.6.6version>
parent>
点进去之后:
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0modelVersion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>2.6.6version>
<packaging>pompackaging>
<name>spring-boot-dependenciesname>
<description>Spring Boot Dependenciesdescription>
<url>https://spring.io/projects/spring-booturl>
<licenses>
<license>
<name>Apache License, Version 2.0name>
<url>https://www.apache.org/licenses/LICENSE-2.0url>
license>
licenses>
<developers>
<developer>
<name>Pivotalname>
<email>[email protected]email>
<organization>Pivotal Software, Inc.organization>
<organizationUrl>https://www.spring.ioorganizationUrl>
developer>
developers>
<scm>
<url>https://github.com/spring-projects/spring-booturl>
scm>
<properties>
<activemq.version>5.16.4activemq.version>
<antlr2.version>2.7.7antlr2.version>
<appengine-sdk.version>1.9.95appengine-sdk.version>
<artemis.version>2.19.1artemis.version>
......
父项目的主要功能就是依赖管理,几乎声明了所有开发中常用的依赖的版本号,自动版本仲裁机制。
无需关注版本号,自动版本仲裁
比如:我们想要引入mysql的依赖
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
在这里我们不需要指定版本号。刷新maven后,查看版本号为:8.0.28
可以查找一下spring-boot-dependencies
文件中指定的mysql版本号:
<mysql.version>8.0.28mysql.version>
可以修改默认版本号
spring-boot-dependencies
里面规定当前依赖的版本用的key;比如:就修改mysql的驱动版本为5.1.43
查看spring-boot-dependencies
里面规定当前依赖的版本用的key:(上边已经提到过了,使用的是
标签)
在当前项目的 pom.xml
中重写:
<properties>
<mysql.version>5.1.43mysql.version>
properties>
再次刷新maven,可以看到mysql的包就是5.1.43了。
spring-boot-starter-*
: *就某种场景
只要引入starter,这个场景的所有常规需要的依赖我们都自动引入
SpringBoot所有支持的场景
https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters
*-spring-boot-starter
: 第三方为我们提供的简化开发的场景启动器
所有场景启动器最底层的依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<version>2.6.6version>
<scope>compilescope>
dependency>
可以使用IDEA的功能,查看一下jar包的依赖关系:
自动配好Tomcat
引入Tomcat依赖
web的场景spring-boot-starter-web
中已经引入Tomcat:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
<version>2.6.6version>
<scope>compilescope>
dependency>
配置Tomcat
可以在统一的配置文件中进行配置即可。
自动配好SpringMVC
引入SpringMVC全套组件
自动配好SpringMVC常用组件(功能)
验证:在主程序中我们打印一下IOC容器中注册的Bean:
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
//1、返回IOC容器
ConfigurableApplicationContext run =
SpringApplication.run(MainApplication.class, args);
//2、查看容器里边的组件
String[] names = run.getBeanDefinitionNames();
for (String name :
names) {
System.out.println(name);
}
}
}
第一、在web应用中,肯定要配置的就是前端控制器:dispatcherServlet
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfiguration dispatcherServlet
第二、解决字符乱码问题,需要配置字符编码过滤器:characterEncodingFilter
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
characterEncodingFilter
第三、视图解析器
......
defaultViewResolver
viewResolver
......
第四、文件上传解析器:multipartResolver
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration
multipartConfigElement multipartResolver
默认的包结构
主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来
无需以前的包扫描配置
想要改变扫描路径,@SpringBootApplication(scanBasePackages="com.atguigu")
@ComponentScan
指定扫描路径查看 @SpringBootApplication
源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
......
是一个组合了其他注解的组合注解。
各种配置拥有默认值
按需加载所有自动配置项
spring-boot-autoconfigure
包里面准备两个组件User和Pet:
public class Pet {
private String name;
......
public class User {
private String name;
private Integer age;
......
之前使用Spring进行组件注册,我们需要创建一个**xml配置文件**,在里边使用
标签进行组件注册。
使用SpringBoot,则可以使用 @Configuration
注解。
使用 @Configuration
注解标注一个类(配置类),就相当于说明这是一个配置文件。
@Configuration //告诉SpringBoot,这是一个配置类 == 一个配置文件
public class MyConfig {
@Bean //给容器中添加组件,以方法名作为组件的id,返回类型是组件类型。返回的值,就是组件在容器中的实例
public User user01(){
return new User("zhangsan", 18);
}
@Bean("tom") //自定义组件名id
public Pet tomPet(){
return new Pet("tomPet");
}
}
使用 @Bean
注解给容器中注册组件:以方法名作为组件的id,返回类型是组件类型。返回的值,就是组件在容器中的实例。
也就是说,id默认为方法名,也可以自定义组件名id。
那么,如何查看容器中是否包含指定id的类呢?
在主程序中,从IOC容器中获取指定id名的Bean:
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
//1、返回IOC容器
ConfigurableApplicationContext run =
SpringApplication.run(MainApplication.class, args);
//2、查看容器里边的组件
String[] names = run.getBeanDefinitionNames();
for (String name :
names) {
System.out.println(name);
}
//3、从容器中获取组件
Pet tom01 = run.getBean("tom", Pet.class);
Pet tom02 = run.getBean("tom", Pet.class);
System.out.println(tom01 == tom02); //true
}
}
也就是说,使用 @Bean
默认就是单实例。而且配置类本身也是一个组件。
一些性质:
使用
@Bean
默认就是单实例配置类本身也是一个组件
MyConfig myConfig = run.getBean(MyConfig.class); System.out.println(myConfig); //com.atguigu.boot.config.MyConfig$$EnhancerBySpringCGLIB$$f0cbc915@c68a5f8
可以看到获取到的对象本身就是代理对象。
@Configuration()
中的属性proxyBeanMethods(代理bean方法),默认为true,意味着外部无论对配置类中的这个组件注册方法调用多少次,取到的都是之前注册到容器中的单实例对象User user01 = myConfig.user01(); User user02 = myConfig.user01(); System.out.println(user01 == user02); //true
从上面可知,myConfig本身就是代理对象。那么调用方法就是代理对象调用方法。SpringBoot总会检查这个组件是否在容器中有。总之,就是保持组件单实例。
如果把属性proxyBeanMethods改为false:
@Configuration(proxyBeanMethods = false)
System.out.println(user01 == user02); //false
这个属性解决的最主要问题就是:组件依赖!
- Full(proxyBeanMethods = true)【保证每个@Bean方法被调用多少次返回的组件都是单实例】
- Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
组件依赖必须使用Full模式默认。其他默认是否Lite模式。
一个例子:
我们想要在User类中添加一个Pet类的属性,也就是使得User组件依赖于Pet组件。
public class User { private String name; private Integer age; private Pet pet; ......
在配置文件中,在创建User的方法中获取Pet:
@Configuration(proxyBeanMethods = true) //告诉SpringBoot,这是一个配置类 == 一个配置文件 public class MyConfig { @Bean //给容器中添加组件,以方法名作为组件的id,返回类型是组件类型。返回的值,就是组件在容器中的实例 public User user01(){ User zhangsan = new User("zhangsan", 18); //user组件依赖了Pet组件 zhangsan.setPet(tomPet()); return zhangsan; } @Bean("tom") //自定义组件名id public Pet tomPet(){ return new Pet("tomPet"); } }
验证一下User类中的Pet和创建的Pet是否是同一个:
User user = run.getBean("user01", User.class); Pet tom = run.getBean("tom", Pet.class); System.out.println("用户的宠物:"+(user.getPet() == tom)); //true
但是如果把proxyBeanMethods改为false,程序就会报错!但可以运行:
System.out.println("用户的宠物:"+(user.getPet() == tom)); //false
基于上面的测试,总结最佳实战:
以前使用的注解也都还可以使用。
@Bean
:注册组件@Component
:组件@Controller
:控制器@Service
:业务逻辑组件@Repository
:数据库层组件@ComponentScan
:包扫描规则
@Import
:给容器中自动创建组件、默认组件的名字就是全类名
@Import({User.class})
@Configuration(proxyBeanMethods = false) //告诉SpringBoot,这是一个配置类 == 一个配置文件
public class MyConfig {
......
测试:
System.out.println("------------------");
String[] beanNamesForType = run.getBeanNamesForType(User.class);
for (String s : beanNamesForType) {
System.out.println(s);
}
------------------
com.atguigu.boot.bean.User //@Import导入的,全类名
user01 //是配置类中方法导入的
@Import
高级用法
条件装配:满足Conditional指定的条件,则进行组件注入
以 @ConditionalOnBean
为例(容器中有哪些组件了才注册…):
先把配置类中的@Bean("tom")
注释掉,这就说明,tomPet()
只是一个普通的方法了,不会向容器中注册组件。验证:
//@Bean("tom") //自定义组件名id
public Pet tomPet(){
return new Pet("tomPet");
}
boolean tom = run.containsBean("tom");
System.out.println("容器中Tom组件:"+tom); //容器中Tom组件:false
User中还是会调用tomPet()
方法,容器中还是会有User组件:
boolean user01 = run.containsBean("user01");
System.out.println("容器中User组件:"+user01); //容器中User组件:true
我们想要的效果是:如果容器中没有tom组件,那么也就不用向容器中注册User组件了。
@ConditionalOnBean(name = "tom")
@Bean //给容器中添加组件,以方法名作为组件的id,返回类型是组件类型。返回的值,就是组件在容器中的实例
public User user01(){
User zhangsan = new User("zhangsan", 18);
//user组件依赖了Pet组件
zhangsan.setPet(tomPet());
return zhangsan;
}
boolean user01 = run.containsBean("user01");
System.out.println("容器中User组件:"+user01); //容器中User组件:false
也可以将@ConditionalOnBean
标注在类上,这就表明:如果没有tom组件,那么该类中的所有组件(包括该类)将不再注册。
boolean tom = run.containsBean("tom");
System.out.println("容器中Tom组件:"+tom); //容器中Tom组件:false
boolean user01 = run.containsBean("user01");
System.out.println("容器中User组件:"+user01); //容器中User组件:false
boolean myConfig = run.containsBean("myConfig");
System.out.println("容器中myConfig组件:"+myConfig); //容器中myConfig组件:false
那么 @ConditionalOnMissingBean
(容器中没有哪些组件了才注册…):
//@ConditionalOnBean(name = "tom")
@ConditionalOnMissingBean(name = "tom2")
@Import({User.class})
@Configuration(proxyBeanMethods = true) //告诉SpringBoot,这是一个配置类 == 一个配置文件
public class MyConfig {
......
验证:
boolean tom = run.containsBean("tom");
System.out.println("容器中Tom组件:"+tom); //容器中Tom组件:true
boolean user01 = run.containsBean("user01");
System.out.println("容器中User组件:"+user01); //容器中User组件:true
boolean myConfig = run.containsBean("myConfig");
System.out.println("容器中myConfig组件:"+myConfig); //容器中myConfig组件:true
原生的beans.xml进行配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="haha" class="com.atguigu.boot.bean.User">
<property name="name" value="zhangsan">property>
<property name="age" value="18">property>
bean>
<bean id="hehe" class="com.atguigu.boot.bean.Pet">
<property name="name" value="tomcat">property>
bean>
beans>
那么SpringBoot中是否会有这些组件呢?
boolean haha = run.containsBean("haha");
System.out.println("容器中haha组件:"+haha); //容器中haha组件:false
boolean hehe = run.containsBean("hehe");
System.out.println("容器中hehe组件:"+hehe); //容器中hehe组件:false
那么,就可以使用@ImportResource
引入xml中的配置:
//@ConditionalOnBean(name = "tom")
@ConditionalOnMissingBean(name = "tom2")
@Import({User.class})
@ImportResource("classpath:beans.xml")
@Configuration(proxyBeanMethods = true) //告诉SpringBoot,这是一个配置类 == 一个配置文件
public class MyConfig {
......
再次测试:
boolean haha = run.containsBean("haha");
System.out.println("容器中haha组件:"+haha); //容器中haha组件:true
boolean hehe = run.containsBean("hehe");
System.out.println("容器中hehe组件:"+hehe); //容器中hehe组件:true
如何使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用;
新建一个Car类:
public class Car {
private String brand;
private Integer price;
......
在application.properties
配置文件中配置其属性:
mycar.brand=BYD
mycar.price=100000
要想获取到application.properties
配置文件的属性,有三种方法
第一种方法:使用@Component + @ConfigurationProperties
注解:
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
private String brand;
private Integer price;
......
我们在控制器中定义方法:
@RestController
public class HelloController {
@Autowired
Car car;
@RequestMapping("/car")
public Car car(){
return car;
}
......
测试:
第二种方法:使用@EnableConfigurationProperties + @ConfigurationProperties
//@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
在配置类中使用@EnableConfigurationProperties
@ConditionalOnMissingBean(name = "tom2")
@Import({User.class})
@ImportResource("classpath:beans.xml")
@EnableConfigurationProperties(Car.class) //1、开启属性绑定;2、把组件自动注册到容器中
@Configuration(proxyBeanMethods = true) //告诉SpringBoot,这是一个配置类 == 一个配置文件
public class MyConfig {
@EnableConfigurationProperties
有两个作用:
我们从主程序开始看起:
//@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.atguigu.boot")
public class MainApplication {
我们说,@SpringBootApplication
注解就相当于@SpringBootConfiguration
+@EnableAutoConfiguration
+@ComponentScan("com.atguigu.boot")
。
可以查看一下@SpringBootApplication
注解源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
......
@SpringBootConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
......
@Configuration
。代表当前是一个配置类。
@ComponentScan
指定扫描哪些。
@EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@AutoConfigurationPackage
+@Import
。那么这两个注解分别的作用是什么呢?
@AutoConfigurationPackage
:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class}) //给容器导入一个组件
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
那么,导入的这个组件Registrar是什么?
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
}
}
利用Registrar给容器中导入一系列组件。可以打个断点,调试:
可以看到registerBeanDefinitions()
方法传入了两个参数:AnnotationMetadata metadata
和BeanDefinitionRegistry registry
。
AnnotationMetadata metadata
:注解的元信息
其中就包含了注解标注的位置。位于class com.atguigu.boot.MainApplication
。
然后在方法中,new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames()
拿到元信息并获取其包名。
然后,将获取到的包名toArray(new String[0])
封装成数组,并作为AutoConfigurationPackages.register()
的参数,就相当于把该包下的所有组件批量注册进容器。
@Import
:
可以看到@Import({AutoConfigurationImportSelector.class})
,导入了一个AutoConfigurationImportSelector的类。查看源码:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
其中,有一个selectImports()
方法,返回的是String的数组。这个方法主要是调用getAutoConfigurationEntry()
方法,返回的是一个autoConfigurationEntry,之后autoConfigurationEntry.getConfigurations()
打包成数组返回。因此,我们需要重点关注的就是getAutoConfigurationEntry()
方法:
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
可以看到其中有一个List
语句(获取到所有需要导入到容器中的配置类),在获取到configurations的List集合后,下边进行了一系列的操作,最终返回。
那么它是如何进行获取配置类的呢?打上端点,调试:
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
利用工厂加载 Map
得到所有的组件。
从META-INF/spring.factories位置来加载一个文件:
默认扫描我们当前系统里面所有META-INF/spring.factories
位置的文件
其中最重要的是:spring-boot-autoconfigure-2.6.6.jar
包里面也有META-INF/spring.factories
要加载的其实是在这里边写死的。其中有一个# Auto Configure
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
......
一共有133个,正好对应上面的configurations
的个数。
其实,就是写死了。SpringBoot一启动,就要给容器中加载的配置类(组件)。
spring-boot-autoconfigure-xxx.jar/META-INF/spring.factories
但实际中,我们在加载SpringBoot时根本没有这么多组件:
//获取加载组件的数目
int beanDefinitionCount = run.getBeanDefinitionCount();
System.out.println(beanDefinitionCount); //1443
虽然spring.factories
中的133个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration
按照条件装配规则(@Conditional
),最终会按需配置。
比如:AOP的类
分析AOP的自动配置能否生效:
在spring.factories
中确实写上了AopAutoConfiguration。但它是不是真的能加载呢?
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnProperty(
prefix = "spring.aop", //配置文件中是否有spring.aop的配置
name = {"auto"}, //如果配置有auto
havingValue = "true", //并且值为true
matchIfMissing = true //就算没配置,也默认值为true
)
public class AopAutoConfiguration {
......
首先,使用@Configuration
,说明这是一个配置类。接着使用了@ConditionalOnProperty
,具体信息上边代码注释写明了。
在其中还定义了两个Class(类):
第一个:
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({Advice.class})
static class AspectJAutoProxyingConfiguration {
......
这也是一个@Configuration
,并且@ConditionalOnClass
是否使用了Advice.class
。具体看看Advice.class
:
import org.aspectj.weaver.Advice;
可以看到Advice.class
来自于aspectj
。如果我们没有导入aspectj
,那么这个类就不会生效,自然其下边的也都不会生效。
第二个:
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnMissingClass({"org.aspectj.weaver.Advice"}) //没有org.aspectj.weaver.Advice注册
@ConditionalOnProperty( //同上
prefix = "spring.aop",
name = {"proxy-target-class"},
havingValue = "true",
matchIfMissing = true
)
static class ClassProxyingConfiguration {
ClassProxyingConfiguration() {
......
@Configuration
表名也是一个配置类。@ConditionalOnMissingClass
也是按照条件注册。
分析web应用时,DispatcherServlet自动加载类时,有一段这样的代码:
@Bean
@ConditionalOnBean({MultipartResolver.class}) //容器中有这个类型组件
@ConditionalOnMissingBean( //容器中没有这个名字的组件
name = {"multipartResolver"}
)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
//给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找
//SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范(意思是即使用户起名不是multipartResolver,但经过这个方法处理后,名字还是multipartResolver)
return resolver;
}
给容器中添加文件上传解析器。具体的分析如注释。
我们再看一下HttpEncodingAutoConfiguration
自动配置类,我们知道这个是解决请求中文乱码问题的。
做一个测试:
@RequestMapping("/hello")
public String handle01(@RequestParam("name") String name){
return "Hello, Spring Boot 2!" + "你好:" + name;
}
@Configuration( //这是一个配置类
proxyBeanMethods = false
)
@EnableConfigurationProperties({ServerProperties.class}) //配置绑定
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({CharacterEncodingFilter.class}) //是否加载了CharacterEncodingFilter过滤器
@ConditionalOnProperty(
prefix = "server.servlet.encoding", //配置文件中是否有server.servlet.encoding
value = {"enabled"}, //默认是enabled
matchIfMissing = true //即使没有,也是true
)
public class HttpEncodingAutoConfiguration {
......
来看看下边做了什么:
@Bean
@ConditionalOnMissingBean //如果容器中配置了,就不配了;反之,这里配置
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.RESPONSE));
return filter;
}
注册了一个CharacterEncodingFilter,解决字符编码问题。
SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先。
总结:
- SpringBoot先加载所有的自动配置类
xxxxxAutoConfiguration
- 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定
- 生效的配置类就会给容器中装配很多组件
- 只要容器中有这些组件,相当于这些功能就有了
- 自定义配置
- 用户直接自己@Bean替换底层的组件
- 用户去看这个组件是获取的配置文件什么值就去修改
xxxxxAutoConfiguration —> 组件 —> xxxxProperties里面拿值 ----> application.properties
引入场景依赖
https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters
查看自动配置了哪些(选做)
是否需要修改
@Bean
、@Component
…简化JavaBean开发。
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
@Component
@ConfigurationProperties(prefix = "mycar")
@Data //属性的getter和setter方法
@ToString //toString方法
@AllArgsConstructor //全参构造器
@NoArgsConstructor //无参构造器
@EqualsAndHashCode //使用属性重写EqualsAndHashCode方法
public class Car {
private String brand;
private Integer price;
}
使用到的注解:@Data
+@ToString
+@AllArgsConstructor
+@NoArgsConstructor
+@EqualsAndHashCode
。
@RestController
@Slf4j
public class HelloController {
@Autowired
Car car;
@RequestMapping("/car")
public Car car(){
return car;
}
@RequestMapping("/hello")
public String handle01(@RequestParam("name") String name){
log.info("请求进来了...");
return "Hello, Spring Boot 2!" + "你好:" + name;
}
}
还有@Slf4j
注解的日志功能。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
热部署。只需要[Ctrl]+F9
。
不仅更新静态资源,类做了改变也会重启。