網站首頁 編程語言 正文
正文
先來看看入門使用Spring的代碼:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();
這三行代碼底層都做了什么,比如:
- 第一行代碼,會構造一個ClassPathXmlApplicationContext對象,ClassPathXmlApplicationContext該如何理解,調用該構造方法除開會實例化得到一個對象,還會做哪些事情?
- 第二行代碼,會調用ClassPathXmlApplicationContext的getBean方法,會得到一個UserService對象,getBean()是如何實現的?返回的UserService對象和我們自 己直接new的UserService對象有區別嗎?
- 第三行代碼,就是簡單的調用UserService的test()方法,不難理解。
但是用ClassPathXmlApplicationContext其實已經過時了,在新版的Spring MVC和Spring Boot的底層主要用的都是AnnotationConfigApplicationContext,比如:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();
可以看到AnnotationConfigApplicationContext的用法和ClassPathXmlApplicationContext是非常類似的,只不過需要傳入的是一個class,而不是
一個xml文件。而AppConfig.class和spring.xml一樣,表示Spring的配置,比如可以指定掃描路徑,可以直接定義Bean,比如:spring.xml中的內容為:
<context:component-scan base-package="com.service"/>
<bean id="userService" class="com.service.UserService"/>
AppConfig中的內容為:
@ComponentScan("com.service")
public class AppConfig {
@Bean
public UserService userService(){
return new UserService();
}
}
所以spring.xml和AppConfig.class本質上是一樣的。
目前,我們基本很少直接使用上面這種方式來用Spring,而是使用Spring MVC,或者Spring Boot,但是它們都是基于上面這種方式的,都需要在內部去創建一個 ApplicationContext的,只不過:
- Spring MVC創建的是XmlWebApplicationContext 和 ClassPathXmlApplicationContext類似,都是基于XML配置的
- Spring Boot創建的是AnnotationConfigApplicationContext
- AnnotationConfigApplicationContext和ClassPathXmlApplicationContext大部分底層都是共同的。
Spring中是如何創建一個對象?
其實不管是AnnotationConfigApplicationContext還是ClassPathXmlApplicationContext,目前,我們都可以簡單的將它們理解為就是用來創建Java對象的,比如調用getBean()就會去創建對象(此處不嚴謹,getBean可能也不會去創建對象,后續課程詳解)。在Java語言中,肯定是根據某個類來創建一個對象的。我們在看一下實例代碼:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();
當我們調用context.getBean(“userService”)時,就會去創建一個對象,但是getBean方法內部怎么知道"userService"對應的是UserService類呢?所以,我們就可以分析出來,在調用AnnotationConfigApplicationContext的構造方法時,也就是第一行代碼,會去做一些事情:
- 解析AppConfig.class,得到掃描路徑
- 遍歷掃描路徑下的所有Java類,如果發現某個類上存在@Component、@Service等注解,那么Spring就把這個類記錄下來,存在一個Map中,比如Map<String, Class>。(實際上,Spring源碼中確實存在類似的這么一個Map,叫做BeanDefinitionMap)
- Spring會根據某個規則生成當前類對應的beanName,作為key存入Map,當前類作為value這樣,但調用context.getBean(“userService”)時,就可以根據"userService"找到UserService類,從而就可以去創建對象了。
Bean的創建過程
那么Spring到底是如何來創建一個Bean的呢,這個就是Bean創建的生命周期,大致過程如下:
- 利用該類的構造方法來實例化得到一個對象(但是如何一個類中有多個構造方法,Spring則會進行選擇,這個叫做推斷構造方法)
- 得到一個對象后,Spring會判斷該對象中是否存在被@Autowired注解了的屬性,把這些屬性找出來并由Spring進行賦值(依賴注入)
- 依賴注入后,Spring會判斷該對象是否實現了BeanNameAware接口、BeanClassLoaderAware接口、BeanFactoryAware接口,如果實現了,就表示當前對象必須實現該接口中所定義的setBeanName()、setBeanClassLoader()、setBeanFactory()方法,那Spring就會調用這些方法并傳入相應的參數(Aware回調)
- Aware回調后,Spring會判斷該對象中是否存在某個方法被@PostConstruct注解了,如果存在,Spring會調用當前對象的此方法(初始化前)
- 緊接著,Spring會判斷該對象是否實現了InitializingBean接口,如果實現了,就表示當前對象必須實現該接口中的afterPropertiesSet()方法,那Spring就會調用當前對象中的afterPropertiesSet()方法(初始化)
- 最后,Spring會判斷當前對象需不需要進行AOP,如果不需要那么Bean就創建完了,如果需要進行AOP,則會進行動態代理并生成一個代理對象做為Bean(初始化后)
通過最后一步,我們可以發現,當Spring根據UserService類來創建一個Bean時:
1. 如果不用進行AOP,那么Bean就是UserService類的構造方法所得到的對象。
2. 如果需要進行AOP,那么Bean就是UserService的代理類所實例化得到的對象,而不是UserService本身所得到的對象。
Bean對象創建出來后:
3. 如果當前Bean是單例Bean,那么會把該Bean對象存入一個Map<String,Object>,Map的key為beanName,value為Bean對象。這樣下次getBean時就可以直接從Map中拿到對應的Bean對象了。(實際上,在Spring源碼中,這個Map就是單例池)
4. 如果當前Bean是原型Bean,那么后續沒有其他動作,不會存入一個Map,下次getBean時會再次執行上述創建過程,得到一個新的Bean對象。
推斷構造方法
Spring在基于某個類生成Bean的過程中,需要利用該類的構造方法來實例化得到一個對象,但是如果一個類存在多個構造方法,Spring會使用哪個呢? Spring的判斷邏輯如下:
- 如果一個類只存在一個構造方法,不管該構造方法是無參構造方法,還是有參構造方法,Spring都會用這個構造方法
- 如果一個類存在多個構造方法
a. 這些構造方法中,存在一個無參的構造方法,那么Spring就會用這個無參的構造方法
b. 這些構造方法中,不存在一個無參的構造方法,那么Spring就會報錯
Spring的設計思想是這樣的:
- 如果一個類只有一個構造方法,那么沒得選擇,只能用這個構造方法
- 如果一個類存在多個構造方法,Spring不知道如何選擇,就會看是否有無參的構造方法,因為無參構造方法本身表示了一種默認的意義3. 不過如果某個構造方法上加了@Autowired注解,那就表示程序員告訴Spring就用這個加了注解的方法,那Spring就會用這個加了@Autowired注解構造方法了需要重視的是,如果Spring選擇了一個有參的構造方法,Spring在調用這個有參構造方法時,需要傳入參數,那這個參數是怎么來的呢?Spring會根據入參的類型和入參的名字去Spring中找Bean對象(以單例Bean為例,Spring會從單例池那個Map中去找):
- 先根據入參類型找,如果只找到一個,那就直接用來作為入參
- 如果根據類型找到多個,則再根據入參名字來確定唯一一個
- 最終如果沒有找到,則會報錯,無法創建當前Bean對象確定用哪個構造方法,確定入參的Bean對象,這個過程就叫做推斷構造方法。
AOP大致流程
AOP就是進行動態代理,在創建一個Bean的過程中,Spring在最后一步會去判斷當前正在創建的這個Bean是不是需要進行AOP,如果需要則會進行動態代理。
如何判斷當前Bean對象需不需要進行AOP:
- 找出所有的切面Bean
- 遍歷切面中的每個方法,看是否寫了@Before、@After等注解
- 如果寫了,則判斷所對應的Pointcut是否和當前Bean對象的類是否匹配
- 如果匹配則表示當前Bean對象有匹配的的Pointcut,表示需要進行AOP利用cglib進行AOP的大致流程:
- 生成代理類UserServiceProxy,代理類繼承UserService
- 代理類中重寫了父類的方法,比如UserService中的test()方法
- 代理類中還會有一個target屬性,該屬性的值為被代理對象(也就是通過UserService類推斷構造方法實例化出來的對象,進行了依賴注入、初始化等步驟的對象)
- 代理類中的test()方法被執行時的邏輯如下:
a. 執行切面邏輯(@Before)
b. 調用target.test()
當我們從Spring容器得到UserService的Bean對象時,拿到的就是UserServiceProxy所生成的對象,也就是代理對象。
UserService代理對象.test()—>執行切面邏輯—>target.test(),注意target對象不是代理對象,而是被代理對象。
Spring事務
當我們在某個方法上加了@Transactional注解后,就表示該方法在調用時會開啟Spring事務,而這個方法所在的類所對應的Bean對象會是該類的代理對象。
Spring事務的代理對象執行某個方法時的步驟:
- 判斷當前執行的方法是否存在@Transactional注解
- 如果存在,則利用事務管理器(TransactionMananger)新建一個數據庫連接
- 修改數據庫連接的autocommit為false
- 執行target.test(),執行程序員所寫的業務邏輯代碼,也就是執行sql
- 執行完了之后如果沒有出現異常,則提交,否則回滾
Spring事務是否會失效的判斷標準:某個加了@Transactional注解的方法被調用時,要判斷到底是不是直接被代理對象調用的,如果是則事務會生效,如果不是則失效。
原文鏈接:https://blog.csdn.net/yixuan_love/article/details/125919073
- 上一篇:二分搜索防止整形溢出
- 下一篇:css媒體查詢
相關推薦
- 2022-12-22 Go語言編程通過dwarf獲取內聯函數_Golang
- 2022-09-10 python中的隨機數種子seed()用法說明_python
- 2022-06-01 C#中內聯函數的用法介紹_C#教程
- 2022-10-10 C++實現特殊矩陣的壓縮存儲算法_C 語言
- 2023-12-21 npm install 報錯(npm ERR! errno: -4048, npm ERR! c
- 2023-12-16 IDEA中調用方法時,同步顯示方法的注釋信息
- 2022-08-14 C++學習之算術運算符使用詳解_C 語言
- 2022-10-24 golang程序進度條實現示例詳解_Golang
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支