1、spring-boot-starter-parent 有什么用 ?
spring-boot-starter-parent
是一个特殊的 starter,它用于提供 Spring Boot 应用的默认配置。当你在项目中以父项目的形式引入 spring-boot-starter-parent
时,它将带来以下好处:
-
预定义的依赖管理:
spring-boot-starter-parent
提供了一个依赖管理的部分,里面预定义了许多依赖的版本,这些依赖与 Spring Boot 的版本保持兼容。使用它可以避免自己定义常用依赖的版本,简化了 Maven 的pom.xml
文件。 -
默认的插件配置:它提供了一些 Maven 插件的默认配置,如
maven-compiler-plugin
设置了 Java 的编译版本,maven-surefire-plugin
用于执行测试等。这样你就不必在每个项目中重复这些配置。 -
资源过滤:它配置了资源的过滤规则,允许使用属性占位符并将这些属性替换为实际的值。
-
打包配置:它包含了专门为 Spring Boot 应用定制的
spring-boot-maven-plugin
,这个插件能够创建可执行的 JAR 或 WAR 文件,这些文件包含了嵌入式的 Tomcat、Jetty 或 Undertow 服务器。 -
属性配置:它提供了一些有用的 Maven 属性配置,例如
<java.version>
,你可以用它来指定 Java 版本。 -
默认的配置属性:
spring-boot-starter-parent
也提供了一些 Spring Boot 特有的默认配置属性,如spring-boot.repackage.exclude
和spring-boot.repackage.include
,这些属性用来自定义repackage
任务的行为。
通过继承 spring-boot-starter-parent
,你可以轻松地设置和管理项目,而不需要担心兼容性和版本控制的问题。这是为了提供“开箱即用”的体验,在很大程度上减少了 Spring Boot 项目初始搭建的复杂性。
请注意,虽然使用 spring-boot-starter-parent
为项目带来了许多便利,但它也有可能限制了一些自定义的设置。如果你需要更灵活的配置,可以考虑不继承 spring-boot-starter-parent
,而是使用 spring-boot-dependencies
作为依赖管理的 BOM(Bill of Materials)参考。
2、说一说SpringBoot的自动配置原理?
Spring Boot 的自动配置原理是其核心功能之一,它允许开发者快速设置项目并运行,而无需手动进行大量的配置。自动配置通过以下几个步骤实现:
-
启动类注解: Spring Boot 应用通常从一个带有
@SpringBootApplication
注解的主类开始。@SpringBootApplication
是一个组合注解,它包含了@SpringBootConfiguration
、@EnableAutoConfiguration
和@ComponentScan
。 -
@EnableAutoConfiguration:
@EnableAutoConfiguration
注解是自动配置的关键,它告诉 Spring Boot 基于添加的依赖自动配置应用程序。Spring Boot 会查看类路径下的 JAR 文件,并为你添加的依赖自动配置相关的组件。 -
spring.factories 文件: 在 Spring Boot 的自动配置过程中,会加载
META-INF/spring.factories
文件中配置的类。在 Spring Boot 的启动过程中,它会读取这个文件,并查找org.springframework.boot.autoconfigure.EnableAutoConfiguration
键下配置的所有自动配置类。 -
自动配置类: 通过
spring.factories
文件找到的自动配置类通常会带有@Configuration
注解,这表明它们是 Java 配置类(相当于 XML 配置文件)。这些配置类通常会使用@Conditional
相关的注解来进行条件判断,只有当特定的条件成立时,相关的自动配置才会生效。例如,
@ConditionalOnClass
注解会根据类路径下是否存在某个类来决定是否执行配置;@ConditionalOnBean
和@ConditionalOnMissingBean
注解则是基于 Spring 上下文中是否存在(或缺失)某个 Bean 来决定配置是否生效。 -
条件注解: 自动配置类可以通过多种
@Conditional
注解来决定配置是否应该应用。这些条件包括类的存在、属性的值、Bean 的存在等。 -
自定义自动配置: 开发者可以通过创建自己的自动配置类来扩展 Spring Boot 的自动配置功能。这些自定义的自动配置类同样需要在
spring.factories
文件中声明,并且可以使用相同的@Conditional
注解来控制配置的条件。 -
覆盖自动配置: 如果自动配置的默认行为不符合项目的需求,开发者可以通过定义自己的 Bean 来覆盖自动配置的 Bean,或者使用
application.properties
或application.yml
文件中的属性来自定义自动配置的行为。
Spring Boot 的自动配置机制极大地简化了项目的配置工作,使得开发者可以快速开始开发而无需操心复杂的配置过程。然而,理解自动配置的原理也非常重要,这样当需要进行微调或者排查配置相关的问题时,开发者能够知道从哪里入手。
3. SpringBoot微服务中如何实现 session 共享 ?
在 Spring Boot 微服务架构中实现会话(session)共享,通常需要使用外部存储来维护会话状态,以便在不同服务实例之间共享用户会话信息。以下是实现会话共享的一些常见方法:
-
Spring Session: Spring Session 提供了一个框架,通过这个框架可以将会话信息存储在外部会话存储中,如 Redis、Hazelcast、MongoDB 等。这样,不管用户的请求被路由到哪个服务实例,应用都可以通过这个外部存储来访问会话信息。
要在 Spring Boot 微服务中使用 Spring Session,你通常需要:
- 添加 Spring Session 依赖到你的项目中。
- 通过配置类来指定会话存储类型。
- 在应用中使用标准的
HttpSession
接口。
-
使用负载均衡器的会话粘滞(Sticky Sessions): 会话粘滞是负载均衡器提供的一种功能,它可以确保来自同一用户的所有请求都被路由到同一个服务实例。虽然这种方法可以实现会话共享,但它限制了负载均衡器的灵活路由能力,并且如果服务实例宕机,会导致用户的会话丢失。
-
基于 Token 的认证: 使用 JWT(JSON Web Tokens)或其他形式的 Token,可以避免服务端的会话存储。用户身份验证信息被编码到 Token 中,客户端在每次请求时发送这个 Token。服务端通过解密 Token 来识别用户。这种方式不需要会话共享,也不需要外部存储,但需要确保 Token 的安全传输和有效验证。
-
数据库共享会话: 将会话信息存储在数据库中,所有服务实例都访问这个数据库来检索会话信息。这种方法易于实现,但可能不如专用的会话存储方案高效。
-
分布式缓存: 类似于 Spring Session 使用 Redis,可以使用其他分布式缓存方案如 Memcached、Hazelcast 等来存储会话信息。
-
容器编排工具: 如果你使用 Kubernetes 或其他容器编排工具,可以结合使用它们的会话亲和性(session affinity)设置和外部会话存储来实现会话共享。
在选择会话共享的实现方式时,需要考虑应用的具体需求、性能、可扩展性、安全性以及开发和维护的复杂性。通常情况下,使用 Spring Session 和外部会话存储(如 Redis)是微服务架构中实现会话共享的一个可靠和推荐的方法。
4、说一说你常用的Starter?
-
Spring Boot 也提供了其它的启动器项目包括,包括用于开发特定类型应用程序的典型依赖项。
spring-boot-starter-web-services - SOAP Web Services;
spring-boot-starter-web - Web 和 RESTful 应用程序;
spring-boot-starter-test - 单元测试和集成测试;
spring-boot-starter-jdbc - 传统的 JDBC;
spring-boot-starter-hateoas - 为服务添加 HATEOAS 功能;
spring-boot-starter-security - 使用 SpringSecurity 进行身份验证和授权;
spring-boot-starter-data-jpa - 带有 Hibeernate 的 Spring Data JPA;
spring-boot-starter-data-rest - 使用 Spring Data REST 公布简单的 REST 服务;
5、SpringBoot如何实现热部署?
在 Spring Boot 中实现热部署(也称为热重载或热加载)可以通过不同的方法完成,主要目的是为了能够在开发过程中实时看到代码更改的效果,而无需手动重启应用。以下是几种常用的热部署实现方法:
-
Spring Boot DevTools: Spring Boot 提供了一个名为
spring-boot-devtools
的模块,该模块能够实现自动重启和热加载功能。要使用spring-boot-devtools
,你只需将其添加到你的项目依赖中:1<dependencies> 2 <!-- ... other dependencies ... --> 3 <dependency> 4 <groupId>org.springframework.boot</groupId> 5 <artifactId>spring-boot-devtools</artifactId> 6 <scope>runtime</scope> 7 <optional>true</optional> 8 </dependency> 9</dependencies>
添加依赖后,每当你的代码有变动并且重新编译后,
spring-boot-devtools
就会触发应用重启。这个过程会比冷启动快很多,因为 DevTools 采用了两个 ClassLoader,一个用于加载不会变更的类(如第三方库),另一个用于加载会变更的类(如你的代码)。在重启过程中,不会变更的类不需要重新加载,从而加快了启动速度。 -
JRebel: JRebel 是一个商业的 Java 插件,能够实现真正的热部署。它能够在不重启容器的情况下直接将代码的更改加载到 JVM 中。要使用 JRebel,你需要购买它的许可证,并按照官方文档进行设置。
-
HotSwap: JVM 提供了一个 HotSwap 机制,它允许在运行时替换类的字节码。不过,HotSwap 的功能比较有限,只能够在方法体内进行代码更改。为了增强这一功能,可以使用 DCEVM (Dynamic Code Evolution Virtual Machine),它是一个修改过的 JVM 版本,允许更多形式的热部署,包括添加/删除字段、方法等。
-
IntelliJ IDEA 的自动编译: 如果你使用 IntelliJ IDEA 开发 Spring Boot 应用,可以开启自动编译功能(Build Project automatically),这样当文件被更改并且保存时,IDEA 会自动编译更改过的文件。结合
spring-boot-devtools
使用时,可以实现类似热部署的效果。
要注意的是,热部署技术主要用于开发环境,以提高开发效率,它们通常不应该在生产环境中使用。此外,有时候热部署可能会因为缓存或其他原因而不那么可靠,这时候完整重启应用仍然是最稳妥的方式。
6、说一说SpringBoot中常用的注解有哪些?
- @SpringBootApplication
- @MapperScan
- @RestController
- @RequestMapping
- @Autowired
- @Resource
- @RequestBody
- @PostConstruct
- @CookieValue
7、springMvc
DispatcherServlet: 在 Spring Web MVC 框架中,DispatcherServlet
是前端控制器(Front Controller)的一个实现,作为 MVC(Model-View-Controller)模式中的中心组件负责接收所有的 HTTP 请求。DispatcherServlet
协调各种处理器(Handlers)、处理器映射(Handler Mappings)、视图解析器(View Resolvers)等,负责将请求分发给正确的处理器,并返回响应给客户端。
当一个 HTTP 请求到达 DispatcherServlet
时,它会执行以下操作:
- 接收 HTTP 请求。
- 解析请求到一个特定的处理器。
- 将请求分发给相应的处理器进行处理。
- 处理器处理完请求后返回一个
ModelAndView
对象。 - 然后
DispatcherServlet
将ModelAndView
对象传递给ViewResolver
以解析视图。 - 最后,
DispatcherServlet
渲染视图(可能是 JSP、HTML 或其他格式)并返回给客户端。
ModelAndView: ModelAndView
是一个容器,包含了模型数据和视图信息。在控制器(Controller)处理请求后,会创建并返回一个 ModelAndView
对象,这个对象包含了:
- Model:一个 Map,包含了要传输到视图的数据。
- View:可以是简单的视图名(字符串形式),也可以是具体的
View
对象。视图是最终呈现给用户的页面。
ViewResolver: ViewResolver
的作用是根据视图名解析出具体的 View
对象。ViewResolver
使得控制器可以通过逻辑名称来指定视图,而不用关心视图的实际物理位置。简单来说,它通过视图名来查找或构造渲染的实际视图。
常见的 ViewResolver
实现包括:
- InternalResourceViewResolver:可以解析到 JSP 文件等内部资源的
ViewResolver
,它将视图名转换成 URL 路径。 - ContentNegotiatingViewResolver:根据请求的媒体类型选择最合适的
ViewResolver
。
最终,通过这些组件的协作,Spring MVC 提供了一种灵活且可扩展的方式来处理 Web 应用程序中的请求,并将数据与视图分离。
8、DispatcherServlet是找到我们的controller代码的,有什么映射关系吗?
是的,DispatcherServlet
通过处理器映射(Handler Mapping)来确定哪个 Controller 应该处理传入的 HTTP 请求。在 Spring MVC 中,这个映射关系是通过 @RequestMapping 注解(以及其变体如 @GetMapping、@PostMapping 等)来建立的。
当一个请求到达 DispatcherServlet
时,它会按照以下步骤来处理请求:
-
处理器映射(Handler Mapping):
DispatcherServlet
首先会查询一个或多个HandlerMapping
实例来确定当前请求的 URL 应该由哪个 Controller 方法处理。Spring MVC 中的RequestMappingHandlerMapping
是最常用的处理器映射器,它会扫描所有的 @RequestMapping 注解并构建一个映射表,将 URL 路径映射到相应的 Controller 方法。 -
处理器适配器(Handler Adapter): 一旦找到了处理请求的 Controller 方法,
DispatcherServlet
需要一个HandlerAdapter
去调用这个方法。HandlerAdapter
的作用是作为桥梁,它适配DispatcherServlet
的调用约定到具体 Controller 方法的调用方式。 -
控制器(Controller): Controller 处理请求,并返回一个
ModelAndView
对象,该对象封装了模型数据(Model)和视图名称(View Name)。 -
视图解析器(View Resolver): 使用视图名称,
DispatcherServlet
会调用ViewResolver
来解析视图。ViewResolver
根据视图名称确定如何渲染响应,它可以解析为 JSP、Thymeleaf、Freemarker 等视图技术。 -
渲染视图: 一旦
ViewResolver
提供了视图,DispatcherServlet
则通过View
对象渲染响应。这通常涉及将模型数据合并到视图模板中,并生成最终的 HTML 页面。
例如,一个简单的 Controller 可能会包含一个使用 @GetMapping 注解的方法,如下所示:
1@Controller 2public class MyController { 3 4 @GetMapping("/hello") 5 public ModelAndView hello() { 6 ModelAndView modelAndView = new ModelAndView("hello"); 7 modelAndView.addObject("message", "Hello, World!"); 8 return modelAndView; 9 } 10}
在上述代码中,@Controller 注解指明这是一个控制器类,@GetMapping("/hello") 则建立了一个映射关系,指明当访问 URL "/hello" 时,调用 hello()
方法。这个方法返回一个 ModelAndView
对象,其中包含了视图名称 "hello" 和模型数据 "message"。
DispatcherServlet
按照这些映射关系协调整个请求处理流程,确保正确的 Controller 处理请求并返回响应。
9、它为什么要使用 Handler Adapter?直接调用不好吗?
HandlerAdapter
的使用是 Spring MVC 设计的一部分,它提供了灵活性和可扩展性,解耦了 DispatcherServlet
与实际处理请求的控制器(Controller)之间的直接依赖。以下是使用 HandlerAdapter
的几个理由:
-
统一的调用接口:由于 Spring 支持多种类型的处理器(例如,注解控制器、HTTP请求处理器、Websocket处理器等),每种类型的处理器可能有不同的调用方式。
HandlerAdapter
提供了一个统一的接口,使得DispatcherServlet
可以以统一的方式调用不同类型的处理器,而不需关心调用细节。 -
可扩展性:通过
HandlerAdapter
,开发者可以更轻松地添加对新类型处理器的支持。只需实现一个新的HandlerAdapter
来处理新类型的请求,不需要修改DispatcherServlet
的代码。 -
分离关注点:
HandlerAdapter
负责将DispatcherServlet
传递的信息(如请求对象HttpServletRequest
)转换成控制器可以使用的格式,同时它也负责将控制器的返回值(如模型和视图信息ModelAndView
)转换成DispatcherServlet
可以理解的格式。这种责任分离使得控制器中的业务逻辑更加清晰,不需要处理与 HTTP 相关的底层细节。 -
适配多种控制器:
HandlerAdapter
允许DispatcherServlet
与旧版 Spring 控制器和新版基于注解的控制器以及其他自定义控制器共存。不同的HandlerAdapter
实现可以针对不同的控制器提供适配,保证了向后兼容性。 -
代码可维护性:使用
HandlerAdapter
可以让DispatcherServlet
的代码保持简洁。DispatcherServlet
只需要处理通用的流程控制,具体的执行细节则由各个HandlerAdapter
负责,从而提高了代码的可维护性。
在没有 HandlerAdapter
的情况下,DispatcherServlet
将需要直接调用控制器方法,这将导致 DispatcherServlet
变得复杂,因为它需要知道如何处理各种控制器类型和方法签名。这会破坏 Spring MVC 的设计原则,并减少框架的灵活性和可扩展性。
总之,HandlerAdapter
是 Spring MVC 设计中的一个重要组件,它使得 MVC 框架更加模块化和易于扩展。
10、对于要返回json的接口,springboot是怎么处理的?
对于返回 JSON 的接口,在 Spring MVC 或 Spring Boot 中,通常不会通过 ViewResolver
来处理,而是直接由 HttpMessageConverter
负责将返回值序列化为 JSON 格式的字符串,并写入到响应体(response body)中。
当你在 Controller 的处理方法上使用 @ResponseBody
注解时,或者使用 @RestController
注解(它是一个组合注解,包含了 @Controller
和 @ResponseBody
),Spring 会知道你想要返回的数据应该直接写入响应体,而不是通过一个视图页面来渲染。
在内部,当方法被 @ResponseBody
注解修饰时,DispatcherServlet
会使用合适的 HttpMessageConverter
来转换方法的返回值。对于 JSON 数据,通常会使用 MappingJackson2HttpMessageConverter
或类似的转换器,这些转换器会使用 Jackson 或其他 JSON 处理库将对象序列化为 JSON 字符串。
11、那对于要返回html的接口,ViewResolver它是怎么做的?
对于返回 HTML 的接口,在 Spring MVC 中,ViewResolver
负责将控制器返回的视图名称解析为实际的视图(View)对象,然后该视图对象用于渲染 HTML 响应。这里是这个过程的详细步骤:
-
控制器方法处理请求: 控制器(Controller)中的方法处理传入的 HTTP 请求,并返回一个
ModelAndView
对象,该对象包含了视图名称和模型数据。
@Controller
public class MyController {
@GetMapping("/greeting")
public ModelAndView greeting() {
ModelAndView modelAndView = new ModelAndView("greeting");
modelAndView.addObject("message", "Hello, World!");
return modelAndView;
}
}
在这个例子中,视图名称是 "greeting",模型数据中包含一个 "message" 属性。
-
DispatcherServlet 调用 ViewResolver:
DispatcherServlet
接收到ModelAndView
对象后,它会使用配置的ViewResolver
来解析视图名称。ViewResolver
会根据视图名称和可能的前后缀配置来定位实际的视图资源。 -
ViewResolver 解析视图: 通常情况下,会使用
InternalResourceViewResolver
来解析 JSP 类型的视图。例如,一个配置的InternalResourceViewResolver
可能将 "greeting" 解析为 "/WEB-INF/views/greeting.jsp" 的 JSP 文件。 -
渲染视图:
ViewResolver
返回的View
对象是实际渲染响应的组件。在处理 JSP 的情况下,View
通常是InternalResourceView
的实例。它会使用模型数据填充视图模板(如 JSP 文件),生成最终的 HTML 页面。 -
发送响应: 生成的 HTML 内容随后被发送回客户端作为 HTTP 响应。
-
在 Spring Boot 中,大多数
ViewResolver
配置被自动处理,尤其是当添加了对应的模板引擎(如 Thymeleaf、Freemarker 或 Mustache)的依赖时。例如,如果你在pom.xml
中添加了 Thymeleaf 的依赖,Spring Boot 会自动配置ThymeleafViewResolver
,你所需要做的仅仅是在资源目录下创建模板文件。这一流程使得开发者能够方便地管理控制器逻辑和视图模板,而不必担心视图的解析和渲染细节。这也是 Spring MVC 框架提供的模型视图控制器模式的体现,将业务逻辑(Model)、用户界面(View)和控制逻辑(Controller)分离。
12、springboot 的 import注解有什么作用?
在 Spring Framework 中,@Import
注解是用来导入其他配置类(也可以是普通的组件类)到当前的配置类中。这样,被导入的类中定义的 Bean 将会注册到 Spring 应用程序上下文中。在 Spring Boot 中,@Import
注解同样适用,并被广泛用于模块化配置。
这里有几种使用 @Import
注解的场景:
-
导入配置类: 如果你有多个配置类,而你想将它们组合在一起,可以在主配置类上使用
@Import
注解来实现。
@Configuration
@Import({DatabaseConfig.class, WebConfig.class})
public class MainConfig {
// ...
}
在上面的例子中,DatabaseConfig
和 WebConfig
都是配置类,它们定义了不同的 Bean。使用 @Import
将它们导入到 MainConfig
中后,这些 Bean 就会被注册到 Spring 上下文中。
-
导入具体的组件类: 你可以直接导入具体的组件类(例如一个
@Service
或@Repository
注解的类),这样就无需为它们创建单独的配置类。
@Configuration
@Import(MyService.class)
public class AppConfig {
// ...
}
- 导入 ImportSelector 实现:
ImportSelector
是一个接口,它可以返回需要导入的配置类的全限定名数组。当你需要根据条件动态地导入配置类时,ImportSelector
很有用。
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 根据条件返回配置类名称
return new String[] {"com.example.MyConfig"};
}
}
@Configuration
@Import(MyImportSelector.class)
public class AppConfig {
// ...
}
- 导入 ImportBeanDefinitionRegistrar 实现:
ImportBeanDefinitionRegistrar
是另一个接口,它直接操作 Bean 定义注册过程。如果你需要进行更复杂的注册逻辑,可以实现这个接口。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 执行复杂的注册逻辑
}
}
@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class AppConfig {
// ...
}
@Import
注解提供了一种强大的机制来组织和加载配置,可以实现配置的模块化以及根据不同的条件导入不同的配置类或组件,它为 Spring 应用程序提供了很大的灵活性。
13、Spring Boot 的自动扫描和手动导入的区别?
第一种情况:为什么需要导入配置类?
在 Spring 中,只有当配置类在组件扫描的路径下时,它们才会被自动检测和注册。Spring Boot 通过 @SpringBootApplication
注解,结合了 @Configuration
、@EnableAutoConfiguration
和 @ComponentScan
注解,默认扫描主配置类(带有 @SpringBootApplication
注解的类)所在的包以及其子包中的组件。
如果你的 DatabaseConfig
类不在主配置类的包或子包下,那么它不会被自动扫描到,除非你显式地指定了扫描路径。在这种情况下,使用 @Import
注解来手动导入配置类是有必要的。
另一种情况是,即使 DatabaseConfig
类在扫描路径下,你也可能想要控制配置类的加载顺序或仅在特定条件下加载某些配置,这时手动使用 @Import
注解可以帮助你精确地控制配置类的注册。
第二种情况:为什么需要导入特定的组件类?
对于带有 @Service
、@Repository
、@Component
等注解的类,通常它们会被自动扫描并注册到 Spring 容器中,前提是这些类位于组件扫描的路径下。如果你的组件类不在自动扫描的路径下,或者你出于组织结构的考虑决定不进行自动扫描,这时可以使用 @Import
来手动将这些类导入配置。
此外,有时候开发者也会使用 @Import
来明确地表达依赖关系,即表明主配置类依赖于被导入的组件或配置,这可以使代码的结构更加清晰。
总结
总的来说,虽然 Spring Boot 提供了强大的自动扫描和自动配置能力,但在一些情况下,手动导入配置类或组件类可以提供额外的控制和灵活性。这些情况包括但不限于:
- 被导入的类不在自动扫描的路径下。
- 需要控制配置类的加载顺序。
- 仅在特定条件下加载某些配置或组件。
- 需要明确表达配置类之间的依赖关系。
- 组织代码结构,使之更清晰和模块化。
使用 @Import
注解是 Spring 强大功能的体现,它允许开发者根据实际需求选择最适合的配置管理方式。