SpringBoot面试题
- 1、Spring Boot 的自动配置是如何实现的?
- 2、shiro和oauth还有cas他们之间的关系是什么?问下您公司权限是如何设计,还有就是这几个概念的区别。
- 3、Spring Cache 三种常用的缓存注解和意义?
- 4、Spring Boot 如何设置支持跨域请求?
- 5、JPA 和 Hibernate 有哪些区别?JPA 可以支持动态 SQL 吗?
- 6、SpringBoot启动过程的关键事件(按照触发顺序)
- 7、SpringBoot上下文
- 8、SpringBoot配置加载顺序
- 9、spring-boot-starter-parent有什么用?
- 10、YAML配置优势
- 11、什么是 Spring Data ?
- 12、Spring Boot 打成的 jar 和普通的 jar 有什么区别 ?
- 13、bootstrap.properties 和 application.properties 有何区别 ?
1、Spring Boot 的自动配置是如何实现的?
Spring Boot 项目的启动注解是:@SpringBootApplication,其实它就是由下面三个注解组成的:
- @Configuration
- @ComponentScan
- @EnableAutoConfiguration
其中 @EnableAutoConfiguration 是实现自动配置的入口,该注解又通过 @Import 注解导入了AutoConfigurationImportSelector,在该类中加载 META-INF/spring.factories 的配置信息。然后筛选出以 EnableAutoConfiguration 为 key 的数据,加载到 IOC 容器中,实现自动配置功能!
2、shiro和oauth还有cas他们之间的关系是什么?问下您公司权限是如何设计,还有就是这几个概念的区别。
cas和oauth是一个解决单点登录的组件,shiro主要是负责权限安全方面的工作,所以功能点不一致。但往往需要单点登陆和权限控制一起来使用,所以就有 cas+shiro或者oauth+shiro这样的组合。
token一般是客户端登录后服务端生成的令牌,每次访问服务端会进行校验,一般保存到内存即可,也可以放到其他介质;redis可以做Session共享,如果前端web服务器有几台负载,但是需要保持用户登录的状态,这场景使用比较常见。
我们公司使用oauth+shiro这样的方式来做后台权限的管理,oauth负责多后台统一登录认证,shiro负责给登录用户赋予不同的访问权限。
2.1 Spring Security 和 Shiro 各自的优缺点 ?
如果是 Spring Boot 项目,一般选择 Spring Security 。当然这只是一个建议的组合,单纯从技术上来说,无论怎么组合,都是没有问题的。Shiro 和 Spring Security 相比,主要有如下一些特点:
- Spring Security 是一个重量级的安全管理框架;Shiro 则是一个轻量级的安全管理框架
- Spring Security 概念复杂,配置繁琐;Shiro 概念简单、配置简单
- Spring Security 功能强大;Shiro 功能简单
3、Spring Cache 三种常用的缓存注解和意义?
- @Cacheable ,用来声明方法是可缓存,将结果存储到缓存中以便后续使用相同参数调用时不需执行实际的方法,直接从缓存中取值。
- @CachePut,使用 @CachePut 标注的方法在执行前,不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
- @CacheEvict,是用来标注在需要清除缓存元素的方法或类上的,当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。
3.1 .微服务中如何实现 session 共享 ?
在微服务中,一个完整的项目被拆分成多个不相同的独立的服务,各个服务独立部署在不同的服务器上,各自的 session 被从物理空间上隔离开了,但是经常,我们需要在不同微服务之间共享 session ,常见的方案就是 Spring Session + Redis 来实现 session 共享。将所有微服务的 session 统一保存在 Redis 上,当各个微服务对 session 有相关的读写操作时,都去操作 Redis 上的 session 。这样就实现了 session 共享,Spring Session 基于 Spring 中的代理过滤器实现,使得 session 的同步操作对开发人员而言是透明的,非常简便。
4、Spring Boot 如何设置支持跨域请求?
现代浏览器出于安全的考虑, HTTP 请求时必须遵守同源策略,否则就是跨域的 HTTP 请求,默认情况下是被禁止的,IP(域名)不同、或者端口不同、协议不同(比如 HTTP、HTTPS)都会造成跨域问题。
一般前端的解决方案有:
- 使用 JSONP 来支持跨域的请求,JSONP 实现跨域请求的原理简单的说,就是动态创建
后来 HTML5 支持了 CORS 协议。CORS 是一个 W3C 标准,全称是”跨域资源共享”(Cross-origin resource sharing),允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 AJAX 只能同源使用的限制。它通过服务器增加一个特殊的 Header[Access-Control-Allow-Origin]来告诉客户端跨域的限制,如果浏览器支持 CORS、并且判断 Origin 通过的话,就会允许 XMLHttpRequest 发起跨域请求。
前端使用了 CORS 协议,就需要后端设置支持非同源的请求,Spring Boot 设置支持非同源的请求有两种方式。
第一,配置 CorsFilter。
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.setAllowCredentials(true);
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.addExposedHeader("*");
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", config);
return new CorsFilter(configSource);
}
}
第二,在启动类上添加:
public class Application extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowCredentials(true)
.allowedHeaders("*")
.allowedOrigins("*")
.allowedMethods("*");
}
}
5、JPA 和 Hibernate 有哪些区别?JPA 可以支持动态 SQL 吗?
JPA本身是一种规范,它的本质是一种ORM规范(不是ORM框架,因为JPA并未提供ORM实现,只是制定了规范)因为JPA是一种规范,所以,只是提供了一些相关的接口,但是接口并不能直接使用,JPA底层需要某种JPA实现,Hibernate 是 JPA 的一个实现集。
JPA 是根据实体类的注解来创建对应的表和字段,如果需要动态创建表或者字段,需要动态构建对应的实体类,再重新调用Jpa刷新整个Entity。动态SQL,mybatis支持的最好,jpa也可以支持,但是没有Mybatis那么灵活。
6、SpringBoot启动过程的关键事件(按照触发顺序)
- 开始启动
- Environment构建完成
- ApplicationContext构建完成
- ApplicationContext完成加载
- ApplicationContext完成刷新并启动
- 启动完成
- 启动失败
package org.springframework.boot;
public interface SpringApplicationRunListener {
// 在run()方法开始执行时,该方法就立即被调用,可用于在初始化最早期时做一些工作
void starting();
// 当environment构建完成,ApplicationContext创建之前,该方法被调用
void environmentPrepared(ConfigurableEnvironment environment);
// 当ApplicationContext构建完成时,该方法被调用
void contextPrepared(ConfigurableApplicationContext context);
// 在ApplicationContext完成加载,但没有被刷新前,该方法被调用
void contextLoaded(ConfigurableApplicationContext context);
// 在ApplicationContext刷新并启动后,CommandLineRunners和ApplicationRunner未被调用前,该方法被调用
void started(ConfigurableApplicationContext context);
// 在run()方法执行完成前该方法被调用
void running(ConfigurableApplicationContext context);
// 当应用运行出错时该方法被调用
void failed(ConfigurableApplicationContext context, Throwable exception);
}
7、SpringBoot上下文
- Spring Boot 中有两种上下文,一种是 bootstrap,另一种是 application。
- bootstrap 是应用程序的父上下文,加载优先于 application。
- bootstrap 主要用于从额外的资源来加载配置信息,还可以在本地外部配置文件中解密属性。
这两个上下文共用一个环境,它是任何 Spring 应用程序的外部属性来源。
- bootstrap 里面的属性会优先加载,他们默认也不能被本地相同的配置覆盖。
bootstrap 配置文件特性(对比 application)
- bootstrap 由父 ApplicationContext 加载,比 application 优先加载
- bootstrap 里面的属性不能被覆盖
应用场景
-
application
主要用于 Spring Boot 项目的自动化配置 -
bootstrap
- 使用 Spring Cloud Config 配置中心时,这时需要在bootstrap 配置文件中添加连接到配置中心的配置属性,来加载外部配置中心的配置信息
- 一些固定的不能被覆盖的属性
- 一些加密/解密的场景
技术上,bootstrap.yml 是被一个父级的 Spring ApplicationContext 加载的。这个父级的 Spring ApplicationContext是先加载的,在加载application.yml 的 ApplicationContext之前。
为何需要把 config server 的信息放在 bootstrap.yml 里?
当使用 Spring Cloud 的时候,配置信息一般是从 config server 加载的,为了取得配置信息(比如密码等),你需要一些提早的或引导配置。因此,把 config server 信息放在 bootstrap.yml,用来加载真正需要的配置信息。
8、SpringBoot配置加载顺序
Spring Boot为了能够更合理的重写各属性的值,使用了下面这种较为特别的属性加载顺序:
- 命令行中传入的参数。
- SPRING_APPLICATION_JSON中的属性。SPRING_APPLICATION_JSON是以JSON格式配置在系统环境变量中的内容。
java:comp/env中的JNDI属性。 - Java的系统属性,可以通过System.getProperties()获得的内容。
- 操作系统的环境变量
- 通过random.*配置的随机属性
- 位于当前应用jar包之外,针对不同{profile}环境的配置文件内容,例如:application-{profile}.properties或是YAML定义的配置文件
- 位于当前应用jar包之内,针对不同{profile}环境的配置文件内容,例如:application-{profile}.properties或是YAML定义的配置文件
- 位于当前应用jar包之外的application.properties和YAML配置内容
- 位于当前应用jar包之内的application.properties和YAML配置内容
- 在@Configuration注解修改的类中,通过@PropertySource注解定义的属性
- 应用默认属性,使用SpringApplication.setDefaultProperties定义的内容
- 优先级按上面的顺序有高到低,数字越小优先级越高。
可以看到,其中第7项和第9项都是从应用jar包之外读取配置文件,所以,实现外部化配置的原理就是从此切入,为其指定外部配置文件的加载位置来取代jar包之内的配置内容。通过这样的实现,我们的工程在配置中就变的非常干净,我们只需要在本地放置开发需要的配置即可,而其他环境的配置就可以不用关心,由其对应环境的负责人去维护即可。
9、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。
10、YAML配置优势
YAML 配置和传统的 properties 配置相比到底有哪些优势呢?
- 配置有序,在一些特殊的场景下,配置有序很关键
- 支持数组,数组中的元素可以是基本数据类型也可以是对象
- 简洁
相比 properties 配置文件,YAML 还有一个缺点,就是不支持 @PropertySource 注解导入自定义的 YAML 配置。
11、什么是 Spring Data ?
Spring Data 是 Spring 的一个子项目。用于简化数据库访问,支持NoSQL 和 关系数据存储。其主要目标是使数据库的访问变得方便快捷。Spring Data 具有如下特点:
- SpringData 项目支持 NoSQL 存储:
- MongoDB (文档数据库)
- Neo4j(图形数据库)
- Redis(键/值存储)
- Hbase(列族数据库)
SpringData 项目所支持的关系数据存储技术:
- JDBC
- JPA
Spring Data Jpa 致力于减少数据访问层 (DAO) 的开发量. 开发者唯一要做的,就是声明持久层的接口,其他都交给 Spring Data JPA 来帮你完成!Spring Data JPA 通过规范方法的名字,根据符合规范的名字来确定方法需要实现什么样的逻辑。
12、Spring Boot 打成的 jar 和普通的 jar 有什么区别 ?
Spring Boot 项目最终打包成的 jar 是可执行 jar ,这种 jar 可以直接通过 java -jar xxx.jar 命令来运行,这种 jar 不可以作为普通的 jar 被其他项目依赖,即使依赖了也无法使用其中的类。
Spring Boot 的 jar 无法被其他项目依赖,主要还是他和普通 jar 的结构不同。普通的 jar 包,解压后直接就是包名,包里就是我们的代码,而 Spring Boot 打包成的可执行 jar 解压后,在 BOOT-INFclasses 目录下才是我们的代码,因此无法被直接引用。如果非要引用,可以在 pom.xml 文件中增加配置,将 Spring Boot 项目打包成两个 jar ,一个可执行,一个可引用。
13、bootstrap.properties 和 application.properties 有何区别 ?
单纯做 Spring Boot 开发,可能不太容易遇到 bootstrap.properties 配置文件,但是在结合 Spring Cloud 时,这个配置就会经常遇到了,特别是在需要加载一些远程配置文件的时侯。
bootstrap.properties 在 application.properties 之前加载,配置在应用程序上下文的引导阶段生效。一般来说我们在 Spring Cloud Config 或者 Nacos 中会用到它。bootstrap.properties 被 Spring ApplicationContext 的父类加载,这个类先于加载 application.properties 的 ApplicatonContext 启动。
当然,前面叙述中的 properties 也可以修改为 yaml 。