32 道 Spring 常見(jiàn)面試題!萬(wàn)字總結(jié)!
你好,我是 Guide。昨晚,我對(duì) JavaGuide 上的 Spring 面試題部分的內(nèi)容進(jìn)行了重構(gòu)完善,公眾號(hào)同步一下最新更新,希望能夠幫助你。
你也可以在網(wǎng)站(javaguide.cn)上在線(xiàn)閱讀,閱讀體驗(yàn)會(huì)更好!

Spring 基礎(chǔ)
什么是 Spring 框架?
Spring 是一款開(kāi)源的輕量級(jí) Java 開(kāi)發(fā)框架,旨在提高開(kāi)發(fā)人員的開(kāi)發(fā)效率以及系統(tǒng)的可維護(hù)性。
我們一般說(shuō) Spring 框架指的都是 Spring Framework,它是很多模塊的集合,使用這些模塊可以很方便地協(xié)助我們進(jìn)行開(kāi)發(fā),比如說(shuō) Spring 支持 IoC(Inverse of Control:控制反轉(zhuǎn)) 和 AOP(Aspect-Oriented Programming:面向切面編程)、可以很方便地對(duì)數(shù)據(jù)庫(kù)進(jìn)行訪問(wèn)、可以很方便地集成第三方組件(電子郵件,任務(wù),調(diào)度,緩存等等)、對(duì)單元測(cè)試支持比較好、支持 RESTful Java 應(yīng)用程序的開(kāi)發(fā)。

Spring 最核心的思想就是不重新造輪子,開(kāi)箱即用,提高開(kāi)發(fā)效率。
Spring 翻譯過(guò)來(lái)就是春天的意思,可見(jiàn)其目標(biāo)和使命就是為 Java 程序員帶來(lái)春天??!感動(dòng)!
?? 多提一嘴 :語(yǔ)言的流行通常需要一個(gè)殺手級(jí)的應(yīng)用,Spring 就是 Java 生態(tài)的一個(gè)殺手級(jí)的應(yīng)用框架。
Spring 提供的核心功能主要是 IoC 和 AOP。學(xué)習(xí) Spring ,一定要把 IoC 和 AOP 的核心思想搞懂!
Spring 官網(wǎng):https://spring.io/ Github 地址:https://github.com/spring-projects/spring-framework
Spring 包含的模塊有哪些?
Spring4.x 版本 :

Spring5.x 版本 :

Spring5.x 版本中 Web 模塊的 Portlet 組件已經(jīng)被廢棄掉,同時(shí)增加了用于異步響應(yīng)式處理的 WebFlux 組件。
Spring 各個(gè)模塊的依賴(lài)關(guān)系如下:

Core Container
Spring 框架的核心模塊,也可以說(shuō)是基礎(chǔ)模塊,主要提供 IoC 依賴(lài)注入功能的支持。Spring 其他所有的功能基本都需要依賴(lài)于該模塊,我們從上面那張 Spring 各個(gè)模塊的依賴(lài)關(guān)系圖就可以看出來(lái)。
spring-core :Spring 框架基本的核心工具類(lèi)。 spring-beans :提供對(duì) bean 的創(chuàng)建、配置和管理等功能的支持。 spring-context :提供對(duì)國(guó)際化、事件傳播、資源加載等功能的支持。 spring-expression :提供對(duì)表達(dá)式語(yǔ)言(Spring Expression Language) SpEL 的支持,只依賴(lài)于 core 模塊,不依賴(lài)于其他模塊,可以單獨(dú)使用。
AOP
spring-aspects :該模塊為與 AspectJ 的集成提供支持。 spring-aop :提供了面向切面的編程實(shí)現(xiàn)。 spring-instrument :提供了為 JVM 添加代理(agent)的功能。具體來(lái)講,它為 Tomcat 提供了一個(gè)織入代理,能夠?yàn)?Tomcat 傳遞類(lèi)文 件,就像這些文件是被類(lèi)加載器加載的一樣。沒(méi)有理解也沒(méi)關(guān)系,這個(gè)模塊的使用場(chǎng)景非常有限。
Data Access/Integration
spring-jdbc :提供了對(duì)數(shù)據(jù)庫(kù)訪問(wèn)的抽象 JDBC。不同的數(shù)據(jù)庫(kù)都有自己獨(dú)立的 API 用于操作數(shù)據(jù)庫(kù),而 Java 程序只需要和 JDBC API 交互,這樣就屏蔽了數(shù)據(jù)庫(kù)的影響。 spring-tx :提供對(duì)事務(wù)的支持。 spring-orm :提供對(duì) Hibernate、JPA 、iBatis 等 ORM 框架的支持。 spring-oxm :提供一個(gè)抽象層支撐 OXM(Object-to-XML-Mapping),例如:JAXB、Castor、XMLBeans、JiBX 和 XStream 等。 spring-jms : 消息服務(wù)。自 Spring Framework 4.1 以后,它還提供了對(duì) spring-messaging 模塊的繼承。
Spring Web
spring-web :對(duì) Web 功能的實(shí)現(xiàn)提供一些最基礎(chǔ)的支持。 spring-webmvc :提供對(duì) Spring MVC 的實(shí)現(xiàn)。 spring-websocket :提供了對(duì) WebSocket 的支持,WebSocket 可以讓客戶(hù)端和服務(wù)端進(jìn)行雙向通信。 spring-webflux :提供對(duì) WebFlux 的支持。WebFlux 是 Spring Framework 5.0 中引入的新的響應(yīng)式框架。與 Spring MVC 不同,它不需要 Servlet API,是完全異步。
Messaging
spring-messaging 是從 Spring4.0 開(kāi)始新加入的一個(gè)模塊,主要職責(zé)是為 Spring 框架集成一些基礎(chǔ)的報(bào)文傳送應(yīng)用。
Spring Test
Spring 團(tuán)隊(duì)提倡測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(TDD)。有了控制反轉(zhuǎn) (IoC)的幫助,單元測(cè)試和集成測(cè)試變得更簡(jiǎn)單。
Spring 的測(cè)試模塊對(duì) JUnit(單元測(cè)試框架)、TestNG(類(lèi)似 JUnit)、Mockito(主要用來(lái) Mock 對(duì)象)、PowerMock(解決 Mockito 的問(wèn)題比如無(wú)法模擬 final, static, private 方法)等等常用的測(cè)試框架支持的都比較好。
Spring,Spring MVC,Spring Boot 之間什么關(guān)系?
很多人對(duì) Spring,Spring MVC,Spring Boot 這三者傻傻分不清楚!這里簡(jiǎn)單介紹一下這三者,其實(shí)很簡(jiǎn)單,沒(méi)有什么高深的東西。
Spring 包含了多個(gè)功能模塊(上面剛剛提高過(guò)),其中最重要的是 Spring-Core(主要提供 IoC 依賴(lài)注入功能的支持) 模塊, Spring 中的其他模塊(比如 Spring MVC)的功能實(shí)現(xiàn)基本都需要依賴(lài)于該模塊。
下圖對(duì)應(yīng)的是 Spring4.x 版本。目前最新的 5.x 版本中 Web 模塊的 Portlet 組件已經(jīng)被廢棄掉,同時(shí)增加了用于異步響應(yīng)式處理的 WebFlux 組件。

