1、基础入门案例
Step1、导入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
Step2、编写主程序
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
Step3、编写业务
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/hello")
public String handle01(){
return "Hello, Spring Boot 2!";
}
}
Step4、运行程序
- 运行
MainApplication
类 - 浏览器输入
http://localhost:8080/hello
,将会输出Hello, Spring Boot 2!
。
Step5、打包部署
pom文件中添加
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
在IDEA的Maven插件上点击运行 clean 、package,把helloworld工程项目的打包成jar包,
打包好的jar包被生成在helloworld工程项目的target文件夹内。
用cmd运行java -jar boot-01-helloworld-1.0-SNAPSHOT.jar
,既可以运行helloworld工程项目。
将jar包直接在目标服务器执行即可。
2、了解自动配置原理
2.1、依赖管理
springboot的父工程提供了依赖管理机制,声明了开发中常用的依赖的版本号。
<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>
如果提供的默认版本号不符合开发要求,可以在pom.xml中进行重写配置(maven的就近优先原则)
案例:修改mysql依赖的版本号
<properties>
<mysql.version>5.1.43</mysql.version>
</properties>
2.2、start场景启动器
spring-boot-start-*
:*
表示某种场景
*-spring-boot-start
:第三方提供的场景启动器
导入start后,该场景下所需要的依赖将会自动引入。
所有的场景启动器最底层的依赖是
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
常见的start
开发web项目的场景:spring-boot-starter-web
开发数据库jdbc的场景:spring-boot-starter-data-jdbc
开发redis:spring-boot-starter-data-redis
2.3、自动配置
-
自动配置好了Tomcat
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>2.3.4.RELEASE</version> <scope>compile</scope> </dependency>
-
自动配置好了SpringMVC
- 前端控制器DispatcherServlet:拦截所有前端发送过来的请求
- 字符编码CharacterEncodingFilter:解决中文乱码问题
- 视图解析器viewResolver:对返回的视图进行渲染
- 文件上传解析器multipatResolver:文件上传
这些组件在springboot启动时都会装配到容器中。
-
自动配置好了web常见功能,如字符编码问题
-
各种配置拥有默认值
- 默认配置最终都是映射到某个类上,如:MultipartProperties
- 配置文件的值最终会绑定到某个类上,这个类会在容器中创建对象
-
按需加载所有自动配置项
- 引入了某个start,这个场景的自动配置才会开启
- springboot所有的自动配置功能都在spring-boot-autoconfiguer包里面
- SpringBoot所有的自动配置都依赖
@EnableAutoConfiguration
2.4、默认的包结构
- 主程序所在的包(即MainApplication 所在的包)及其下面的所有子包里面的组件都会被默认扫描进来,无需再配置以前的包扫描
- 通过
@SpringBootApplication(scanBasePackages="com.huawei")
或者@ComponentScan
可以指定扫描路径,扩大扫描包的范围
@SpringBootApplication
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.atguigu.boot")
注意:@ComponentScan
注解包含在@SpringBootApplication
注解中,所有二者不能同时在一个类中使用。所以如果要改变包扫描规则,不能直接在@SpringBootApplication
上面写@ComponentScan
,只能是将@SpringBootApplication
替换为三个注解。
3、容器功能
3.1、组件添加
3.1.1、@Configuration
Full模式和Lite模式
Full模式获取组件时会先检查容器中是否有该组件,Lite模式不会检查容器,而是直接创建一个新组件返回。默认是Full模式。
boolean proxyBeanMethods() default true;
-
何时为Lite模式
- 类上有
@Component
注解 - 类上有
@ComponentScan
注解 - 类上有
@Import
注解 - 类上有
@ImportResource
注解 - 类上没有任何注解,但是类中存在
@Bean
注解的方法
- 类上有
-
Lite模式总结:
运行时不用生成CGLIB子类,提高运行性能,降低启动时间,可以作为普通类使用。但是不能声明@Bean之间的依赖 -
何时为Full模式
- 类上有
@Configuration
或者@Configuration(proxyBeanMethods = true)
的类称为Full模式的配置类。
- 类上有
-
Full模式总结
单例模式能有效避免Lite模式下的错误。性能没有Lite模式好
最佳实战
- 配置类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
- 配置类组件之间有依赖关系,方法会被调用得到之前的单实例组件,用Full模式
该功能能决定内部的bean是否要被代理来巩固bean生命周期表现。比如即使用户直接调取了bean方法,也能返回一个单例实例。
当值为True时,用户调用组件注册方法,会由内部代理来指向该类方法,并返回一个单例实例。被称作“Full Mode”,往往用在需要组件依赖时。该特质要求配置类满足CGLIB子类,并且类中的方法不能时final。
当值为False时,用户会直接调用该类。多次调用,会导致返回的对象引用不一致。这也被叫做“Lite Mode”,轻量级操作,节约了时间。It is therefore behaviorally equivalent to removing the @Configuration stereotype.
问:@Configuration(proxyBeanMethods = false)代理的是Myconfig里面的方法,如果我重复获取两次Myconfig容器,其实是还是同一个容器,对吗?
答:肯定是同一个容器,只是不同的组件,false是Lite模式,每次获取都会创建新的组件,true是Full模式,相当于已经把组件注入到了容器中,每次需要使用的时候都会去容器中查找,不管你用多少次找到的都是从容器中拿到的同一个组件
Configuration使用示例
/**
* 1、配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
* 2、配置类本身也是组件
* 3、proxyBeanMethods:代理bean的方法
* Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
* Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
* 组件依赖必须使用Full模式默认。其他默认是否Lite模式
*/
@Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件, 如果为true则该配置类是代理类
public class MyConfig {
/**
* Full:外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
* @return
*/
@Bean //给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
public User user01(){
User zhangsan = new User("zhangsan", 18);
//user组件依赖了Pet组件
zhangsan.setPet(tomcatPet());
return zhangsan;
}
@Bean("tom")
public Pet tomcatPet(){
return new Pet("tomcat");
}
}
@Configuration测试代码
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.atguigu.boot")
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));
//4、com.atguigu.boot.config.MyConfig$$EnhancerBySpringCGLIB$$51f1e1ca@1654a892
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
//如果@Configuration(proxyBeanMethods = true)代理对象调用方法。SpringBoot总会检查这个组件是否在容器中有。
//保持组件单实例
User user = bean.user01();
User user1 = bean.user01();
System.out.println(user == user1);
User user01 = run.getBean("user01", User.class);
Pet tom = run.getBean("tom", Pet.class);
System.out.println("用户的宠物:"+(user01.getPet() == tom));
}
}
3.1.2、@Bean、@Component、@Controller、@Service、@Repository
3.1.3、@ComponentScan、@Import
/*
*@Import自动调用无参构造器创建对象并放到IOC容器中、默认组件的名字就是全类名
*/
@Import({User.class, DBHelper.class})
@Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {
}
3.1.4、@Conditional
条件装配:只有满足Condition指定的条件,才能进行组件注入
@Conditional
派生了很多的子注解,它们可以添加在@Bean
注解的方法上也可以添加在配置类上。添加在方法上表示满足条件时则执行方法中内容并注册到 IOC 容器中如果不满足条件则不注册。添加在配置类上表示满足条件时则执行配置类中所有的@Bean
方法并注册到 IOC 容器中如果不满足条件则不注册。以@ConditionalOnBean(name="tom")
为例,当 IOC 容器中拥有id为tom的组件时才会满足条件,否则不满足条件。
条件装配演示
@Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件
//@ConditionalOnBean(name = "tom")
@ConditionalOnMissingBean(name = "tom")
public class MyConfig {
/**
* Full:外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
* @return
*/
@Bean //给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
public User user01(){
User zhangsan = new User("zhangsan", 18);
//user组件依赖了Pet组件
zhangsan.setPet(tomcatPet());
return zhangsan;
}
@Bean("tom22")
public Pet tomcatPet(){
return new Pet("tomcat");
}
}
测试条件装配
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);
}
boolean tom = run.containsBean("tom");
System.out.println("容器中Tom组件:"+tom);
boolean user01 = run.containsBean("user01");
System.out.println("容器中user01组件:"+user01);
boolean tom22 = run.containsBean("tom22");
System.out.println("容器中tom22组件:"+tom22);
}
3.2、原生配置文件引入
指以xml结尾的配置文件,通过@ImportResource
导入后SpringBoot进行解析,完成对应的组件注册
位置:在主配置类的上方
beans.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"
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>
@ImportResource("classpath:beans.xml")
public class MyConfig {}
boolean haha = run.containsBean("haha");
boolean hehe = run.containsBean("hehe");
System.out.println("haha:"+haha);//true
System.out.println("hehe:"+hehe);//true
3.3、配置绑定
使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用。
场景例子:我们习惯将经常爱变化的东西写在.properties配置文件中,比如与数据库相关的信息(连接池、URL等)配置到配置文件中,为了方便我们会将配置文件中的内容解析到JavaBean中。这个过程使用java原生代码较为麻烦。
3.3.1、@Component + @ConfigurationProperties
/**
* 只有在容器中的组件,才会拥有SpringBoot提供的强大功能
*/
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
private String brand;
private Integer price;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
'}';
}
}
3.3.2、@EnableConfigurationProperties+@ConfigurationProperties
4、自动配置原理入门
4.1、引导加载自动配置类
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication{}
4.1.1、@SpringBootConfiguration
代表当前是一个配置类;
4.1.2、@ComponentScan
指定扫描哪些Spring注解;
4.1.3、@EnableAutoConfiguration
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
4.1.3.1、@AutoConfigurationPackage
自动配置包,指定了默认的包规则
//给容器中导入一个组件
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}
//利用Registrar给容器中导入一系列组件
//将指定的一个包下的所有组件导入进来。MainApplication 所在包下。
4.1.3.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
4.2、按需开启自动配置项
虽然场景的所有自动配置(xxxxAutoConfiguration)启动的时候默认全部加载。
但是xxxxAutoConfiguration类上会有@Conditional
注解,最终会按需配置。
4.3、修改默认配置
@Bean
@ConditionalOnBean(MultipartResolver.class) //容器中有这个类型组件
@ConditionalOnMissingBean(name=DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字 multipartResolver 的组件
public MultipartResolver multipartResolver(MultipartResolver resolver) {
//给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
//SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
SpringBoot默认会在底层配好所有的组件。如果用户自己配置了以用户的优先。
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
}
4.4、总结
- SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
- 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和自动配置类进行了绑定
- 生效的配置类就会给容器中装配很多组件
- 只要容器中有这些组件,相当于这些功能就有了
- 定制化配置
- 用户直接自己
@Bean
替换底层的组件 - 用户去看这个组件是获取的配置文件什么值就去修改。
- 用户直接自己
xxxxxAutoConfiguration —> 组件 —>xxxxProperties里面拿值 ----> application.properties