共计 18586 个字符,预计需要花费 47 分钟才能阅读完成。
首发于 JavaGuide 在线网站:Spring 常见面试题总结
最近在对 JavaGuide 的内容进行重构欠缺,同步一下最新更新,心愿可能帮忙你。
Spring 根底
什么是 Spring 框架?
Spring 是一款开源的轻量级 Java 开发框架,旨在进步开发人员的开发效率以及零碎的可维护性。
咱们个别说 Spring 框架指的都是 Spring Framework,它是很多模块的汇合,应用这些模块能够很不便地帮助咱们进行开发,比如说 Spring 反对 IoC(Inverse of Control: 管制反转)和 AOP(Aspect-Oriented Programming: 面向切面编程)、能够很不便地对数据库进行拜访、能够很不便地集成第三方组件(电子邮件,工作,调度,缓存等等)、对单元测试反对比拟好、反对 RESTful Java 应用程序的开发。
Spring 最外围的思维就是不从新造轮子,开箱即用,进步开发效率。
Spring 翻译过去就是春天的意思,可见其指标和使命就是为 Java 程序员带来春天啊!打动!
🤐 多提一嘴:语言的风行通常须要一个杀手级的利用,Spring 就是 Java 生态的一个杀手级的利用框架。
Spring 提供的外围性能次要是 IoC 和 AOP。学习 Spring,肯定要把 IoC 和 AOP 的核心思想搞懂!
- Spring 官网:https://spring.io/
- Github 地址:https://github.com/spring-pro…
Spring 蕴含的模块有哪些?
Spring4.x 版本:
Spring5.x 版本:
Spring5.x 版本中 Web 模块的 Portlet 组件曾经被废除掉,同时减少了用于异步响应式解决的 WebFlux 组件。
Spring 各个模块的依赖关系如下:
Core Container
Spring 框架的外围模块,也能够说是根底模块,次要提供 IoC 依赖注入性能的反对。Spring 其余所有的性能根本都须要依赖于该模块,咱们从下面那张 Spring 各个模块的依赖关系图就可以看进去。
- spring-core:Spring 框架根本的外围工具类。
- spring-beans:提供对 bean 的创立、配置和治理等性能的反对。
- spring-context:提供对国际化、事件流传、资源加载等性能的反对。
- spring-expression:提供对表达式语言(Spring Expression Language)SpEL 的反对,只依赖于 core 模块,不依赖于其余模块,能够独自应用。
AOP
- spring-aspects:该模块为与 AspectJ 的集成提供反对。
- spring-aop:提供了面向切面的编程实现。
- spring-instrument:提供了为 JVM 增加代理(agent)的性能。具体来讲,它为 Tomcat 提供了一个织入代理,可能为 Tomcat 传递类文 件,就像这些文件是被类加载器加载的一样。没有了解也没关系,这个模块的应用场景十分无限。
Data Access/Integration
- spring-jdbc:提供了对数据库拜访的形象 JDBC。不同的数据库都有本人独立的 API 用于操作数据库,而 Java 程序只须要和 JDBC API 交互,这样就屏蔽了数据库的影响。
- spring-tx:提供对事务的反对。
- spring-orm:提供对 Hibernate、JPA、iBatis 等 ORM 框架的反对。
- spring-oxm:提供一个形象层撑持 OXM(Object-to-XML-Mapping),例如:JAXB、Castor、XMLBeans、JiBX 和 XStream 等。
- spring-jms : 音讯服务。自 Spring Framework 4.1 当前,它还提供了对 spring-messaging 模块的继承。
Spring Web
- spring-web:对 Web 性能的实现提供一些最根底的反对。
- spring-webmvc:提供对 Spring MVC 的实现。
- spring-websocket:提供了对 WebSocket 的反对,WebSocket 能够让客户端和服务端进行双向通信。
- spring-webflux:提供对 WebFlux 的反对。WebFlux 是 Spring Framework 5.0 中引入的新的响应式框架。与 Spring MVC 不同,它不须要 Servlet API,是齐全异步。
Messaging
spring-messaging 是从 Spring4.0 开始新退出的一个模块,主要职责是为 Spring 框架集成一些根底的报文传送利用。
Spring Test
Spring 团队提倡测试驱动开发(TDD)。有了管制反转 (IoC)的帮忙,单元测试和集成测试变得更简略。
Spring 的测试模块对 JUnit(单元测试框架)、TestNG(相似 JUnit)、Mockito(次要用来 Mock 对象)、PowerMock(解决 Mockito 的问题比方无奈模仿 final, static,private 办法)等等罕用的测试框架反对的都比拟好。
Spring,Spring MVC,Spring Boot 之间什么关系?
很多人对 Spring,Spring MVC,Spring Boot 这三者傻傻分不清楚!这里简略介绍一下这三者,其实很简略,没有什么浅近的货色。
Spring 蕴含了多个功能模块(下面刚刚进步过),其中最重要的是 Spring-Core(次要提供 IoC 依赖注入性能的反对)模块,Spring 中的其余模块(比方 Spring MVC)的性能实现根本都须要依赖于该模块。
下图对应的是 Spring4.x 版本。目前最新的 5.x 版本中 Web 模块的 Portlet 组件曾经被废除掉,同时减少了用于异步响应式解决的 WebFlux 组件。
Spring MVC 是 Spring 中的一个很重要的模块,次要赋予 Spring 疾速构建 MVC 架构的 Web 程序的能力。MVC 是模型 (Model)、视图(View)、控制器(Controller) 的简写,其核心思想是通过将业务逻辑、数据、显示拆散来组织代码。
应用 Spring 进行开发各种配置过于麻烦比方开启某些 Spring 个性时,须要用 XML 或 Java 进行显式配置。于是,Spring Boot 诞生了!
Spring 旨在简化 J2EE 企业应用程序开发。Spring Boot 旨在简化 Spring 开发(缩小配置文件,开箱即用!)。
Spring Boot 只是简化了配置,如果你须要构建 MVC 架构的 Web 程序,你还是须要应用 Spring MVC 作为 MVC 框架,只是说 Spring Boot 帮你简化了 Spring MVC 的很多配置,真正做到开箱即用!
Spring IoC
谈谈本人对于 Spring IoC 的理解
IoC(Inverse of Control: 管制反转) 是一种设计思维,而不是一个具体的技术实现。IoC 的思维就是将本来在程序中手动创建对象的控制权,交由 Spring 框架来治理。不过,IoC 并非 Spring 特有,在其余语言中也有利用。
为什么叫管制反转?
- 管制:指的是对象创立(实例化、治理)的势力
- 反转:控制权交给外部环境(Spring 框架、IoC 容器)
将对象之间的相互依赖关系交给 IoC 容器来治理,并由 IoC 容器实现对象的注入。这样能够很大水平上简化利用的开发,把利用从简单的依赖关系中解放出来。IoC 容器就像是一个工厂一样,当咱们须要创立一个对象的时候,只须要配置好配置文件 / 注解即可,齐全不必思考对象是如何被创立进去的。
在理论我的项目中一个 Service 类可能依赖了很多其余的类,如果咱们须要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只须要配置好,而后在须要的中央援用就行了,这大大增加了我的项目的可维护性且升高了开发难度。
在 Spring 中,IoC 容器是 Spring 用来实现 IoC 的载体,IoC 容器实际上就是个 Map(key,value),Map 中寄存的是各种对象。
Spring 时代咱们个别通过 XML 文件来配置 Bean,起初开发人员感觉 XML 文件来配置不太好,于是 SpringBoot 注解配置就缓缓开始流行起来。
相干浏览:
- IoC 源码浏览
- 面试被问了几百遍的 IoC 和 AOP,还在傻傻搞不清楚?
什么是 Spring Bean?
简略来说,Bean 代指的就是那些被 IoC 容器所治理的对象。
咱们须要通知 IoC 容器帮忙咱们治理哪些对象,这个是通过配置元数据来定义的。配置元数据能够是 XML 文件、注解或者 Java 配置类。
<!-- Constructor-arg with 'value' attribute -->
<bean id="..." class="...">
<constructor-arg value="..."/>
</bean>
下图简略地展现了 IoC 容器如何应用配置元数据来治理对象。
org.springframework.beans
和 org.springframework.context
这两个包是 IoC 实现的根底,如果想要钻研 IoC 相干的源码的话,能够去看看
将一个类申明为 Bean 的注解有哪些?
@Component
:通用的注解,可标注任意类为Spring
组件。如果一个 Bean 不晓得属于哪个层,能够应用@Component
注解标注。@Repository
: 对应长久层即 Dao 层,次要用于数据库相干操作。@Service
: 对应服务层,次要波及一些简单的逻辑,须要用到 Dao 层。@Controller
: 对应 Spring MVC 管制层,次要用户承受用户申请并调用 Service 层返回数据给前端页面。
@Component 和 @Bean 的区别是什么?
@Component
注解作用于类,而@Bean
注解作用于办法。@Component
通常是通过类门路扫描来主动侦测以及主动拆卸到 Spring 容器中(咱们能够应用@ComponentScan
注解定义要扫描的门路从中找出标识了须要拆卸的类主动拆卸到 Spring 的 bean 容器中)。@Bean
注解通常是咱们在标有该注解的办法中定义产生这个 bean,@Bean
通知了 Spring 这是某个类的实例,当我须要用它的时候还给我。@Bean
注解比@Component
注解的自定义性更强,而且很多中央咱们只能通过@Bean
注解来注册 bean。比方当咱们援用第三方库中的类须要拆卸到Spring
容器时,则只能通过@Bean
来实现。
@Bean
注解应用示例:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {return new TransferServiceImpl();
}
}
下面的代码相当于上面的 xml 配置
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
上面这个例子是通过 @Component
无奈实现的。
@Bean
public OneService getService(status) {case (status) {
when 1:
return new serviceImpl1();
when 2:
return new serviceImpl2();
when 3:
return new serviceImpl3();}
}
注入 Bean 的注解有哪些?
Spring 内置的 @Autowired
以及 JDK 内置的 @Resource
和 @Inject
都能够用于注入 Bean。
Annotaion | Package | Source |
---|---|---|
@Autowired |
org.springframework.bean.factory |
Spring 2.5+ |
@Resource |
javax.annotation |
Java JSR-250 |
@Inject |
javax.inject |
Java JSR-330 |
@Autowired
和 @Resource
应用的比拟多一些。
@Autowired 和 @Resource 的区别是什么?
Autowired
属于 Spring 内置的注解,默认的注入形式为byType
(依据类型进行匹配),也就是说会优先依据接口类型去匹配并注入 Bean(接口的实现类)。
这会有什么问题呢? 当一个接口存在多个实现类的话,byType
这种形式就无奈正确注入对象了,因为这个时候 Spring 会同时找到多个满足条件的抉择,默认状况下它本人不晓得抉择哪一个。
这种状况下,注入形式会变为 byName
(依据名称进行匹配),这个名称通常就是类名(首字母小写)。就比如说上面代码中的 smsService
就是我这里所说的名称,这样应该比拟好了解了吧。
// smsService 就是咱们下面所说的名称
@Autowired
private SmsService smsService;
举个例子,SmsService
接口有两个实现类: SmsServiceImpl1
和 SmsServiceImpl2
,且它们都曾经被 Spring 容器所治理。
// 报错,byName 和 byType 都无奈匹配到 bean
@Autowired
private SmsService smsService;
// 正确注入 SmsServiceImpl1 对象对应的 bean
@Autowired
private SmsService smsServiceImpl1;
// 正确注入 SmsServiceImpl1 对象对应的 bean
// smsServiceImpl1 就是咱们下面所说的名称
@Autowired
@Qualifier(value = "smsServiceImpl1")
private SmsService smsService;
咱们还是倡议通过 @Qualifier
注解来显示指定名称而不是依赖变量的名称。
@Resource
属于 JDK 提供的注解,默认注入形式为 byName
。如果无奈通过名称匹配到对应的 Bean 的话,注入形式会变为byType
。
@Resource
有两个比拟重要且日常开发罕用的属性:name
(名称)、type
(类型)。
public @interface Resource {String name() default "";
Class<?> type() default Object.class;}
如果仅指定 name
属性则注入形式为 byName
,如果仅指定type
属性则注入形式为 byType
,如果同时指定name
和type
属性(不倡议这么做)则注入形式为byType
+byName
。
// 报错,byName 和 byType 都无奈匹配到 bean
@Resource
private SmsService smsService;
// 正确注入 SmsServiceImpl1 对象对应的 bean
@Resource
private SmsService smsServiceImpl1;
// 正确注入 SmsServiceImpl1 对象对应的 bean(比拟举荐这种形式)@Resource(name = "smsServiceImpl1")
private SmsService smsService;
简略总结一下:
@Autowired
是 Spring 提供的注解,@Resource
是 JDK 提供的注解。Autowired
默认的注入形式为byType
(依据类型进行匹配),@Resource
默认注入形式为byName
(依据名称进行匹配)。- 当一个接口存在多个实现类的状况下,
@Autowired
和@Resource
都须要通过名称能力正确匹配到对应的 Bean。Autowired
能够通过@Qualifier
注解来显示指定名称,@Resource
能够通过name
属性来显示指定名称。
Bean 的作用域有哪些?
Spring 中 Bean 的作用域通常有上面几种:
- singleton : IoC 容器中只有惟一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的利用。
- prototype : 每次获取都会创立一个新的 bean 实例。也就是说,间断
getBean()
两次,失去的是不同的 Bean 实例。 - request(仅 Web 利用可用): 每一次 HTTP 申请都会产生一个新的 bean(申请 bean),该 bean 仅在以后 HTTP request 内无效。
- session(仅 Web 利用可用): 每一次来自新 session 的 HTTP 申请都会产生一个新的 bean(会话 bean),该 bean 仅在以后 HTTP session 内无效。
- application/global-session(仅 Web 利用可用):每个 Web 利用在启动时创立一个 Bean(利用 Bean),,该 bean 仅在以后利用启动工夫内无效。
- websocket(仅 Web 利用可用):每一次 WebSocket 会话产生一个新的 bean。
如何配置 bean 的作用域呢?
xml 形式:
<bean id="..." class="..." scope="singleton"></bean>
注解形式:
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {return new Person();
}
单例 Bean 的线程平安问题理解吗?
大部分时候咱们并没有在我的项目中应用多线程,所以很少有人会关注这个问题。单例 Bean 存在线程问题,次要是因为当多个线程操作同一个对象的时候是存在资源竞争的。
常见的有两种解决办法:
- 在 Bean 中尽量避免定义可变的成员变量。
- 在类中定义一个
ThreadLocal
成员变量,将须要的可变成员变量保留在ThreadLocal
中(举荐的一种形式)。
不过,大部分 Bean 理论都是无状态(没有实例变量)的(比方 Dao、Service),这种状况下,Bean 是线程平安的。
Bean 的生命周期理解么?
上面的内容整顿自:https://yemengying.com/2016/0…。
- Bean 容器找到配置文件中 Spring Bean 的定义。
- Bean 容器利用 Java Reflection API 创立一个 Bean 的实例。
- 如果波及到一些属性值 利用
set()
办法设置一些属性值。 - 如果 Bean 实现了
BeanNameAware
接口,调用setBeanName()
办法,传入 Bean 的名字。 - 如果 Bean 实现了
BeanClassLoaderAware
接口,调用setBeanClassLoader()
办法,传入ClassLoader
对象的实例。 - 如果 Bean 实现了
BeanFactoryAware
接口,调用setBeanFactory()
办法,传入BeanFactory
对象的实例。 - 与下面的相似,如果实现了其余
*.Aware
接口,就调用相应的办法。 - 如果有和加载这个 Bean 的 Spring 容器相干的
BeanPostProcessor
对象,执行postProcessBeforeInitialization()
办法 - 如果 Bean 实现了
InitializingBean
接口,执行afterPropertiesSet()
办法。 - 如果 Bean 在配置文件中的定义蕴含 init-method 属性,执行指定的办法。
- 如果有和加载这个 Bean 的 Spring 容器相干的
BeanPostProcessor
对象,执行postProcessAfterInitialization()
办法 - 当要销毁 Bean 的时候,如果 Bean 实现了
DisposableBean
接口,执行destroy()
办法。 - 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义蕴含 destroy-method 属性,执行指定的办法。
图示:
与之比拟相似的中文版本:
Spring AoP
谈谈本人对于 AOP 的理解
AOP(Aspect-Oriented Programming: 面向切面编程)可能将那些与业务无关,却为业务模块所独特调用的逻辑或责任(例如事务处理、日志治理、权限管制等)封装起来,便于缩小零碎的反复代码,升高模块间的耦合度,并有利于将来的可拓展性和可维护性。
Spring AOP 就是基于动静代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会应用 JDK Proxy,去创立代理对象,而对于没有实现接口的对象,就无奈应用 JDK Proxy 去进行代理了,这时候 Spring AOP 会应用 Cglib 生成一个被代理对象的子类来作为代理,如下图所示:
当然你也能够应用 AspectJ!Spring AOP 曾经集成了 AspectJ,AspectJ 应该算的上是 Java 生态系统中最残缺的 AOP 框架了。
AOP 切面编程设计到的一些专业术语:
术语 | 含意 |
---|---|
指标(Target) | 被告诉的对象 |
代理(Proxy) | 向指标对象利用告诉之后创立的代理对象 |
连接点(JoinPoint) | 指标对象的所属类中,定义的所有办法均为连接点 |
切入点(Pointcut) | 被切面拦挡 / 加强的连接点(切入点肯定是连接点,连接点不肯定是切入点) |
告诉(Advice) | 加强的逻辑 / 代码,也即拦挡到指标对象的连接点之后要做的事件 |
切面(Aspect) | 切入点(Pointcut)+ 告诉(Advice) |
Weaving(织入) | 将告诉利用到指标对象,进而生成代理对象的过程动作 |
Spring AOP 和 AspectJ AOP 有什么区别?
Spring AOP 属于运行时加强,而 AspectJ 是编译时加强。 Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。
Spring AOP 曾经集成了 AspectJ,AspectJ 应该算的上是 Java 生态系统中最残缺的 AOP 框架了。AspectJ 相比于 Spring AOP 性能更加弱小,然而 Spring AOP 相对来说更简略,
如果咱们的切面比拟少,那么两者性能差别不大。然而,当切面太多的话,最好抉择 AspectJ,它比 Spring AOP 快很多。
AspectJ 定义的告诉类型有哪些?
- Before(前置告诉):指标对象的办法调用之前触发
- After(后置告诉):指标对象的办法调用之后触发
- AfterReturning(返回告诉):指标对象的办法调用实现,在返回后果值之后触发
- AfterThrowing(异样告诉):指标对象的办法运行中抛出 / 触发异样后触发。AfterReturning 和 AfterThrowing 两者互斥。如果办法调用胜利无异样,则会有返回值;如果办法抛出了异样,则不会有返回值。
- Around:(盘绕告诉)编程式控制目标对象的办法调用。盘绕告诉是所有告诉类型中可操作范畴最大的一种,因为它能够间接拿到指标对象,以及要执行的办法,所以盘绕告诉能够任意的在指标对象的办法调用前后搞事,甚至不调用指标对象的办法
多个切面的执行程序如何管制?
1、通常应用@Order
注解间接定义切面程序
// 值越小优先级越高
@Order(3)
@Component
@Aspect
public class LoggingAspect implements Ordered {
2、实现Ordered
接口重写 getOrder
办法。
@Component
@Aspect
public class LoggingAspect implements Ordered {
// ....
@Override
public int getOrder() {
// 返回值越小优先级越高
return 1;
}
}
Spring MVC
说说本人对于 Spring MVC 理解?
MVC 是模型 (Model)、视图(View)、控制器(Controller) 的简写,其核心思想是通过将业务逻辑、数据、显示拆散来组织代码。
网上有很多人说 MVC 不是设计模式,只是软件设计规范,我集体更偏向于 MVC 同样是泛滥设计模式中的一种。java-design-patterns 我的项目中就有对于 MVC 的相干介绍。
想要真正了解 Spring MVC,咱们先来看看 Model 1 和 Model 2 这两个没有 Spring MVC 的时代。
Model 1 时代
很多学 Java 后端比拟晚的敌人可能并没有接触过 Model 1 时代下的 JavaWeb 利用开发。在 Model1 模式下,整个 Web 利用简直全副用 JSP 页面组成,只用大量的 JavaBean 来解决数据库连贯、拜访等操作。
这个模式下 JSP 即是管制层(Controller)又是体现层(View)。不言而喻,这种模式存在很多问题。比方管制逻辑和体现逻辑混淆在一起,导致代码重用率极低;再比方前端和后端相互依赖,难以进行测试保护并且开发效率极低。
Model 2 时代
学过 Servlet 并做过相干 Demo 的敌人应该理解“Java Bean(Model)+ JSP(View)+Servlet(Controller)”这种开发模式,这就是晚期的 JavaWeb MVC 开发模式。
- Model: 零碎波及的数据,也就是 dao 和 bean。
- View:展现模型中的数据,只是用来展现。
- Controller:解决用户申请都发送给,返回数据给 JSP 并展现给用户。
Model2 模式下还存在很多问题,Model2 的形象和封装水平还远远不够,应用 Model2 进行开发时不可避免地会反复造轮子,这就大大降低了程序的可维护性和复用性。
于是,很多 JavaWeb 开发相干的 MVC 框架应运而生比方 Struts2,然而 Struts2 比拟轻便。
Spring MVC 时代
随着 Spring 轻量级开发框架的风行,Spring 生态圈呈现了 Spring MVC 框架,Spring MVC 是以后最优良的 MVC 框架。相比于 Struts2,Spring MVC 应用更加简略和不便,开发效率更高,并且 Spring MVC 运行速度更快。
MVC 是一种设计模式,Spring MVC 是一款很优良的 MVC 框架。Spring MVC 能够帮忙咱们进行更简洁的 Web 层的开发,并且它天生与 Spring 框架集成。Spring MVC 下咱们个别把后端我的项目分为 Service 层(解决业务)、Dao 层(数据库操作)、Entity 层(实体类)、Controller 层(管制层,返回数据给前台页面)。
Spring MVC 的外围组件有哪些?
记住了上面这些组件,也就记住了 SpringMVC 的工作原理。
DispatcherServlet
:外围的中央处理器,负责接管申请、散发,并给予客户端响应。HandlerMapping
:处理器映射器,依据 uri 去匹配查找能解决的Handler
,并会将申请波及到的拦截器和Handler
一起封装。HandlerAdapter
:处理器适配器,依据HandlerMapping
找到的Handler
,适配执行对应的Handler
;Handler
:申请处理器,解决理论申请的处理器。ViewResolver
:视图解析器,依据Handler
返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给DispatcherServlet
响应客户端
SpringMVC 工作原理理解吗?
Spring MVC 原理如下图所示:
SpringMVC 工作原理的图解我没有本人画,间接图省事在网上找了一个十分清晰直观的,原出处不明。
流程阐明(重要):
- 客户端(浏览器)发送申请,
DispatcherServlet
拦挡申请。 DispatcherServlet
依据申请信息调用HandlerMapping
。HandlerMapping
依据 uri 去匹配查找能解决的Handler
(也就是咱们平时说的Controller
控制器),并会将申请波及到的拦截器和Handler
一起封装。DispatcherServlet
调用HandlerAdapter
适配执行Handler
。Handler
实现对用户申请的解决后,会返回一个ModelAndView
对象给DispatcherServlet
,ModelAndView
顾名思义,蕴含了数据模型以及相应的视图的信息。Model
是返回的数据对象,View
是个逻辑上的View
。ViewResolver
会依据逻辑View
查找理论的View
。DispaterServlet
把返回的Model
传给View
(视图渲染)。- 把
View
返回给请求者(浏览器)
对立异样解决怎么做?
举荐应用注解的形式对立异样解决,具体会应用到 @ControllerAdvice
+ @ExceptionHandler
这两个注解。
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {@ExceptionHandler(BaseException.class)
public ResponseEntity<?> handleAppException(BaseException ex, HttpServletRequest request) {//......}
@ExceptionHandler(value = ResourceNotFoundException.class)
public ResponseEntity<ErrorReponse> handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) {//......}
}
这种异样解决形式下,会给所有或者指定的 Controller
织入异样解决的逻辑(AOP),当 Controller
中的办法抛出异样的时候,由被@ExceptionHandler
注解润饰的办法进行解决。
ExceptionHandlerMethodResolver
中 getMappedMethod
办法决定了异样具体被哪个被 @ExceptionHandler
注解润饰的办法解决异样。
@Nullable
private Method getMappedMethod(Class<? extends Throwable> exceptionType) {List<Class<? extends Throwable>> matches = new ArrayList<>();
// 找到能够解决的所有异样信息。mappedMethods 中寄存了异样和解决异样的办法的对应关系
for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {if (mappedException.isAssignableFrom(exceptionType)) {matches.add(mappedException);
}
}
// 不为空阐明有办法解决异样
if (!matches.isEmpty()) {
// 依照匹配水平从小到大排序
matches.sort(new ExceptionDepthComparator(exceptionType));
// 返回解决异样的办法
return this.mappedMethods.get(matches.get(0));
}
else {return null;}
}
从源代码看出:getMappedMethod()
会首先找到能够匹配解决异样的所有办法信息,而后对其进行从小到大的排序,最初取最小的那一个匹配的办法(即匹配度最高的那个)。
Spring 框架中用到了哪些设计模式?
对于上面一些设计模式的具体介绍,能够看笔主前段时间的原创文章《面试官:“谈谈 Spring 中都用到了那些设计模式?”。》。
- 工厂设计模式 : Spring 应用工厂模式通过
BeanFactory
、ApplicationContext
创立 bean 对象。 - 代理设计模式 : Spring AOP 性能的实现。
- 单例设计模式 : Spring 中的 Bean 默认都是单例的。
- 模板办法模式 : Spring 中
jdbcTemplate
、hibernateTemplate
等以 Template 结尾的对数据库操作的类,它们就应用到了模板模式。 - 包装器设计模式 : 咱们的我的项目须要连贯多个数据库,而且不同的客户在每次拜访中依据须要会去拜访不同的数据库。这种模式让咱们能够依据客户的需要可能动静切换不同的数据源。
- 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个利用。
- 适配器模式 : Spring AOP 的加强或告诉(Advice) 应用到了适配器模式、spring MVC 中也是用到了适配器模式适配
Controller
。 - ……
Spring 事务
Spring/SpringBoot 模块下专门有一篇是讲 Spring 事务的,总结的十分具体,通俗易懂。
Spring 治理事务的形式有几种?
- 编程式事务 :在代码中硬编码(不举荐应用) : 通过
TransactionTemplate
或者TransactionManager
手动治理事务,理论利用中很少应用,然而对于你了解 Spring 事务管理原理有帮忙。 - 申明式事务:在 XML 配置文件中配置或者间接基于注解(举荐应用): 理论是通过 AOP 实现(基于
@Transactional
的全注解形式应用最多)
Spring 事务中哪几种事务流传行为?
事务流传行为是为了解决业务层办法之间相互调用的事务问题。
当事务办法被另一个事务办法调用时,必须指定事务应该如何流传。例如:办法可能持续在现有事务中运行,也可能开启一个新事务,并在本人的事务中运行。
正确的事务流传行为可能的值如下:
1.TransactionDefinition.PROPAGATION_REQUIRED
应用的最多的一个事务流传行为,咱们平时常常应用的 @Transactional
注解默认应用就是这个事务流传行为。如果以后存在事务,则退出该事务;如果以后没有事务,则创立一个新的事务。
2.TransactionDefinition.PROPAGATION_REQUIRES_NEW
创立一个新的事务,如果以后存在事务,则把以后事务挂起。也就是说不论内部办法是否开启事务,Propagation.REQUIRES_NEW
润饰的外部办法会新开启本人的事务,且开启的事务互相独立,互不烦扰。
3.TransactionDefinition.PROPAGATION_NESTED
如果以后存在事务,则创立一个事务作为以后事务的嵌套事务来运行;如果以后没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED
。
4.TransactionDefinition.PROPAGATION_MANDATORY
如果以后存在事务,则退出该事务;如果以后没有事务,则抛出异样。(mandatory:强制性)
这个应用的很少。
若是谬误的配置以下 3 种事务流传行为,事务将不会产生回滚:
TransactionDefinition.PROPAGATION_SUPPORTS
: 如果以后存在事务,则退出该事务;如果以后没有事务,则以非事务的形式持续运行。TransactionDefinition.PROPAGATION_NOT_SUPPORTED
: 以非事务形式运行,如果以后存在事务,则把以后事务挂起。TransactionDefinition.PROPAGATION_NEVER
: 以非事务形式运行,如果以后存在事务,则抛出异样。
Spring 事务中的隔离级别有哪几种?
和事务流传行为这块一样,为了方便使用,Spring 也相应地定义了一个枚举类:Isolation
public enum Isolation {DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),
READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),
REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),
SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);
private final int value;
Isolation(int value) {this.value = value;}
public int value() {return this.value;}
}
上面我顺次对每一种事务隔离级别进行介绍:
TransactionDefinition.ISOLATION_DEFAULT
: 应用后端数据库默认的隔离级别,MySQL 默认采纳的REPEATABLE_READ
隔离级别 Oracle 默认采纳的READ_COMMITTED
隔离级别.TransactionDefinition.ISOLATION_READ_UNCOMMITTED
: 最低的隔离级别,应用这个隔离级别很少,因为它容许读取尚未提交的数据变更,可能会导致脏读、幻读或不可反复读TransactionDefinition.ISOLATION_READ_COMMITTED
: 容许读取并发事务曾经提交的数据,能够阻止脏读,然而幻读或不可反复读仍有可能产生TransactionDefinition.ISOLATION_REPEATABLE_READ
: 对同一字段的屡次读取后果都是统一的,除非数据是被自身事务本人所批改,能够阻止脏读和不可反复读,但幻读仍有可能产生。TransactionDefinition.ISOLATION_SERIALIZABLE
: 最高的隔离级别,齐全遵从 ACID 的隔离级别。所有的事务顺次一一执行,这样事务之间就齐全不可能产生烦扰,也就是说,该级别能够避免脏读、不可反复读以及幻读。然而这将重大影响程序的性能。通常状况下也不会用到该级别。
@Transactional(rollbackFor = Exception.class)注解理解吗?
Exception
分为运行时异样 RuntimeException
和非运行时异样。事务管理对于企业应用来说是至关重要的,即便出现异常状况,它也能够保证数据的一致性。
当 @Transactional
注解作用于类上时,该类的所有 public 办法将都具备该类型的事务属性,同时,咱们也能够在办法级别应用该标注来笼罩类级别的定义。如果类或者办法加了这个注解,那么这个类外面的办法抛出异样,就会回滚,数据库外面的数据也会回滚。
在 @Transactional
注解中如果不配置 rollbackFor
属性, 那么事务只会在遇到 RuntimeException
的时候才会回滚,加上 rollbackFor=Exception.class
, 能够让事务在遇到非运行时异样时也回滚。
Spring Data JPA
JPA 重要的是实战,这里仅对小局部知识点进行总结。
如何应用 JPA 在数据库中非长久化一个字段?
如果咱们有上面一个类:
@Entity(name="USER")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID")
private Long id;
@Column(name="USER_NAME")
private String userName;
@Column(name="PASSWORD")
private String password;
private String secrect;
}
如果咱们想让secrect
这个字段不被长久化,也就是不被数据库存储怎么办?咱们能够采纳上面几种办法:
static String transient1; // not persistent because of static
final String transient2 = "Satish"; // not persistent because of final
transient String transient3; // not persistent because of transient
@Transient
String transient4; // not persistent because of @Transient
个别应用前面两种形式比拟多,我集体应用注解的形式比拟多。
JPA 的审计性能是做什么的?有什么用?
审计性能次要是帮忙咱们记录数据库操作的具体行为比方某条记录是谁创立的、什么工夫创立的、最初批改人是谁、最初批改工夫是什么时候。
@Data
@AllArgsConstructor
@NoArgsConstructor
@MappedSuperclass
@EntityListeners(value = AuditingEntityListener.class)
public abstract class AbstractAuditBase {
@CreatedDate
@Column(updatable = false)
@JsonIgnore
private Instant createdAt;
@LastModifiedDate
@JsonIgnore
private Instant updatedAt;
@CreatedBy
@Column(updatable = false)
@JsonIgnore
private String createdBy;
@LastModifiedBy
@JsonIgnore
private String updatedBy;
}
@CreatedDate
: 示意该字段为创立工夫字段,在这个实体被 insert 的时候,会设置值-
@CreatedBy
: 示意该字段为创建人,在这个实体被 insert 的时候,会设置值@LastModifiedDate
、@LastModifiedBy
同理。
实体之间的关联关系注解有哪些?
@OneToOne
: 一对一。@ManyToMany
:多对多。@OneToMany
: 一对多。@ManyToOne
:多对一。
利用 @ManyToOne
和 @OneToMany
也能够表白多对多的关联关系。
Spring Security
Spring Security 重要的是实战,这里仅对小局部知识点进行总结。
有哪些管制申请拜访权限的办法?
permitAll()
:无条件容许任何模式拜访,不论你登录还是没有登录。anonymous()
:容许匿名拜访,也就是没有登录才能够拜访。denyAll()
:无条件决绝任何模式的拜访。authenticated()
:只容许已认证的用户拜访。fullyAuthenticated()
:只容许曾经登录或者通过 remember-me 登录的用户拜访。hasRole(String)
: 只容许指定的角色拜访。hasAnyRole(String)
: 指定一个或者多个角色,满足其一的用户即可拜访。hasAuthority(String)
:只容许具备指定权限的用户拜访hasAnyAuthority(String)
:指定一个或者多个权限,满足其一的用户即可拜访。hasIpAddress(String)
: 只容许指定 ip 的用户拜访。
hasRole 和 hasAuthority 有区别吗?
能够看看松哥的这篇文章:Spring Security 中的 hasRole 和 hasAuthority 有区别吗?,介绍的比拟具体。
如何对明码进行加密?
如果咱们须要保留明码这类敏感数据到数据库的话,须要先加密再保留。
Spring Security 提供了多种加密算法的实现,开箱即用,十分不便。这些加密算法实现类的父类是 PasswordEncoder
,如果你想要本人实现一个加密算法的话,也须要继承 PasswordEncoder
。
PasswordEncoder
接口一共也就 3 个必须实现的办法。
public interface PasswordEncoder {
// 加密也就是对原始明码进行编码
String encode(CharSequence var1);
// 比对原始明码和数据库中保留的明码
boolean matches(CharSequence var1, String var2);
// 判断加密明码是否须要再次进行加密,默认返回 false
default boolean upgradeEncoding(String encodedPassword) {return false;}
}
官网举荐应用基于 bcrypt 强哈希函数的加密算法实现类。
如何优雅更换零碎应用的加密算法?
如果咱们在开发过程中,忽然发现现有的加密算法无奈满足咱们的需要,须要更换成另外一个加密算法,这个时候应该怎么办呢?
举荐的做法是通过 DelegatingPasswordEncoder
兼容多种不同的明码加密计划,以适应不同的业务需要。
从名字也能看进去,DelegatingPasswordEncoder
其实就是一个代理类,并非是一种全新的加密算法,它做的事件就是代理下面提到的加密算法实现类。在 Spring Security 5.0 之后,默认就是基于 DelegatingPasswordEncoder
进行明码加密的。
参考
- 《Spring 技术底细》
- 《从零开始深刻学习 Spring》:https://juejin.cn/book/685791…
- http://www.cnblogs.com/wmyskx…
- https://www.journaldev.com/26…
- https://www.edureka.co/blog/i…
- https://www.cnblogs.com/clwyd…
- https://howtodoinjava.com/int…
- http://www.tomaszezula.com/20…
- https://stackoverflow.com/que…
后记
专一 Java 原创干货分享,大三开源 JavaGuide(「Java 学习 + 面试指南」一份涵盖大部分 Java 程序员所须要把握的外围常识。筹备 Java 面试,首选 JavaGuide!),目前曾经 120k+ Star。
如果本文对你有帮忙的话,欢送点赞分享,这对我持续分享 & 创作优质文章十分重要。感激 🙏🏻