Spring MVC 是 Spring 中的一個(gè)很重要的模塊,主要賦予 Spring 快速構(gòu)建 MVC 架構(gòu)的 Web 程序的能力。MVC 是模型(Model)、視圖(View)、控制器(Controller)的簡(jiǎn)寫(xiě),其核心思想是通過(guò)將業(yè)務(wù)邏輯、數(shù)據(jù)、顯示分離來(lái)組織代碼。

使用 Spring 進(jìn)行開(kāi)發(fā)各種配置過(guò)于麻煩比如開(kāi)啟某些 Spring 特性時(shí),需要用 XML 或 Java 進(jìn)行顯式配置。于是,Spring Boot 誕生了!
Spring 旨在簡(jiǎn)化 J2EE 企業(yè)應(yīng)用程序開(kāi)發(fā)。Spring Boot 旨在簡(jiǎn)化 Spring 開(kāi)發(fā)(減少配置文件,開(kāi)箱即用?。?。
Spring Boot 只是簡(jiǎn)化了配置,如果你需要構(gòu)建 MVC 架構(gòu)的 Web 程序,你還是需要使用 Spring MVC 作為 MVC 框架,只是說(shuō) Spring Boot 幫你簡(jiǎn)化了 Spring MVC 的很多配置,真正做到開(kāi)箱即用!
Spring IoC
談?wù)勛约簩?duì)于 Spring IoC 的了解
IoC(Inverse of Control:控制反轉(zhuǎn)) 是一種設(shè)計(jì)思想,而不是一個(gè)具體的技術(shù)實(shí)現(xiàn)。IoC 的思想就是將原本在程序中手動(dòng)創(chuàng)建對(duì)象的控制權(quán),交由 Spring 框架來(lái)管理。不過(guò), IoC 并非 Spring 特有,在其他語(yǔ)言中也有應(yīng)用。
為什么叫控制反轉(zhuǎn)?
控制 :指的是對(duì)象創(chuàng)建(實(shí)例化、管理)的權(quán)力 反轉(zhuǎn) :控制權(quán)交給外部環(huán)境(Spring 框架、IoC 容器)

將對(duì)象之間的相互依賴(lài)關(guān)系交給 IoC 容器來(lái)管理,并由 IoC 容器完成對(duì)象的注入。這樣可以很大程度上簡(jiǎn)化應(yīng)用的開(kāi)發(fā),把應(yīng)用從復(fù)雜的依賴(lài)關(guān)系中解放出來(lái)。IoC 容器就像是一個(gè)工廠一樣,當(dāng)我們需要?jiǎng)?chuàng)建一個(gè)對(duì)象的時(shí)候,只需要配置好配置文件/注解即可,完全不用考慮對(duì)象是如何被創(chuàng)建出來(lái)的。
在實(shí)際項(xiàng)目中一個(gè) Service 類(lèi)可能依賴(lài)了很多其他的類(lèi),假如我們需要實(shí)例化這個(gè) Service,你可能要每次都要搞清這個(gè) Service 所有底層類(lèi)的構(gòu)造函數(shù),這可能會(huì)把人逼瘋。如果利用 IoC 的話(huà),你只需要配置好,然后在需要的地方引用就行了,這大大增加了項(xiàng)目的可維護(hù)性且降低了開(kāi)發(fā)難度。
在 Spring 中, IoC 容器是 Spring 用來(lái)實(shí)現(xiàn) IoC 的載體, IoC 容器實(shí)際上就是個(gè) Map(key,value),Map 中存放的是各種對(duì)象。
Spring 時(shí)代我們一般通過(guò) XML 文件來(lái)配置 Bean,后來(lái)開(kāi)發(fā)人員覺(jué)得 XML 文件來(lái)配置不太好,于是 SpringBoot 注解配置就慢慢開(kāi)始流行起來(lái)。
相關(guān)閱讀:
什么是 Spring Bean?
簡(jiǎn)單來(lái)說(shuō),Bean 代指的就是那些被 IoC 容器所管理的對(duì)象。
我們需要告訴 IoC 容器幫助我們管理哪些對(duì)象,這個(gè)是通過(guò)配置元數(shù)據(jù)來(lái)定義的。配置元數(shù)據(jù)可以是 XML 文件、注解或者 Java 配置類(lèi)。
<!-- Constructor-arg with 'value' attribute -->
<bean id="..." class="...">
<constructor-arg value="..."/>
</bean>
下圖簡(jiǎn)單地展示了 IoC 容器如何使用配置元數(shù)據(jù)來(lái)管理對(duì)象。

