面试金字塔尖之springboot
一、概述
基于 Spring 的全新框架,旨在简化 Spring Framework 应用的初始搭建和开发过程,核心设计思想是“约定优于配置”,使用最少的配置,以最快的速度来启动和运行 Spring 项目。 其特点如下:
- 遵循“约定优于配置”的原则,使用 Spring Boot 只需要很少的配置或使用默认的配置。
- 使用 JavaConfig,避免使用 XML 的繁琐。
- 提供 Starters(启动器),简化 Maven 配置,避免依赖冲突。
- 提供内嵌 Servlet 容器,可选择内嵌 Tomcat、Jetty 等容器,不需要单独的 Web 服务器。这意味着不再需要启动 Tomcat 或其他任何中间件。
- 提供了一系列项目中常见的非功能特性,如安全监控、应用监控、健康检测等。
- 与微服务的天然集成。
Spring Boot 是在 Spring 的基础上发展而来的,它不是为了取代 Spring,而是为了简化 Spring 应用的创建、运行、调试、部署,让开发者更容易地使用 Spring。
官网:
https://docs.spring.io/spring-boot/docs/2.7.11/reference/html/features.html#features.spring-application
二、入门回顾
2.1 基本步骤:
步骤一:创建普通maven项目
步骤二:pom.xml引入boot父工程
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.4</version>
</parent>
步骤三:引入某功能的起步依赖
<!--web起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
不需要写依赖坐标的版本号,已经被spring-boot-starter-parent管理!
步骤四:创建引导类/启动类
@SpringBootApplication
public class TliasApplication {
public static void main(String[] args) {
SpringApplication.run(TliasApplication.class, args);
}
}
步骤五:配置文件application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/tlias?&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
servlet:
multipart:
#一次请求中每个文件不得大于2M
max-file-size: 2MB
#一次请求中总文件不得大于2M
max-request-size: 2MB
步骤六:开发业务代码
@RestController
public class CommonController {
@PostMapping("/hello")
public String uploadImage(String name){
return "hello" + name;
}
}
2.2 注意事项
1)版本控制
spring-boot-starter-parent对大部分功能坐标版本都进行了管理!
> ```
> dependencyManagement
> maven的版本控制,只负责控制项目中依赖版本号,不负责引入依赖!
> ```
### 2)起步依赖坐标
```java
spring-boot-starter-web是springboot提前设定好的坐标,其中管理了web开发、运行所需依赖
spring-boot-starter-web起步依赖又依赖了其他的坐标(与该功能相关的使用坐标)。利用maven的依赖传递特性,将某功能所需的依赖都拉过来!
3)配置文件
基于“约定优于配置”思想,大部分配置都有默认值,如需变更,则在配置文件中覆盖即可。
三、配置文件说明
https://docs.spring.io/spring-boot/docs/2.7.11/reference/html/features.html#features.external-config
3.1 配置文件类型
1) .properties
2) .yml
3) .yaml
内容语法:
key=value
key: value
配置项读取
1、@Value("${key}")
2、@ConfigurationProperties(prefix = "")
3.2 配置文件使用优先级
properties
yml
yaml
放置目录放到classpath:/config (优先级更高)
Java系统属性配置 (格式: -Dkey=value)
-Dserver.port=8001
命令行参数 (格式:--key=value)
--server.port=8002
作用1:
开发时启动多个应用:
作用2:
项目部署时启动指定参数。
java -jar spring-boot-entry.jar --server.port=8110
其他:
可以在jar的位置放置配置文件,其中配置项优先级高于jar中配置项
java -jar spring-boot-entry.jar --spring.profiles.active=prod
四、bean管理
4.1 自己的bean管理
维护项目中自己想要放到spring容器中的对象(bean)
@Component //及其衍生注解
@Controller
@Service
@Configration
@Mapper //(mybatis-spring借助spring的IOC技术完成对象管理,并交给spring容器)
要想自己的bean受管理,类需要声明在启动类所在包下!
4.2 第三方bean管理(掌握)
使用@Bean管理
@Configuration
public class DruidConfig {
@Bean//类似于将@Configuration写在类上;
public DataSource dataSource(@Qualifier("druidParam") DruidParam druidParam){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(druidParam.getDriverName());
druidDataSource.setUrl(druidParam.getUrl());
druidDataSource.setPassword(druidParam.getPassword());
druidDataSource.setUsername(druidParam.getUsername());
return druidDataSource;
}
}
如何在@Bean注解基础上注入属性(DI)
//需求:druidDataSource属性赋值,值不是写死的!而是通过配置文件进行读取
datasource:
driverName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///tlias
username: root
password: root123
@Component
@ConfigurationProperties(prefix = "datasource")
@Data
public class DruidParam {
private String driverName;
private String url;
private String username;
private String password;
}
@Configuration
public class DruidConfig {
//需求:druidDataSource属性赋值,值不是写死的!而是通过配置文件进行读取
@Bean//类似于将@Configuration写在类上;
public DataSource dataSource(@Qualifier("druidParam") DruidParam druidParam){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(druidParam.getDriverName());
druidDataSource.setUrl(druidParam.getUrl());
druidDataSource.setPassword(druidParam.getPassword());
druidDataSource.setUsername(druidParam.getUsername());
return druidDataSource;
}
}
五、自动装配原理
5.1 了解自动配置过程描述
1)启动类
//@SpringBootApplication springboot关键注解 作用繁重
@SpringBootApplication
public class ReadApp {
public static void main(String[] args) {
// run 做spring容器管理 bean自动创建
SpringApplication.run(ReadApp.class, args);
}
}
2)@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}
3)@EnableAutoConfiguration
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
4)@Import(AutoConfigurationImportSelector.class)
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
//AutoConfigurationImportSelector类
//String[] 返回值 放着要被自动管理bean对象的 全限定类名字符串
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 获取自动配置类
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
5)getAutoConfigurationEntry(annotationMetadata);
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取自动配置类 全限定类名
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
6)getCandidateConfigurations(annotationMetadata, attributes);
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
META-INF/spring.factories
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
7)spring容器拿到这些字符串基于IOC管理bean
难道都要生成这些配置bean和里面的bean?不是的,需要@Conditional进行判断,只有判断通过的才创建bean对象,并放到容器。
5.2 条件判断
-
@ConditionalOnClass:判断环境中有对应字节码文件,才注册bean到IOC容器。
-
@ConditionalOnMissingBean:判断环境中没有对应的bean(类型或名称),才注册bean到IOC容器。
-
@ConditionalOnProperty:判断配置文件中有对应属性和值,才注册bean到IOC容器。
5.3 @Import注解
作用:
① 导入Bean
② 导入配置类
③ 导入 ImportSelector 实现类。一般用于加载配置文件中的类
④ 导入 ImportBeanDefinitionRegistrar 实现类。
作用2演示:
第三方包中有一个配置类,其中有一个bean
@Configuration
public class JacksonConfig {
@Bean
// @ConditionalOnProperty(name = "shen", havingValue = "ikun")
// @ConditionalOnMissingBean
public ObjectMapper objectMapper(){
return new ObjectMapper();
}
}
我们自己的代码中可以直接导入
@SpringBootApplication
@Import({JacksonConfig.class})
public class MyUse3DApp {
public static void main(String[] args) {
SpringApplication.run(MyUse3DApp.class, args);
}
}
作用3演示:
第三方包中声明一个ImportSelector:
public class ImportJacksonBeanSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"org.itcast.jackson.bean.JacksonConfig"};
}
}
我们自己的代码中可以直接导入
@SpringBootApplication
@Import({ImportJacksonBeanSelector.class})
public class MyUse3DApp {
public static void main(String[] args) {
SpringApplication.run(MyUse3DApp.class, args);
}
}
5.4 @EnableXxx注解
底层就是@Import注解,为了美观,使用Enable注解
第三方包中声明一个EnableJK注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ImportJacksonBeanSelector.class)
public @interface EnableJK {
}
我们自己的代码中可以直接使用
@SpringBootApplication
@EnableJK
public class MyUse3DApp {
public static void main(String[] args) {
SpringApplication.run(MyUse3DApp.class, args);
}
}
面试题:boot自动配置过程
1)读取自动配置类
2)spring容器管理bean(有@Conditional注解),基于IOC底层源码(工厂模式+反射)
六、自定义starter
面试题:你能否自己写一个starter。
写starter的目的:抽取统一功能,只要你的项目依赖了,就可以完成自动装配,代码中可以直接注入使用!
可以模仿springboot中的starter,也可以模仿pagehelper的
需求:
抽取阿里云OSS功能,完成一个文件上传功能的starter,将来该功能可以被重复依赖!
转载自:https://juejin.cn/post/7227333256076541989