Spring常見面試題
Spring框架中的單例bean是線程安全的嗎
????????不是線程安全的 Spring框架中有一個@Scope注解,默認的值就是singleton,單例的。 因為一般在spring的bean的中都是注入無狀態的對象,沒有線程安全問題,如果在bean中定義了可修改的成員變量,是要考慮線程安全問題的,可以使用多例或者加鎖來解決
Spring框架中的bean是單例的嗎
- singleton : bean在每個Spring IOC容器中只有一個實例。
- prototype:一個bean的定義可以有多個實例。
什么是AOP
????????AOP稱為面向切面編程,用于將那些與業務無關,但卻對多個對象產生影響的公共行為和邏輯,抽取并封裝為一個可重用的模塊,這個模塊被命名為“切面”(Aspect),減少系統中的重復代碼,降低了模塊間的耦合度,同時提高了系統的可維護性。
常見的AOP使用場景:
- 記錄操作日志
- 緩存處理
- Spring中內置的事務處理
記錄操作日志思路
????????獲取請求的用戶名、請求方式、訪問地址、模塊名稱、登錄ip、操作時間,記錄到數據庫的日志表中。核心是:使用aop中的環繞通知+切點表達式(找到要記錄日志的方法),通過環繞通知的參數獲取請求方法的參數(類、方法、注解、請求方式等),獲取到這些參數以后,保存到數據庫
Spring中的事務是如何實現的
Spring支持編程式事務管理和聲明式事務管理兩種方式。
- 編程式事務控制:需使用TransactionTemplate來進行實現,對業務代碼有侵入性,項目中很少使用
- 聲明式事務管理:聲明式事務管理建立在AOP之上的。其本質是通過AOP功能,對方法前后進行攔截,將事務處理的功能編織到攔截的方法中,也就是在目標方法開始之前加入一個事務,在執行完目標方法之后根據執行情況提交或者回滾事務。
Spring中事務失效的場景有哪些
情況一:異常捕獲處理
原因:事務通知只有捉到了目標拋出的異常,才能進行后續的回滾處理,如果目標自己處理掉異常,事務通知無法知悉
解決:在catch塊添加throw new RuntimeException(e)拋出
情況二:拋出檢查異常
原因:Spring 默認只會回滾非檢查異常
解決:配置rollbackFor屬性@Transactional(rollbackFor=Exception.class)
情況三:非public方法導致的事務失效
原因:Spring 為方法創建代理、添加事務通知、前提條件都是該方法是 public 的。
解決:改為 public?
Spring的bean的生命周期
BeanDefinition
????????Spring容器在進行實例化時,會將xml配置的<bean>的信息封裝成一個BeanDefinition對象,Spring根據BeanDefinition來創建Bean對象,里面有很多的屬性用來描述Bean。
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" lazy-init="true"/>
<bean id="userService" class="com.itheima.service.UserServiceImpl" scope="singleton"><property name="userDao" ref="userDao"></property>
</bean>
- beanClassName:bean 的類名
- initMethodName:初始化方法名稱
- properryValues:bean 的屬性值
- scope:作用域
- lazyInit:延遲初始化
- 通過BeanDefinition獲取bean的定義信息
- 調用構造函數實例化bean
- bean的依賴注入
- 處理Aware接口(BeanNameAware、BeanFactoryAware、ApplicationContextAware)
- Bean的后置處理器BeanPostProcessor-前置
- 初始化方法(InitializingBean、init-method)
- Bean的后置處理器BeanPostProcessor-后置
- 銷毀bean
Spring中的循環引用
什么是Spring的循環依賴?
????????循環依賴發生在兩個或兩個以上的bean互相持有對方,形成閉環。在創建A對象的同時需要使用的B對象,在創建B對象的同時需要使用到A對象。
三級緩存解決循環依賴
//單實例對象注冊器
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;//一級緩存private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);//三級緩存private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);//二級緩存private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
}
一級緩存作用:限制bean在beanFactory中只存一份,即實現singleton scope,解決不了循環依賴
如果要想打破循環依賴, 就需要一個中間人的參與, 這個中間人就是二級緩存。
從二級緩存中獲取的是指向A對象的地址,即使是半成品對于B來講只要拿到A對象的地址就能創建
- 循環依賴:循環依賴其實就是循環引用,也就是兩個或兩個以上的bean互相持有對方,最終形成閉環。比如A依賴于B,B依賴于A
- 循環依賴在spring中是允許存在,spring框架依據三級緩存已經解決了大部分的循環依賴
- 一級緩存:單例池,緩存已經經歷了完整的生命周期,已經初始化完成的bean對象
- 二級緩存:緩存早期的bean對象(生命周期還沒走完)
- 三級緩存:緩存的是ObjectFactory,表示對象工廠,用來創建某個對象的?
緩存名稱 | 源碼名稱 | 作用 |
一級緩存 | singletonObjects | 單例池,緩存已經經歷了完整的生命周期,已經初始化完成的 bean對象 |
二級緩存 | earlySingletonObjects | 緩存早期的bean對象(生命周期還沒走完) |
三級緩存 | singletonFactories | 緩存的是ObjectFactory,表示對象工廠,用來創建某個對象的 |
構造方法出現了循環依賴怎么解決?
A依賴于B,B依賴于A,注入的方式是構造函數
原因:由于bean的生命周期中構造函數是第一個執行的,spring框架并不能解決構造函數的的依賴注入
解決方案:使用@Lazy進行懶加載,什么時候需要對象再進行bean對象的創建
public A(@Lazy B b){System.out.println("A的構造方法執行了...");this.b = b ;
}
SpringMVC的執行流程
- 視圖階段(老舊JSP等)
- 用戶發送出請求到前端控制器DispatcherServlet
- DispatcherServlet收到請求調用HandlerMapping(處理器映射器)
- HandlerMapping找到具體的處理器,生成處理器對象及處理器攔截器(如果有),再一起返回給DispatcherServlet。
- DispatcherServlet調用HandlerAdapter(處理器適配器)
- HandlerAdapter經過適配調用具體的處理器(Handler/Controller)
- Controller執行完成返回ModelAndView對象
- HandlerAdapter將Controller執行結果ModelAndView返回給DispatcherServlet
- DispatcherServlet將ModelAndView傳給ViewReslover(視圖解析器)
- ViewReslover解析后返回具體View(視圖)
- DispatcherServlet根據View進行渲染視圖(即將模型數據填充至視圖中)
- DispatcherServlet響應用戶?
- 前后端分離階段(接口開發,異步)
- 用戶發送出請求到前端控制器DispatcherServlet
- DispatcherServlet收到請求調用HandlerMapping(處理器映射器)
- HandlerMapping找到具體的處理器,生成處理器對象及處理器攔截器(如果有),再一起返回給DispatcherServlet。
- DispatcherServlet調用HandlerAdapter(處理器適配器)
- HandlerAdapter經過適配調用具體的處理器(Handler/Controller)
- 方法上添加了@ResponseBody
- ?通過HttpMessageConverter來返回結果轉換為JSON并響應
Springboot自動配置原理
- @SpringBootConfiguration:該注解與 @Configuration 注解作用相同,用來聲明當前也是一個配置類。
- @ComponentScan:組件掃描,默認掃描當前引導類所在包及其子包。
- @EnableAutoConfiguration:SpringBoot實現自動化配置的核心注解。
繼續打開@EnableAutoConfiguration
AutoConfigurationImportSelector實現方式為SpringFactories機制
getAutoConfigurationEntry為Spring自動配置入口
SpringFactories文件路徑
總結:Spring Boot的自動配置原理基于
@SpringBootApplication
注解,它封裝了@SpringBootConfiguration
、@EnableAutoConfiguration
和@ComponentScan
。@EnableAutoConfiguration
是核心,它通過@Import
導入配置選擇器,讀取META-INF/spring.factories
文件中的類名,根據條件注解決定是否將配置類中的Bean導入到Spring容器中。
Springboot配置原理視頻講解:B站目前講的最透徹的SpringBoot自動配置,大廠面試必備知識點#安員外很有碼_嗶哩嗶哩_bilibili
Spring 的常見注解有哪些??
注解 | 說明 |
@Component、@Controller、@Service、@Repository | 使用在類上用于實例化Bean |
@Autowired | 使用在字段上用于根據類型依賴注入 |
@Qualifier | 結合@Autowired一起使用用于根據名稱進行依賴注入 |
@Scope | 標注Bean的作用范圍 |
@Configuration | 指定當前類是一個?Spring?配置類,當創建容器時會從該類上加載注解 |
@ComponentScan | 用于指定?Spring???在初始化容器時要掃描的包 |
@Bean | 使用在方法上,標注將該方法的返回值存儲到Spring容器中 |
@Import | 使用@Import導入的類會被Spring加載到IOC容器中 |
@Aspect、@Before、@After、@Around、@Pointcut | 用于切面編程(AOP) |
Springboot常見注解
注解 | 說明 |
@SpringBootConfiguration | 組合了-?@Configuration注解,實現配置文件的功能 |
@EnableAutoConfiguration | 打開自動配置的功能,也可以關閉某個自動配置的選 |
@ComponentScan | Spring組件掃描 |
SpringMVC
注解 | 說明 |
@RequestMapping | 用于映射請求路徑,可以定義在類上和方法上。用于類上,則表示類中的所有的方法都是以該地址作為父路徑 |
@RequestBody | 注解實現接收http請求的json數據,將json轉換為java對象 |
@RequestParam | 指定請求參數的名稱 |
@PathViriable | 從請求路徑下中獲取請求參數(/user/{id}),傳遞給方法的形式參數 |
@ResponseBody | 注解實現將controller方法返回對象轉化為json對象響應給客戶端 |
@RequestHeader | 獲取指定的請求頭數據 |
@RestController | @Controller?+?@ResponseBody |