org.springframework.beans和 org.springframework.context 這兩個(gè)包是 IoC 實(shí)現(xiàn)的基礎(chǔ),如果想要研究 IoC 相關(guān)的源碼的話(huà),可以去看看
將一個(gè)類(lèi)聲明為 Bean 的注解有哪些?
@Component:通用的注解,可標(biāo)注任意類(lèi)為Spring組件。如果一個(gè) Bean 不知道屬于哪個(gè)層,可以使用@Component注解標(biāo)注。@Repository: 對(duì)應(yīng)持久層即 Dao 層,主要用于數(shù)據(jù)庫(kù)相關(guān)操作。@Service: 對(duì)應(yīng)服務(wù)層,主要涉及一些復(fù)雜的邏輯,需要用到 Dao 層。@Controller: 對(duì)應(yīng) Spring MVC 控制層,主要用戶(hù)接受用戶(hù)請(qǐng)求并調(diào)用 Service 層返回?cái)?shù)據(jù)給前端頁(yè)面。
@Component 和 @Bean 的區(qū)別是什么?
@Component注解作用于類(lèi),而@Bean注解作用于方法。@Component通常是通過(guò)類(lèi)路徑掃描來(lái)自動(dòng)偵測(cè)以及自動(dòng)裝配到 Spring 容器中(我們可以使用@ComponentScan注解定義要掃描的路徑從中找出標(biāo)識(shí)了需要裝配的類(lèi)自動(dòng)裝配到 Spring 的 bean 容器中)。@Bean注解通常是我們?cè)跇?biāo)有該注解的方法中定義產(chǎn)生這個(gè) bean,@Bean告訴了 Spring 這是某個(gè)類(lèi)的實(shí)例,當(dāng)我需要用它的時(shí)候還給我。@Bean注解比@Component注解的自定義性更強(qiáng),而且很多地方我們只能通過(guò)@Bean注解來(lái)注冊(cè) bean。比如當(dāng)我們引用第三方庫(kù)中的類(lèi)需要裝配到Spring容器時(shí),則只能通過(guò)@Bean來(lái)實(shí)現(xiàn)。
@Bean注解使用示例:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
上面的代碼相當(dāng)于下面的 xml 配置
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
下面這個(gè)例子是通過(guò) @Component 無(wú)法實(shí)現(xiàn)的。
@Bean
public OneService getService(status) {
case (status) {
when 1:
return new serviceImpl1();
when 2:
return new serviceImpl2();
when 3:
return new serviceImpl3();
}
}
注入 Bean 的注解有哪些?
Spring 內(nèi)置的 @Autowired 以及 JDK 內(nèi)置的 @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 的區(qū)別是什么?
Autowired 屬于 Spring 內(nèi)置的注解,默認(rèn)的注入方式為byType(根據(jù)類(lèi)型進(jìn)行匹配),也就是說(shuō)會(huì)優(yōu)先根據(jù)接口類(lèi)型去匹配并注入 Bean (接口的實(shí)現(xiàn)類(lèi))。
這會(huì)有什么問(wèn)題呢? 當(dāng)一個(gè)接口存在多個(gè)實(shí)現(xiàn)類(lèi)的話(huà),byType這種方式就無(wú)法正確注入對(duì)象了,因?yàn)檫@個(gè)時(shí)候 Spring 會(huì)同時(shí)找到多個(gè)滿(mǎn)足條件的選擇,默認(rèn)情況下它自己不知道選擇哪一個(gè)。
這種情況下,注入方式會(huì)變?yōu)?byName(根據(jù)名稱(chēng)進(jìn)行匹配),這個(gè)名稱(chēng)通常就是類(lèi)名(首字母小寫(xiě))。就比如說(shuō)下面代碼中的 smsService 就是我這里所說(shuō)的名稱(chēng),這樣應(yīng)該比較好理解了吧。
// smsService 就是我們上面所說(shuō)的名稱(chēng)
@Autowired
private SmsService smsService;
舉個(gè)例子,SmsService 接口有兩個(gè)實(shí)現(xiàn)類(lèi): SmsServiceImpl1和 SmsServiceImpl2,且它們都已經(jīng)被 Spring 容器所管理。
// 報(bào)錯(cuò),byName 和 byType 都無(wú)法匹配到 bean
@Autowired
private SmsService smsService;
// 正確注入 SmsServiceImpl1 對(duì)象對(duì)應(yīng)的 bean
@Autowired
private SmsService smsServiceImpl1;
// 正確注入 SmsServiceImpl1 對(duì)象對(duì)應(yīng)的 bean
// smsServiceImpl1 就是我們上面所說(shuō)的名稱(chēng)
@Autowired
@Qualifier(value = "smsServiceImpl1")
private SmsService smsService;
我們還是建議通過(guò) @Qualifier 注解來(lái)顯示指定名稱(chēng)而不是依賴(lài)變量的名稱(chēng)。
@Resource屬于 JDK 提供的注解,默認(rèn)注入方式為 byName。如果無(wú)法通過(guò)名稱(chēng)匹配到對(duì)應(yīng)的 Bean 的話(huà),注入方式會(huì)變?yōu)?code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #2980B9;background: ECF0F1;">byType。
@Resource 有兩個(gè)比較重要且日常開(kāi)發(fā)常用的屬性:name(名稱(chēng))、type(類(lèi)型)。
public @interface Resource {
String name() default "";
Class<?> type() default Object.class;
}
如果僅指定 name 屬性則注入方式為byName,如果僅指定type屬性則注入方式為byType,如果同時(shí)指定name 和type屬性(不建議這么做)則注入方式為byType+byName。
// 報(bào)錯(cuò),byName 和 byType 都無(wú)法匹配到 bean
@Resource
private SmsService smsService;
// 正確注入 SmsServiceImpl1 對(duì)象對(duì)應(yīng)的 bean
@Resource
private SmsService smsServiceImpl1;
// 正確注入 SmsServiceImpl1 對(duì)象對(duì)應(yīng)的 bean(比較推薦這種方式)
@Resource(name = "smsServiceImpl1")
private SmsService smsService;
簡(jiǎn)單總結(jié)一下:
@Autowired是 Spring 提供的注解,@Resource是 JDK 提供的注解。Autowired默認(rèn)的注入方式為byType(根據(jù)類(lèi)型進(jìn)行匹配),@Resource默認(rèn)注入方式為byName(根據(jù)名稱(chēng)進(jìn)行匹配)。當(dāng)一個(gè)接口存在多個(gè)實(shí)現(xiàn)類(lèi)的情況下, @Autowired和@Resource都需要通過(guò)名稱(chēng)才能正確匹配到對(duì)應(yīng)的 Bean。Autowired可以通過(guò)@Qualifier注解來(lái)顯示指定名稱(chēng),@Resource可以通過(guò)name屬性來(lái)顯示指定名稱(chēng)。
Bean 的作用域有哪些?
Spring 中 Bean 的作用域通常有下面幾種:
singleton : IoC 容器中只有唯一的 bean 實(shí)例。Spring 中的 bean 默認(rèn)都是單例的,是對(duì)單例設(shè)計(jì)模式的應(yīng)用。 prototype : 每次獲取都會(huì)創(chuàng)建一個(gè)新的 bean 實(shí)例。也就是說(shuō),連續(xù) getBean()兩次,得到的是不同的 Bean 實(shí)例。request (僅 Web 應(yīng)用可用): 每一次 HTTP 請(qǐng)求都會(huì)產(chǎn)生一個(gè)新的 bean(請(qǐng)求 bean),該 bean 僅在當(dāng)前 HTTP request 內(nèi)有效。 session (僅 Web 應(yīng)用可用) : 每一次來(lái)自新 session 的 HTTP 請(qǐng)求都會(huì)產(chǎn)生一個(gè)新的 bean(會(huì)話(huà) bean),該 bean 僅在當(dāng)前 HTTP session 內(nèi)有效。 application/global-session (僅 Web 應(yīng)用可用):每個(gè) Web 應(yīng)用在啟動(dòng)時(shí)創(chuàng)建一個(gè) Bean(應(yīng)用 Bean),,該 bean 僅在當(dāng)前應(yīng)用啟動(dòng)時(shí)間內(nèi)有效。 websocket (僅 Web 應(yīng)用可用):每一次 WebSocket 會(huì)話(huà)產(chǎn)生一個(gè)新的 bean。
如何配置 bean 的作用域呢?
xml 方式:
<bean id="..." class="..." scope="singleton"></bean>
注解方式:
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {
return new Person();
}
單例 Bean 的線(xiàn)程安全問(wèn)題了解嗎?
大部分時(shí)候我們并沒(méi)有在項(xiàng)目中使用多線(xiàn)程,所以很少有人會(huì)關(guān)注這個(gè)問(wèn)題。單例 Bean 存在線(xiàn)程問(wèn)題,主要是因?yàn)楫?dāng)多個(gè)線(xiàn)程操作同一個(gè)對(duì)象的時(shí)候是存在資源競(jìng)爭(zhēng)的。
常見(jiàn)的有兩種解決辦法:
在 Bean 中盡量避免定義可變的成員變量。 在類(lèi)中定義一個(gè) ThreadLocal成員變量,將需要的可變成員變量保存在ThreadLocal中(推薦的一種方式)。
不過(guò),大部分 Bean 實(shí)際都是無(wú)狀態(tài)(沒(méi)有實(shí)例變量)的(比如 Dao、Service),這種情況下, Bean 是線(xiàn)程安全的。
Bean 的生命周期了解么?
下面的內(nèi)容整理自:https://yemengying.com/2016/07/14/spring-bean-life-cycle/ ,除了這篇文章,再推薦一篇很不錯(cuò)的文章 :https://www.cnblogs.com/zrtqsk/p/3735273.html 。
Bean 容器找到配置文件中 Spring Bean 的定義。 Bean 容器利用 Java Reflection API 創(chuàng)建一個(gè) Bean 的實(shí)例。 如果涉及到一些屬性值 利用 set()方法設(shè)置一些屬性值。如果 Bean 實(shí)現(xiàn)了 BeanNameAware接口,調(diào)用setBeanName()方法,傳入 Bean 的名字。如果 Bean 實(shí)現(xiàn)了 BeanClassLoaderAware接口,調(diào)用setBeanClassLoader()方法,傳入ClassLoader對(duì)象的實(shí)例。如果 Bean 實(shí)現(xiàn)了 BeanFactoryAware接口,調(diào)用setBeanFactory()方法,傳入BeanFactory對(duì)象的實(shí)例。與上面的類(lèi)似,如果實(shí)現(xiàn)了其他 *.Aware接口,就調(diào)用相應(yīng)的方法。如果有和加載這個(gè) Bean 的 Spring 容器相關(guān)的 BeanPostProcessor對(duì)象,執(zhí)行postProcessBeforeInitialization()方法如果 Bean 實(shí)現(xiàn)了 InitializingBean接口,執(zhí)行afterPropertiesSet()方法。如果 Bean 在配置文件中的定義包含 init-method 屬性,執(zhí)行指定的方法。 如果有和加載這個(gè) Bean 的 Spring 容器相關(guān)的 BeanPostProcessor對(duì)象,執(zhí)行postProcessAfterInitialization()方法當(dāng)要銷(xiāo)毀 Bean 的時(shí)候,如果 Bean 實(shí)現(xiàn)了 DisposableBean接口,執(zhí)行destroy()方法。當(dāng)要銷(xiāo)毀 Bean 的時(shí)候,如果 Bean 在配置文件中的定義包含 destroy-method 屬性,執(zhí)行指定的方法。
圖示:

