以前开发web都需要写servert,写web.xml配置文件,从spring3.0以后就可以直接使用注解的方式进行开发。
SpringBoot核心的功能就是BeanFactoryPostProcessor
spring中有个非常重要的方法: AbstractApplicationContext类中的refresh方法
springboot源码跟踪
refreshContext()方法 ---》 refresh()方法
面试题
spring boot的启动流程
自动装配原理
Bean的实例化过程
如何内嵌Tomcat
springBootApplication的启动步骤
Class that can be used to bootstrap and lanuch a Spring application from a Java main method. By default calss will perofrm the following steps to bootstrap your applicaton
- Create an appropriate ApplicationContext instance 创建ApplicationContext的实例
- Register a CommandLineProertySource to expose command line arguments as Sprin properties(注册命令行的参数解析,去暴露命令行的参数作为spring的属性值)
- Refresh the application context. Loading all singleton beans(刷新当前应用程序的上下文,加载所有的单例对象)
- Trigger any CommandLineRunner beans(触发所有命令行的runner)
new SpringApplication的构造方法中
new SpringApplication(primarySources)
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//这个web应用的类型有三种 REACTIVE(响应式) NONE SERVLET(默认)
this.webApplicationType = this.deduceWebApplicationType();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
1. 创建一个SpringApplication实例。
2. 从一个特殊的primary source(启动程序时指定好的这个类)中加载对应的bean(MainApplication.class)
SpringApplication.run(MainApplication.class, args);
3. 这些bean加载之前可以进行一些个性化的操作和配置
run方法
new SpringApplication(primarySources)).run(args)
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//创建一个上下文对象
ConfigurableApplicationContext context = null;
//创建一个异常报告器
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
//配置属性
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
在refresh()方法之前都是准备工作。
Environment
- systemproperties
- systemconfig
- contextinit
- <init-param>
- <name>
- <value>
springboot为什么可以简化配置?
自动装配
@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}
)}
)
概述
什么是Spring Boot
- 是Spring开源组织下的子项目,是Spring组件一站式解决方案,主要是简化了使用Spring的难度,简化了繁琐的配置,提供了各种启动器,开发者可以快速上手。
- 容易上手,提升开发效率,为Spring开发提供了一个更快、更广泛的入门体验。
- 开箱即用,远离繁琐的配置
- 提供了一系列大型项目通用的业务性功能,例如:内嵌服务器、安全管理、运行数据监控、运行状态检查和外部化配置等
- 没有代码生成,也不需要XML配置。
- 避免大量的Maven导入和各种版本冲突。
- 总结就是使编码变简单,使配置变简单,使部署变简单,使监控变简单。
配置
Spring Boot的核心注解是哪个?它主要由哪几个注解组成的
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
- @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
- @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能@SpringBootApplication(exclude{DataSourceAutoConfiguration.class})
- @ComponentScan:Spring组件扫描。
什么是 JavaConfig
Spring JavaConfig是Spring社区的产品,它提供了配置IOC容器的纯Java方法,因此它有助于避免使用XML配置,使用JavaConfig的优点在于:
- 面向对象的配置。由于配置被定义为 JavaConfig 中的类,因此用户可以充分利用 Java 中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean 方法等。
- 减少或消除 XML 配置。基于依赖注入原则的外化配置的好处已被证明。但是,许多开发人员不希望在 XML 和 Java 之间来回切换。JavaConfig 为开发人员提供了一种纯 Java 方法来配置与 XML 配置概念相似的 Spring 容器。从技术角度来讲,只使用 JavaConfig 配置类来配置容器是可行的,但实际上很多人认为将JavaConfig 与 XML 混合匹配是理想的。
- 类型安全和重构友好。JavaConfig 提供了一种类型安全的方法来配置 Spring容器。由于 Java 5.0 对泛型的支持,现在可以按类型而不是按名称检索 bean,不需要任何强制转换或基于字符串的查找。
- 常见的Java Config
- @Configuration:在类上打上写下此注解,表示这个类是配置类
- @ComponentScan:在配置类上添加 @ComponentScan 注解。该注解默认会扫描该类所在的包下所有的配置类,相当于之前的 <context:component-scan >。
- @Bean:bean的注入:相当于以前的< bean id=“objectMapper” class=“org.codehaus.jackson.map.ObjectMapper” />
- @EnableWebMvc:相当于xml的<mvc:annotation-driven >
- @ImportResource: 相当于xml的 < import resource=“applicationContext-cache.xml”>
Spring Boot 自动配置原理是什么
- 主要是Spring Boot的启动类上的核心注解SpringBootApplication注解主配置类,有了这个主配置类启动时就会为SpringBoot开启一个@EnableAutoConfiguration注解自动配置功能。
- 有了这个EnableAutoConfiguration的话就会:
- @EnableAutoConfiguration给容器导入META-INF/spring.factories里定义自动装配类,筛选有效的自动配置类。
- @Configuration每一个自动配置类结合对应的 xxxProperties.java 读取配置文件进行自动配置功能
你如何理解 Spring Boot 配置加载顺序
- 在Sring Boot里面,可以使用以下几种方式来加载配置
- properties文件
- yaml文件
- 系统环境变量
- 命令行参数等等
什么是YAML
- 是一种人类可读的数据序列化语言。通常用于配置文件。与属性文件相比,更加结构化、更少混淆,具有分层配置数据的功能。具有如下的优势
- 配置有序,在一些特殊的场景下,配置有序很关键
- 支持数组, 还支持数组,数组中的元素可以是基本数据类型也可以是对象
- 缺点是:不支持@PropertySource 注解导入自定义的 YAML 配置。
Spring Boot 是否可以使用 XML 配置
- 推荐使用Java配置,而非XML配置,但是Spring Boot可以使用XML配置,通过@ImportResource注解可以引入一个XML配置。
spring boot 核心配置文件是什么?
- 单纯做 Spring Boot 开发,可能不太容易遇到 bootstrap.properties 配置文件,但是在结合 Spring Cloud 时,这个配置就会经常遇到了,特别是在需要加载一些远程配置文件的时侯。
- (1) bootstrap (. yml 或者 . properties):boostrap 由父 ApplicationContext 加载的,比 applicaton 优先加载,配置在应用程序上下文的引导阶段生效。一般来说我们在 Spring Cloud Config 或者 Nacos 中会用到它。且 boostrap 里面的属性不能被覆盖;
- (2) 由ApplicationContext加载,用于spring boot项目的自动化配置。
什么是 Spring Profiles?
- 在项目的开发中,有些配置文件在开发、测试或者生产等不同环境中可能是不同的,例如数据库连接、redis的配置等等。那我们如何在不同环境中自动实现配置的切换呢?Spring给我们提供了profiles机制给我们提供的就是来回切换配置文件的功能
- Spring Profiles 允许用户根据配置文件(dev,test,prod 等)来注册 bean。因此,当应用程序在开发中运行时,只有某些 bean 可以加载,而在 PRODUCTION中,某些其他 bean 可以加载。假设我们的要求是 Swagger 文档仅适用于 QA 环境,并且禁用所有其他文档。这可以使用配置文件来完成。Spring Boot 使得使用配置文件非常简单。
开发测试生产环境如何快速切换?
一行配置
application-{profile}.properties/ application-{profile}.yml
可以有三个配置文件
application.properties
application-dev.properties
application-prod.properties
但是默认的只有application.properties生效:可以利用如下配置实现切换
spring.profile.active=dev
如何在自定义端口上运行 Spring Boot 应用程序?
- 在application.properties 中指定端口。server.port = 8090
如何重新加载 Spring Boot 上的更改,而无需重新启动服务器?Spring Boot项目如何热部署
- Spring Boot 有一个开发工具(DevTools)模块,它有助于提高开发人员的生产力。
- Java 开发人员面临的一个主要挑战是将文件更改自动部署到服务器并自动重启服务器。
- DevTools使得开发人员可以重新加载 Spring Boot 上的更改,而无需重新启动服务器。
- spring boot中有两个类加载器,一个是Base 加载器,就是导入的jar包等等,另一个是restart加载器,对应的是开发者自己写的会变化的类。
- 冷启动(从头开始启动)的话,两个加载器都需要工作,但热部署,只是restart加载器去加载变化的类,不变的类可以不用再加载(比如低三方的jar包),因此速度会更快。
Spring Boot 中的 starter 到底是什么 ?(不大懂)
- 首先,这个 Starter 并非什么新的技术点,基本上还是基于 Spring 已有功能来实现的。
- 首先它提供了一个自动化配置类,一般命名为 XXXAutoConfiguration ,在这个配置类中通过条件注解来决定一个配置是否生效(条件注解就是 Spring 中原本就有的),然后它还会提供一系列的默认配置,也允许开发者根据实际情况自定义相关配置,然后通过类型安全的属性注入将这些配置属性注入进来,新注入的属性会代替掉默认属性。正因为如此,很多第三方框架,我们只需要引入依赖就可以直接使用了。当然,开发者也可以自定义 Starter。
SpringBoot Starter的工作原理
- 个人理解Spring Boot就是由各种starter组合起来的,我们也可以开发自己的starter
- 在SpringBoot启动程序时由@SpringBootApplication注解会自动去maven中读取每个starter中的spring.factories文件,该文件里配置了所有要被创建spring容器中的bean, 并且进行自动配置把bean注入SpringContext中(SpringContext是Spring的配置文件)
spring-boot-starter-parent 有什么用 ?(不大懂)
我们都知道,新创建一个 Spring Boot 项目,默认都是有 parent 的,这个 parent 就是 spring-boot-starter-parent ,spring-boot-starter-parent 主要有如下作用:
-
定义了 Java 编译版本为 1.8 。
-
使用 UTF-8 格式编码。
-
继承自 spring-boot-dependencies,这个里边定义了依赖的版本,也正是因为继承了这个依赖,所以我们在写依赖时才不需要写版本号。
-
执行打包操作的配置。
-
自动化的资源过滤。
-
自动化的插件配置。
-
针对 application.properties 和 application.yml 的资源过滤,包括通过 profile 定义的不同环境的配置文件,例如 application-dev.properties 和 application-dev.yml。
Spring Boot 打成的 jar 和普通的 jar 有什么区别 ?
- Spring Boot 项目最终打包成的 jar 是可执行 jar ,这种 jar 可以直接通过 java -jar xxx.jar 命令来运行;
- 这种 jar 不可以作为普通的 jar 被其他项目依赖,即使依赖了也无法使用其中的类;
- 和普通 jar 的结构不同,普通的 jar 包,解压后直接就是包名,包里就是我们的代码;
- Spring Boot 打包成的可执行 jar 解压后,在 \BOOT-INF\classes 目录下才是我们的代码,因此无法被直接引用。
- 如果非要引用,可以在 pom.xml 文件中增加配置,将 Spring Boot 项目打包成两个 jar ,一个可执行,一个可引用。
运行 Spring Boot 有哪几种方式?
- 1)打包用命令或者放到容器中运行
- 2)用 Maven/ Gradle 插件运行
- 3)直接执行 main 方法运行
Spring Boot 需要独立的容器运行吗?
- 可以不需要,内置了 Tomcat/ Jetty 等容器。
开启 Spring Boot 特性有哪几种方式?
- 继承spring-boot-starter-parent项目
- 导入spring-boot-dependencies项目依赖
如何使用 Spring Boot 实现异常处理?
- Spring 提供了一种使用 ControllerAdvice 处理异常的非常有用的方法。我们通过实现一个 ControlerAdvice 类,来处理控制器类抛出的所有异常。
如何使用 Spring Boot 实现分页和排序?(不懂)
- 使用 Spring Boot 实现分页非常简单。使用 Spring Data-JPA 可以实现将可分页的传递给存储库方法。
微服务中如何实现 session 共享 ?(不懂)
- 在微服务中,一个完整的项目被拆分成多个不相同的独立的服务,各个服务独立部署在不同的服务器上,各自的 session 被从物理空间上隔离开了
- 但是经常,我们需要在不同微服务之间共享 session ,常见的方案就是 Spring Session + Redis 来实现 session 共享。
- 将所有微服务的 session 统一保存在 Redis 上,当各个微服务对 session 有相关的读写操作时,都去操作 Redis 上的 session 。
- 这样就实现了 session 共享,Spring Session 基于 Spring 中的代理过滤器实现,使得 session 的同步操作对开发人员而言是透明的,非常简便。
Spring Boot 中如何实现定时任务 ?
- 定时任务也是一个常见的需求,Spring Boot 中对于定时任务的支持主要还是来自 Spring 框架。
- 在 Spring Boot 中使用定时任务主要有两种不同的方式
- 一是使用 Spring 中的 @Scheduled 注解,
- 另一个则是使用第三方框架 Quartz, 按照 Quartz 的方式,定义 Job 和 Trigger 即可。
如何整合web基础组件
- 1.三大Web基础组件
- 2. 一行扫描@ServletComponentScan(可以扫描Filter也可以扫描Listener)
@WebFilter(urlPatterns = "/*")
public class HelloFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("doFilter");
filterChain.doFilter(servletRequest, servletResponse);
}
}
@WebServlet(urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost");
}
}
@WebListener
public class MyRequestListener implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("requestDestroyed");
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("requestInitialized");
}
}
@SpringBootApplication
//扫描所有的组件
@ServletComponentScan(basePackages="com.example.demo")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
什么是类型安全的属性注入(Type-safe Configuraion Properties)
为什么需要类型安全?
1.若该对象有100+个属性,这样写,代码是否太过复杂和冗余?
2.我们都知道属性是需要私有化的,行为才公开化,这样写,代码是否缺少安全性?
解决类型安全的方法
在Book.java上添加注解@ConfigurationProperties, 同时注释或删除原属性上的@Value注解
@ConfigurationProperties(prefix = "book")
@ConfigurationProperties(prefix = “book”)注解的prefix属性对应的值和xxx.properties文件中的book.xxx属性对应,所以我们再定义其他属性时,只需要将属性的名字和xxx.properties文件中的book.xxx对应即可,这样我们就减少了写代码的工作量和提升了类型的安全性,也契合“属性私有化”这一原则。
package com.mango.properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@PropertySource("classpath:book.properties")
@ConfigurationProperties(prefix = "book")
public class Book {
//@Value("${book.id}")
private long id;
//@Value("${book.name}")
private String name;
//@Value("${book.author}")
private String author;
}
Redis.class
@Component
@ConfigurationProperties(prefix = “redis”); 会把application.properties中前缀为redis的批量自动注入进来
public class Redis{
private String host;
private Integer port;
}
application.properties
redis.host = 127.0.0.1
redis.port = 80
@Component
@ConfigurationProperties(prefix = “redisCluster”);
public class RedisCluster{
private String host;
private List<Integer> ports;
}
application.properties
redisCluster.host = 127.0.0.1
redisCluster.port[0] = 6379
redisCluster.port[0] = 6380
redisCluster.port[0] = 63781
@Component
@ConfigurationProperties(prefix = “redisCluster”);
public class RedisCluster{
private String host;
private List<Redis> redis;
}
public class Redis{
private String host;
private Integer port;
}
application.properties
redisCluster.host = 127.0.0.1
redisCluster.redis[0][host] = 192.168.66.130
redisCluster.redis[0][port] = 6379
redisCluster.redis[1][host] = 192.168.66.131
redisCluster.redis[1][port] = 6380
SpringBoot如何实现打包
- 进入项目目录在控制台输入mvn clean package, clean是清空已存在的项目包,package是进行打包
- 或则点击左边选项栏中的Maven, 先点击clean再点击package
您使用到了哪些Maven依赖
- spring-boot-starter-web: 嵌入tomcat和web开发需要的servlet与jsp支持
- spring-boot-starter-data-redis:redis数据库支持
- spring-boot-starter-data-elasticsearch:redis数据库支持
- mybatis-spring-boot-starter:第三方的mybatis集成starter
- spring-boot-starter-thymeleaf
- spring-boot-starter-test
- spring-boot-starter-email
- spring-boot-starter-quartz