SpringBoot2(一)
一、Spring和SpringBoot
Spring:微服务、响应式布局(异步响应流的使用)、分布式云开发、web开发、serverless无服务开发(函数式服务)、事件驱动Event Driver、Batch批处理业务
SpringBoot是一个高层的框架,它的底层就是Spring,解决了Spring大量的”配置地狱“带来的麻烦
Spring5的重大升级,引入的新的响应式编程,有两个技术栈,一个是Servlet Stack ,另一个是Reactive Stack
SpringBoot就是快速的创建生产级别的应用,
优点:
1、可以创建一个独立的Spring
2、使用嵌入式的内部容器(内嵌的web服务器)
3、自动starter依赖,简化配置
4、自动配置Spring和第三方的功能
5、可以提供生产级别的监控、健康检查和外部化配置
6、无代码生成、无需编写xml
缺点:
1、人称版本帝,更新迭代快,需要时刻关注
2、封装太深,内部的原理复杂,不容易精通
springBoot是整合Spring技术栈的一站式框架
SpringBoot是简化Spring技术栈的脚手架
微服务
是一种架构风格
将一个应用拆分成一个一个的小型服务
每个服务可以运行在自己的进程,可以独立部署和升级
服务之间可以使用轻量级的http交互
服务围绕业务逻辑拆分
可以由全自动部署机制独立部署
去中心化(每种服务都可以用不同的语言编写)、服务自治
分布式:
分布式的困难: 远程调用、服务发现、负载均衡、服务容错、配置管理、服务监控、链路追踪、日志管理、任务调度
分布式的解决:
SpringBoot和SpringCloud
云原生:
服务自愈、弹性伸缩、服务隔离、自动化部署、灰度发布、流量治理
二、SpringBoot入门
首先创建maven工程
引入依赖、框架支持
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
创建主程序
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
编写业务程序
//@Controller
//@ResponseBody
// SpringBoot将上面的两个注解合并成了现在的下面的一个注解
@RestController
public class Helloworld {
@RequestMapping("/hello")
public String hello() {
return "Hello SpringBoot2!";
}
}
测试
直接运行main方法
简化配置
所有的配置都可以通过一个application.properties文件来进行配置
简化部署
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
把项目打成jar包,在目标服务器执行就可以
注意点:
取消cmd的快速编辑模式
首次使用的时候下载的东西很多,需要耐心等待
三、了解自动配置机制
1、依赖管理
开发导入starter场景启动器
父项目做依赖管理
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
</parent>
它的子项目中的第一个父项目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.5.3</version>
</parent>
在spring-boot-dependencies这个项目中包含了所有jar的版本信息,通过这个实现了依赖管理,称为自动版本仲裁机制
<properties>
<activemq.version>5.16.2</activemq.version>
<antlr2.version>2.7.7</antlr2.version>
<appengine-sdk.version>1.9.90</appengine-sdk.version>
<artemis.version>2.17.0</artemis.version>
<aspectj.version>1.9.7</aspectj.version>
<assertj.version>3.19.0</assertj.version>
<atomikos.version>4.0.6</atomikos.version>
<awaitility.version>4.0.3</awaitility.version>
<build-helper-maven-plugin.version>3.2.0</build-helper-maven-plugin.version>
<byte-buddy.version>1.10.22</byte-buddy.version>
<caffeine.version>2.9.2</caffeine.version>
<cassandra-driver.version>4.11.2</cassandra-driver.version>
<classmate.version>1.5.1</classmate.version>
<commons-codec.version>1.15</commons-codec.version>
<commons-dbcp2.version>2.8.0</commons-dbcp2.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<commons-pool.version>1.6</commons-pool.version>
<commons-pool2.version>2.9.0</commons-pool2.version>
<couchbase-client.version>3.1.6</couchbase-client.version>
<db2-jdbc.version>11.5.6.0</db2-jdbc.version>
<dependency-management-plugin.version>1.0.11.RELEASE</dependency-management-plugin.version>
<derby.version>10.14.2.0</derby.version>
<dropwizard-metrics.version>4.1.25</dropwizard-metrics.version>
<ehcache.version>2.10.9.2</ehcache.version>
<ehcache3.version>3.9.4</ehcache3.version>
.........
.....
....
如果想更改版本,首先查看spring-boot-dependencies里面规定的版本的名字,直接在自己项目的配置文件里面添加相应的配置即可
<properties>
<mysql.version>5.1.43</mysql.version>
</properties>
所有场景启动器的最底层的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.5.3</version>
<scope>compile</scope>
</dependency>
2、自动配置
自动配置tomcat
引入tomcat依赖
配置Tomcat
自动配置springmvc
自动引入springmvc的全部组件
自动配置好了springmvc的常用功能
自动配置web常见的场景
比如字符编码的问题
默认的包结构
主程序(main)所在的包及其下边的所有子包里面的组件都会被默认扫描进来
无需配置之间的包扫描
如果想要改变扫描的路径,就可以使用@SpringBootApplication(scanBasePackages = “com.itlf”)来改变
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.itlf") // 扫描包路径
// 这三句话相当于 @SpringBootApplication
各种配置拥有默认值
默认的配置最终都是映射到某一个类上面,这个类会在容器中有创建对象
按需加载所有的配置项
引入录入哪些场景,这个场景的配置才会开启
SpringBoot的自动配置功能都在spring-boot-autoconfigure
3、容器功能
3.1、组件添加
①、@Configuration
首先创建bean类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private int age;
}
然后创建一个类,配置类config.MyConfig
// 配置类中使用@Bean标注在方法上,给容器标注组件,默认也是单实例的
// 配置类本身也是组件
@Configuration// 告诉springboot这是一个配置类
// 其中有一个属性proxyBeanMethods:代理bean的方法
public class MyConfig {
/**
* 外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
* @return
*/
@Bean // 给容器中添加组件(方法名就是组件的id,返回值类型就是组件的类型。方法返回的值就是组件在容器中保存的实例)
public User user01() {
return new User(1, "贾可爱", 18);
}
@Bean("tom") // 自定义id
public Pet tomcat() {
return new Pet(1, "Tomcat");
}
}
@Configuration中的一个属性proxyBeanMethods默认是true,会自动调用代理对象的方法,返回的是单实例对象,修改为false,拿到的对象就不再是代理对象,多次调用会产生不同的对象
Full(proxyBeanMethods = true):全模式,将全部的组件都放进容器中,为的是在代码中组件之间有互相的依赖关系的
Lite(proxyBeanMethods = false):轻量级模式,如果一个代码中的上面的组件没有依赖下面的组件的话,可以使用轻量级的模式,这样会大大加快SpringBoot的启动速度
②、@Import
// 给容器中自动添加这个类型的组件
@Import(User.class)
// 从容器中获取组件
for (String s : run.getBeanNamesForType(User.class)) {
System.out.println(s);
}
输出结果:第一个是@Import(User.class)添加的,第二个是 @Bean // 给容器中添加组件来添加的
③、@Conditional
按照条件装配
@ConditionalOnBean(name = "tom") // 当容器中存在名字为tom组件的时候,才加载user01组件
@Bean // 给容器中添加组件(方法名就是组件的id,返回值类型就是组件的类型。方法返回的值就是组件在容器中保存的实例)
public User user01() {
return new User(1, "贾可爱", 18);
}
将@Conditional加载类上面,就是当容器中存在某个组件的时候,才加载这个类的内容,否则不加载
④、@ImportResource
原生配置文件的引入
// 在任意一个配置类里添加这个注解,就可以导入外部的xml文件
@ImportResource("classpath:beans.xml")
3.2、配置绑定
第一种方式:@Component + @ConfigurationProperties
首先创建一个properties配置文件,在配置文件中,写出相应的位置文件的信息
在创建一个实体类,用来接收这个配置文件中的信息,在实体类中加入
@Component // 这个是将这个bean类加载到容器中
@ConfigurationProperties(prefix = "mycar") // 这个是在配置文件中寻找一个为mycar为前缀的属性值,这个注解只是对application.properties这个生效,别的配置文件是不生效的
在controller包下的类中进行输出
@Autowired // 将car对象自动装配
Car car;
最后在浏览器访问/car资源
第二种方式:@EnableConfigurationProperties + @ConfigurationProperties
在任意一个配置类中写出这个注解
/*
1、开启Car的属性配置功能
2、把Car这个组件自动导入容器中
*/
@EnableConfigurationProperties(Car.class) // 开启Car.class的属性配置功能
在bean的想要实现属性配置的类中添加 @ConfigurationProperties(prefix = “mycar”)
这个时候就不需要再写@Component这个注解了,因为@EnableConfigurationProperties已经将Car这个组件注册到了容器当中了
这种方式适用于这个类是第三方包中的类,我们不适合在别人的class上面添加注解进行修改,所以就需要这个方式来添加属性配置
4、SpringBoot自动配置的原理入门
引导加载自动配置类
@SpringBootApplication,其中有三个组成部分
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication{}
@SpringBootConfiguration
代表当前是一个配置类
@EnableAutoConfiguration 开启自动配置(最重要的)
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration{}
1、@AutoConfigurationPackage 自动配置包
@Import({Registrar.class}) // 给容器中导入一个组件
public @interface AutoConfigurationPackage{}
Registrar的源码,说明通过AnnotationMetadata获取到这个注解的类,PackageImports(metadata)).getPackageNames()获取到这个类所在包的包名,存放在一个数组中,通过这个方法来实现,设个包下的所有的组件导入容器
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
2、@Import({AutoConfigurationImportSelector.class})
打开源码,其中核心的方法是
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata); // 重点是这个方法,这个方法getAutoConfigurationEntry获取了所需要的值
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
// 其中getAutoConfigurationEntry中的这句话是获取所有需要导入到容器中的配置类
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
// getCandidateConfigurations 方法
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
// loadFactoryNames
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
// loadSpringFactories
Map<String, List<String>> loadSpringFactories(ClassLoader classLoader){
...
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
// 会加载这个路径下的配置内容G:\maven_repository\org\springframework\boot\spring-boot-autoconfigure\2.5.3\spring-boot-autoconfigure-2.5.3.jar!\META-INF\spring.factories
}
这就是最核心的SpringBoot的自动配置原理,但是全加载(127个)进去不一定要全部生效,最终要按需配置
@ComponentScan
指定包扫描的路径
按需开启自动配置项
导入相关的包,才会有相应的类,这个才会生效
定制化修改自动配置
// 这个方法的主要目的就是将用户自定义的文件上传解析器的名字给规范命名为 multipartResolver ,就是起到了一个重命名的作用
// 方式有些用户配置的文件上传解析器不符合规范
@Bean
@ConditionalOnBean(MultipartResolver.class) // 当容器中有这个MultipartResolver类型的组件
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) // 容器中没有这个名字multipartResolver的组件
// 前面的两句注解的意思就是容器中存在一个MultipartResolver的对象,但是对象的名字不是multipartResolver的情况下SpringBoot的底层就会将这个不是规范名字的对象给规范化
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// 给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
// 给容器加入了文件上传解析器
SpringBoot乱码解析器
@Bean
@ConditionalOnMissingBean // SpringBoot默认会在底层配置好所有的组件,但是用户如果自己配置了,那就以用户的优先
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
return filter;
}
总结就是:
SpringBoot先加载所有的自动配置类,
每一个自动配置类按照条件自动生效,不是全部生效,
每一个配置类生效的时候都会绑定一个默认的properties配置文件,所有的值都是从这个配置文件中获取的
生效的自动配置类就会给容器中装配很多的组件,
只要容器中有这些组件,相当于这些功能就有了
只要用户有自己的配置,那么就以用户自己的优先
定制配置的两个方法
1、用户自己定义一个配置组件@Bean加入到容器中替换之前的组件
2、用户去看配置文件获取的是什么值,去修改配置文件即可
最佳实践
debug=true
开启自动配置报告,其中所有 Negative matches 就是未生效的,Positive matches就是生效的
可以自定义和替换组件,@Bean
自定义器 XXXXCustomizer