與之比較類(lèi)似的中文版本:

Spring AoP
談?wù)勛约簩?duì)于 AOP 的了解
AOP(Aspect-Oriented Programming:面向切面編程)能夠?qū)⒛切┡c業(yè)務(wù)無(wú)關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任(例如事務(wù)處理、日志管理、權(quán)限控制等)封裝起來(lái),便于減少系統(tǒng)的重復(fù)代碼,降低模塊間的耦合度,并有利于未來(lái)的可拓展性和可維護(hù)性。
Spring AOP 就是基于動(dòng)態(tài)代理的,如果要代理的對(duì)象,實(shí)現(xiàn)了某個(gè)接口,那么 Spring AOP 會(huì)使用 JDK Proxy,去創(chuàng)建代理對(duì)象,而對(duì)于沒(méi)有實(shí)現(xiàn)接口的對(duì)象,就無(wú)法使用 JDK Proxy 去進(jìn)行代理了,這時(shí)候 Spring AOP 會(huì)使用 Cglib 生成一個(gè)被代理對(duì)象的子類(lèi)來(lái)作為代理,如下圖所示:

當(dāng)然你也可以使用 AspectJ !Spring AOP 已經(jīng)集成了 AspectJ ,AspectJ 應(yīng)該算的上是 Java 生態(tài)系統(tǒng)中最完整的 AOP 框架了。
AOP 切面編程設(shè)計(jì)到的一些專(zhuān)業(yè)術(shù)語(yǔ):
| 術(shù)語(yǔ) | 含義 |
|---|---|
| 目標(biāo)(Target) | 被通知的對(duì)象 |
| 代理(Proxy) | 向目標(biāo)對(duì)象應(yīng)用通知之后創(chuàng)建的代理對(duì)象 |
| 連接點(diǎn)(JoinPoint) | 目標(biāo)對(duì)象的所屬類(lèi)中,定義的所有方法均為連接點(diǎn) |
| 切入點(diǎn)(Pointcut) | 被切面攔截 / 增強(qiáng)的連接點(diǎn)(切入點(diǎn)一定是連接點(diǎn),連接點(diǎn)不一定是切入點(diǎn)) |
| 通知(Advice) | 增強(qiáng)的邏輯 / 代碼,也即攔截到目標(biāo)對(duì)象的連接點(diǎn)之后要做的事情 |
| 切面(Aspect) | 切入點(diǎn)(Pointcut)+通知(Advice) |
| Weaving(織入) | 將通知應(yīng)用到目標(biāo)對(duì)象,進(jìn)而生成代理對(duì)象的過(guò)程動(dòng)作 |
Spring AOP 和 AspectJ AOP 有什么區(qū)別?
Spring AOP 屬于運(yùn)行時(shí)增強(qiáng),而 AspectJ 是編譯時(shí)增強(qiáng)。 Spring AOP 基于代理(Proxying),而 AspectJ 基于字節(jié)碼操作(Bytecode Manipulation)。
Spring AOP 已經(jīng)集成了 AspectJ ,AspectJ 應(yīng)該算的上是 Java 生態(tài)系統(tǒng)中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加強(qiáng)大,但是 Spring AOP 相對(duì)來(lái)說(shuō)更簡(jiǎn)單,
如果我們的切面比較少,那么兩者性能差異不大。但是,當(dāng)切面太多的話(huà),最好選擇 AspectJ ,它比 Spring AOP 快很多。
AspectJ 定義的通知類(lèi)型有哪些?
Before(前置通知):目標(biāo)對(duì)象的方法調(diào)用之前觸發(fā) After (后置通知):目標(biāo)對(duì)象的方法調(diào)用之后觸發(fā) AfterReturning(返回通知):目標(biāo)對(duì)象的方法調(diào)用完成,在返回結(jié)果值之后觸發(fā) AfterThrowing(異常通知) :目標(biāo)對(duì)象的方法運(yùn)行中拋出 / 觸發(fā)異常后觸發(fā)。AfterReturning 和 AfterThrowing 兩者互斥。如果方法調(diào)用成功無(wú)異常,則會(huì)有返回值;如果方法拋出了異常,則不會(huì)有返回值。 Around:(環(huán)繞通知)編程式控制目標(biāo)對(duì)象的方法調(diào)用。環(huán)繞通知是所有通知類(lèi)型中可操作范圍最大的一種,因?yàn)樗梢灾苯幽玫侥繕?biāo)對(duì)象,以及要執(zhí)行的方法,所以環(huán)繞通知可以任意的在目標(biāo)對(duì)象的方法調(diào)用前后搞事,甚至不調(diào)用目標(biāo)對(duì)象的方法
多個(gè)切面的執(zhí)行順序如何控制?
1、通常使用@Order 注解直接定義切面順序
// 值越小優(yōu)先級(jí)越高
@Order(3)
@Component
@Aspect
public class LoggingAspect implements Ordered {
2、實(shí)現(xiàn)Ordered 接口重寫(xiě) getOrder 方法。
@Component
@Aspect
public class LoggingAspect implements Ordered {
// ....
@Override
public int getOrder() {
// 返回值越小優(yōu)先級(jí)越高
return 1;
}
}
Spring MVC
說(shuō)說(shuō)自己對(duì)于 Spring MVC 了解?
MVC 是模型(Model)、視圖(View)、控制器(Controller)的簡(jiǎn)寫(xiě),其核心思想是通過(guò)將業(yè)務(wù)邏輯、數(shù)據(jù)、顯示分離來(lái)組織代碼。

網(wǎng)上有很多人說(shuō) MVC 不是設(shè)計(jì)模式,只是軟件設(shè)計(jì)規(guī)范,我個(gè)人更傾向于 MVC 同樣是眾多設(shè)計(jì)模式中的一種。**java-design-patterns** 項(xiàng)目中就有關(guān)于 MVC 的相關(guān)介紹。

想要真正理解 Spring MVC,我們先來(lái)看看 Model 1 和 Model 2 這兩個(gè)沒(méi)有 Spring MVC 的時(shí)代。
Model 1 時(shí)代
很多學(xué) Java 后端比較晚的朋友可能并沒(méi)有接觸過(guò) Model 1 時(shí)代下的 JavaWeb 應(yīng)用開(kāi)發(fā)。在 Model1 模式下,整個(gè) Web 應(yīng)用幾乎全部用 JSP 頁(yè)面組成,只用少量的 JavaBean 來(lái)處理數(shù)據(jù)庫(kù)連接、訪問(wèn)等操作。
這個(gè)模式下 JSP 即是控制層(Controller)又是表現(xiàn)層(View)。顯而易見(jiàn),這種模式存在很多問(wèn)題。比如控制邏輯和表現(xiàn)邏輯混雜在一起,導(dǎo)致代碼重用率極低;再比如前端和后端相互依賴(lài),難以進(jìn)行測(cè)試維護(hù)并且開(kāi)發(fā)效率極低。

Model 2 時(shí)代
學(xué)過(guò) Servlet 并做過(guò)相關(guān) Demo 的朋友應(yīng)該了解“Java Bean(Model)+ JSP(View)+Servlet(Controller) ”這種開(kāi)發(fā)模式,這就是早期的 JavaWeb MVC 開(kāi)發(fā)模式。
Model:系統(tǒng)涉及的數(shù)據(jù),也就是 dao 和 bean。 View:展示模型中的數(shù)據(jù),只是用來(lái)展示。 Controller:處理用戶(hù)請(qǐng)求都發(fā)送給 ,返回?cái)?shù)據(jù)給 JSP 并展示給用戶(hù)。

Model2 模式下還存在很多問(wèn)題,Model2 的抽象和封裝程度還遠(yuǎn)遠(yuǎn)不夠,使用 Model2 進(jìn)行開(kāi)發(fā)時(shí)不可避免地會(huì)重復(fù)造輪子,這就大大降低了程序的可維護(hù)性和復(fù)用性。
于是,很多 JavaWeb 開(kāi)發(fā)相關(guān)的 MVC 框架應(yīng)運(yùn)而生比如 Struts2,但是 Struts2 比較笨重。
Spring MVC 時(shí)代
隨著 Spring 輕量級(jí)開(kāi)發(fā)框架的流行,Spring 生態(tài)圈出現(xiàn)了 Spring MVC 框架, Spring MVC 是當(dāng)前最優(yōu)秀的 MVC 框架。相比于 Struts2 , Spring MVC 使用更加簡(jiǎn)單和方便,開(kāi)發(fā)效率更高,并且 Spring MVC 運(yùn)行速度更快。
MVC 是一種設(shè)計(jì)模式,Spring MVC 是一款很優(yōu)秀的 MVC 框架。Spring MVC 可以幫助我們進(jìn)行更簡(jiǎn)潔的 Web 層的開(kāi)發(fā),并且它天生與 Spring 框架集成。Spring MVC 下我們一般把后端項(xiàng)目分為 Service 層(處理業(yè)務(wù))、Dao 層(數(shù)據(jù)庫(kù)操作)、Entity 層(實(shí)體類(lèi))、Controller 層(控制層,返回?cái)?shù)據(jù)給前臺(tái)頁(yè)面)。
Spring MVC 的核心組件有哪些?
記住了下面這些組件,也就記住了 SpringMVC 的工作原理。
DispatcherServlet:核心的中央處理器,負(fù)責(zé)接收請(qǐng)求、分發(fā),并給予客戶(hù)端響應(yīng)。HandlerMapping:處理器映射器,根據(jù) uri 去匹配查找能處理的Handler,并會(huì)將請(qǐng)求涉及到的攔截器和Handler一起封裝。HandlerAdapter:處理器適配器,根據(jù)HandlerMapping找到的Handler,適配執(zhí)行對(duì)應(yīng)的Handler;Handler:請(qǐng)求處理器,處理實(shí)際請(qǐng)求的處理器。ViewResolver:視圖解析器,根據(jù)Handler返回的邏輯視圖 / 視圖,解析并渲染真正的視圖,并傳遞給DispatcherServlet響應(yīng)客戶(hù)端
SpringMVC 工作原理了解嗎?
Spring MVC 原理如下圖所示:
SpringMVC 工作原理的圖解我沒(méi)有自己畫(huà),直接圖省事在網(wǎng)上找了一個(gè)非常清晰直觀的,原出處不明。

流程說(shuō)明(重要):
客戶(hù)端(瀏覽器)發(fā)送請(qǐng)求, DispatcherServlet攔截請(qǐng)求。DispatcherServlet根據(jù)請(qǐng)求信息調(diào)用HandlerMapping。HandlerMapping根據(jù) uri 去匹配查找能處理的Handler(也就是我們平常說(shuō)的Controller控制器) ,并會(huì)將請(qǐng)求涉及到的攔截器和Handler一起封裝。DispatcherServlet調(diào)用HandlerAdapter適配執(zhí)行Handler。Handler完成對(duì)用戶(hù)請(qǐng)求的處理后,會(huì)返回一個(gè)ModelAndView對(duì)象給DispatcherServlet,ModelAndView顧名思義,包含了數(shù)據(jù)模型以及相應(yīng)的視圖的信息。Model是返回的數(shù)據(jù)對(duì)象,View是個(gè)邏輯上的View。ViewResolver會(huì)根據(jù)邏輯View查找實(shí)際的View。DispaterServlet把返回的Model傳給View(視圖渲染)。把 View返回給請(qǐng)求者(瀏覽器)
統(tǒng)一異常處理怎么做?
推薦使用注解的方式統(tǒng)一異常處理,具體會(huì)使用到 @ControllerAdvice + @ExceptionHandler 這兩個(gè)注解 。
@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) {
//......
}
}
這種異常處理方式下,會(huì)給所有或者指定的 Controller 織入異常處理的邏輯(AOP),當(dāng) Controller 中的方法拋出異常的時(shí)候,由被@ExceptionHandler 注解修飾的方法進(jìn)行處理。
ExceptionHandlerMethodResolver 中 getMappedMethod 方法決定了異常具體被哪個(gè)被 @ExceptionHandler 注解修飾的方法處理異常。
@Nullable
private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
List<Class<? extends Throwable>> matches = new ArrayList<>();
//找到可以處理的所有異常信息。mappedMethods 中存放了異常和處理異常的方法的對(duì)應(yīng)關(guān)系
for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
if (mappedException.isAssignableFrom(exceptionType)) {
matches.add(mappedException);
}
}
// 不為空說(shuō)明有方法處理異常
if (!matches.isEmpty()) {
// 按照匹配程度從小到大排序
matches.sort(new ExceptionDepthComparator(exceptionType));
// 返回處理異常的方法
return this.mappedMethods.get(matches.get(0));
}
else {
return null;
}
}
從源代碼看出:getMappedMethod()會(huì)首先找到可以匹配處理異常的所有方法信息,然后對(duì)其進(jìn)行從小到大的排序,最后取最小的那一個(gè)匹配的方法(即匹配度最高的那個(gè))。
Spring 框架中用到了哪些設(shè)計(jì)模式?
關(guān)于下面一些設(shè)計(jì)模式的詳細(xì)介紹,可以看筆主前段時(shí)間的原創(chuàng)文章《面試官:“談?wù)?Spring 中都用到了那些設(shè)計(jì)模式?”。》 。
工廠設(shè)計(jì)模式 : Spring 使用工廠模式通過(guò) BeanFactory、ApplicationContext創(chuàng)建 bean 對(duì)象。代理設(shè)計(jì)模式 : Spring AOP 功能的實(shí)現(xiàn)。 單例設(shè)計(jì)模式 : Spring 中的 Bean 默認(rèn)都是單例的。 模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate等以 Template 結(jié)尾的對(duì)數(shù)據(jù)庫(kù)操作的類(lèi),它們就使用到了模板模式。包裝器設(shè)計(jì)模式 : 我們的項(xiàng)目需要連接多個(gè)數(shù)據(jù)庫(kù),而且不同的客戶(hù)在每次訪問(wèn)中根據(jù)需要會(huì)去訪問(wèn)不同的數(shù)據(jù)庫(kù)。這種模式讓我們可以根據(jù)客戶(hù)的需求能夠動(dòng)態(tài)切換不同的數(shù)據(jù)源。 觀察者模式: Spring 事件驅(qū)動(dòng)模型就是觀察者模式很經(jīng)典的一個(gè)應(yīng)用。 適配器模式 : Spring AOP 的增強(qiáng)或通知(Advice)使用到了適配器模式、spring MVC 中也是用到了適配器模式適配 Controller。......
Spring 事務(wù)
Spring/SpringBoot 模塊下專(zhuān)門(mén)有一篇是講 Spring 事務(wù)的,總結(jié)的非常詳細(xì),通俗易懂。
Spring 管理事務(wù)的方式有幾種?
編程式事務(wù) :在代碼中硬編碼(不推薦使用) : 通過(guò) TransactionTemplate或者TransactionManager手動(dòng)管理事務(wù),實(shí)際應(yīng)用中很少使用,但是對(duì)于你理解 Spring 事務(wù)管理原理有幫助。聲明式事務(wù) :在 XML 配置文件中配置或者直接基于注解(推薦使用) : 實(shí)際是通過(guò) AOP 實(shí)現(xiàn)(基于 @Transactional的全注解方式使用最多)
Spring 事務(wù)中哪幾種事務(wù)傳播行為?
事務(wù)傳播行為是為了解決業(yè)務(wù)層方法之間互相調(diào)用的事務(wù)問(wèn)題。
當(dāng)事務(wù)方法被另一個(gè)事務(wù)方法調(diào)用時(shí),必須指定事務(wù)應(yīng)該如何傳播。例如:方法可能繼續(xù)在現(xiàn)有事務(wù)中運(yùn)行,也可能開(kāi)啟一個(gè)新事務(wù),并在自己的事務(wù)中運(yùn)行。
正確的事務(wù)傳播行為可能的值如下:
1.TransactionDefinition.PROPAGATION_REQUIRED
使用的最多的一個(gè)事務(wù)傳播行為,我們平時(shí)經(jīng)常使用的@Transactional注解默認(rèn)使用就是這個(gè)事務(wù)傳播行為。如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。
2.TransactionDefinition.PROPAGATION_REQUIRES_NEW
創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。也就是說(shuō)不管外部方法是否開(kāi)啟事務(wù),Propagation.REQUIRES_NEW修飾的內(nèi)部方法會(huì)新開(kāi)啟自己的事務(wù),且開(kāi)啟的事務(wù)相互獨(dú)立,互不干擾。
3.TransactionDefinition.PROPAGATION_NESTED
如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來(lái)運(yùn)行;如果當(dāng)前沒(méi)有事務(wù),則該取值等價(jià)于TransactionDefinition.PROPAGATION_REQUIRED。
4.TransactionDefinition.PROPAGATION_MANDATORY
如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則拋出異常。(mandatory:強(qiáng)制性)
這個(gè)使用的很少。
若是錯(cuò)誤的配置以下 3 種事務(wù)傳播行為,事務(wù)將不會(huì)發(fā)生回滾:
TransactionDefinition.PROPAGATION_SUPPORTS: 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。TransactionDefinition.PROPAGATION_NEVER: 以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常。
Spring 事務(wù)中的隔離級(jí)別有哪幾種?
和事務(wù)傳播行為這塊一樣,為了方便使用,Spring 也相應(yīng)地定義了一個(gè)枚舉類(lèi):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;
}
}
下面我依次對(duì)每一種事務(wù)隔離級(jí)別進(jìn)行介紹:
TransactionDefinition.ISOLATION_DEFAULT:使用后端數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別,MySQL 默認(rèn)采用的REPEATABLE_READ隔離級(jí)別 Oracle 默認(rèn)采用的READ_COMMITTED隔離級(jí)別.TransactionDefinition.ISOLATION_READ_UNCOMMITTED:最低的隔離級(jí)別,使用這個(gè)隔離級(jí)別很少,因?yàn)樗试S讀取尚未提交的數(shù)據(jù)變更,可能會(huì)導(dǎo)致臟讀、幻讀或不可重復(fù)讀TransactionDefinition.ISOLATION_READ_COMMITTED: 允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù),可以阻止臟讀,但是幻讀或不可重復(fù)讀仍有可能發(fā)生TransactionDefinition.ISOLATION_REPEATABLE_READ: 對(duì)同一字段的多次讀取結(jié)果都是一致的,除非數(shù)據(jù)是被本身事務(wù)自己所修改,可以阻止臟讀和不可重復(fù)讀,但幻讀仍有可能發(fā)生。TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔離級(jí)別,完全服從 ACID 的隔離級(jí)別。所有的事務(wù)依次逐個(gè)執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說(shuō),該級(jí)別可以防止臟讀、不可重復(fù)讀以及幻讀。但是這將嚴(yán)重影響程序的性能。通常情況下也不會(huì)用到該級(jí)別。
@Transactional(rollbackFor = Exception.class)注解了解嗎?
Exception 分為運(yùn)行時(shí)異常 RuntimeException 和非運(yùn)行時(shí)異常。事務(wù)管理對(duì)于企業(yè)應(yīng)用來(lái)說(shuō)是至關(guān)重要的,即使出現(xiàn)異常情況,它也可以保證數(shù)據(jù)的一致性。
當(dāng) @Transactional 注解作用于類(lèi)上時(shí),該類(lèi)的所有 public 方法將都具有該類(lèi)型的事務(wù)屬性,同時(shí),我們也可以在方法級(jí)別使用該標(biāo)注來(lái)覆蓋類(lèi)級(jí)別的定義。如果類(lèi)或者方法加了這個(gè)注解,那么這個(gè)類(lèi)里面的方法拋出異常,就會(huì)回滾,數(shù)據(jù)庫(kù)里面的數(shù)據(jù)也會(huì)回滾。
在 @Transactional 注解中如果不配置rollbackFor屬性,那么事務(wù)只會(huì)在遇到RuntimeException的時(shí)候才會(huì)回滾,加上 rollbackFor=Exception.class,可以讓事務(wù)在遇到非運(yùn)行時(shí)異常時(shí)也回滾。
Spring Data JPA
JPA 重要的是實(shí)戰(zhàn),這里僅對(duì)小部分知識(shí)點(diǎn)進(jìn)行總結(jié)。
如何使用 JPA 在數(shù)據(jù)庫(kù)中非持久化一個(gè)字段?
假如我們有下面一個(gè)類(lèi):
@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 這個(gè)字段不被持久化,也就是不被數(shù)據(jù)庫(kù)存儲(chǔ)怎么辦?我們可以采用下面幾種方法:
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
一般使用后面兩種方式比較多,我個(gè)人使用注解的方式比較多。
JPA 的審計(jì)功能是做什么的?有什么用?
審計(jì)功能主要是幫助我們記錄數(shù)據(jù)庫(kù)操作的具體行為比如某條記錄是誰(shuí)創(chuàng)建的、什么時(shí)間創(chuàng)建的、最后修改人是誰(shuí)、最后修改時(shí)間是什么時(shí)候。
@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: 表示該字段為創(chuàng)建時(shí)間字段,在這個(gè)實(shí)體被 insert 的時(shí)候,會(huì)設(shè)置值@CreatedBy:表示該字段為創(chuàng)建人,在這個(gè)實(shí)體被 insert 的時(shí)候,會(huì)設(shè)置值@LastModifiedDate、@LastModifiedBy同理。
實(shí)體之間的關(guān)聯(lián)關(guān)系注解有哪些?
@OneToOne: 一對(duì)一。@ManyToMany:多對(duì)多。@OneToMany: 一對(duì)多。@ManyToOne:多對(duì)一。
利用 @ManyToOne 和 @OneToMany 也可以表達(dá)多對(duì)多的關(guān)聯(lián)關(guān)系。
Spring Security
Spring Security 重要的是實(shí)戰(zhàn),這里僅對(duì)小部分知識(shí)點(diǎn)進(jìn)行總結(jié)。
有哪些控制請(qǐng)求訪問(wèn)權(quán)限的方法?

permitAll():無(wú)條件允許任何形式訪問(wèn),不管你登錄還是沒(méi)有登錄。anonymous():允許匿名訪問(wèn),也就是沒(méi)有登錄才可以訪問(wèn)。denyAll():無(wú)條件決絕任何形式的訪問(wèn)。authenticated():只允許已認(rèn)證的用戶(hù)訪問(wèn)。fullyAuthenticated():只允許已經(jīng)登錄或者通過(guò) remember-me 登錄的用戶(hù)訪問(wèn)。hasRole(String): 只允許指定的角色訪問(wèn)。hasAnyRole(String): 指定一個(gè)或者多個(gè)角色,滿(mǎn)足其一的用戶(hù)即可訪問(wèn)。hasAuthority(String):只允許具有指定權(quán)限的用戶(hù)訪問(wèn)hasAnyAuthority(String):指定一個(gè)或者多個(gè)權(quán)限,滿(mǎn)足其一的用戶(hù)即可訪問(wèn)。hasIpAddress(String): 只允許指定 ip 的用戶(hù)訪問(wèn)。
hasRole 和 hasAuthority 有區(qū)別嗎?
可以看看松哥的這篇文章:Spring Security 中的 hasRole 和 hasAuthority 有區(qū)別嗎?,介紹的比較詳細(xì)。
如何對(duì)密碼進(jìn)行加密?
如果我們需要保存密碼這類(lèi)敏感數(shù)據(jù)到數(shù)據(jù)庫(kù)的話(huà),需要先加密再保存。
Spring Security 提供了多種加密算法的實(shí)現(xiàn),開(kāi)箱即用,非常方便。這些加密算法實(shí)現(xiàn)類(lèi)的父類(lèi)是 PasswordEncoder ,如果你想要自己實(shí)現(xiàn)一個(gè)加密算法的話(huà),也需要繼承 PasswordEncoder。
PasswordEncoder 接口一共也就 3 個(gè)必須實(shí)現(xiàn)的方法。
public interface PasswordEncoder {
// 加密也就是對(duì)原始密碼進(jìn)行編碼
String encode(CharSequence var1);
// 比對(duì)原始密碼和數(shù)據(jù)庫(kù)中保存的密碼
boolean matches(CharSequence var1, String var2);
// 判斷加密密碼是否需要再次進(jìn)行加密,默認(rèn)返回 false
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}

官方推薦使用基于 bcrypt 強(qiáng)哈希函數(shù)的加密算法實(shí)現(xiàn)類(lèi)。
如何優(yōu)雅更換系統(tǒng)使用的加密算法?
如果我們?cè)陂_(kāi)發(fā)過(guò)程中,突然發(fā)現(xiàn)現(xiàn)有的加密算法無(wú)法滿(mǎn)足我們的需求,需要更換成另外一個(gè)加密算法,這個(gè)時(shí)候應(yīng)該怎么辦呢?
推薦的做法是通過(guò) DelegatingPasswordEncoder 兼容多種不同的密碼加密方案,以適應(yīng)不同的業(yè)務(wù)需求。
從名字也能看出來(lái),DelegatingPasswordEncoder 其實(shí)就是一個(gè)代理類(lèi),并非是一種全新的加密算法,它做的事情就是代理上面提到的加密算法實(shí)現(xiàn)類(lèi)。在 Spring Security 5.0 之后,默認(rèn)就是基于 DelegatingPasswordEncoder 進(jìn)行密碼加密的。
參考
《Spring 技術(shù)內(nèi)幕》 《從零開(kāi)始深入學(xué)習(xí) Spring》:https://juejin.cn/book/6857911863016390663 http://www.cnblogs.com/wmyskxz/p/8820371.html https://www.journaldev.com/2696/spring-interview-questions-and-answers https://www.edureka.co/blog/interview-questions/spring-interview-questions/ https://www.cnblogs.com/clwydjgs/p/9317849.html https://howtodoinjava.com/interview-questions/top-spring-interview-questions-with-answers/ http://www.tomaszezula.com/2014/02/09/spring-series-part-5-component-vs-bean/ https://stackoverflow.com/questions/34172888/difference-between-bean-and-autowired
··················END················
近期文章精選 :
這個(gè) SpringBoot+ Vue 開(kāi)源博客系統(tǒng)太酷炫了! 美團(tuán)二面:Redis 5 種基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)? IDEA 版防沉迷插件,有點(diǎn)意思! 該死的單元測(cè)試,寫(xiě)起來(lái)是真的折磨! 外包一年,裸辭沖一波中廠! 程序員深漂 6 年,回西安工作了 3 年 Java 工作經(jīng)驗(yàn)裸辭 上岸美團(tuán)、華為、字節(jié)!
??《Java 面試指北》來(lái)啦!這是一份教你如何更高效地準(zhǔn)備面試的小冊(cè),涵蓋常見(jiàn)八股文(系統(tǒng)設(shè)計(jì)、常見(jiàn)框架、分布式、高并發(fā) ......)、優(yōu)質(zhì)面經(jīng)等內(nèi)容。
??如果本文對(duì)你有幫助的話(huà),歡迎 點(diǎn)贊&在看&分享 ,這對(duì)我繼續(xù)分享&創(chuàng)作優(yōu)質(zhì)文章非常重要。非常感謝!
