我們先來說一說Spring,從總體上Spring就是一個基礎框架,同時Spring給我們提供了一個Bean容器,用來裝載和管理具體的Bean對象,你像我們之前創建對象的時候就是通過new關鍵字來實現的,但是現在我們只需要告訴容器有哪些對象,就會幫我們去創建好并且管理他的生命周期;然后呢,Spring又是Spring Boot,Spring Cloud等框架的基石,在原有Spring上去做了一些擴展和開發的;
Bean生命周期:
定義Bean:通過<Bean>標簽或者(@Bean,@Component)注解來進行定義Bean,這一步Spring并不會去創建Bean,只是將Bean的描述信息存儲起來,等待后續的實例化和初始化
注冊Bean:將定義信息會注冊到BeanFactoey或者ApplicationContext中
實例化Bean(createBeanInstance):根據注解和配置文件實例化Bean,在實例化之前有一個方法postProcessMergedBeanDefinition()對Bean進行一些額外的配置或者修改,主要是對元數據進行一些修改
DI屬性注入:Spring將類通過構造器,setter方法或者接口注入到Bean實例當中
其中過程中可能會插入一些自定義的邏輯(
postProcessAfterInstantiation
)和(postProcessProperties
)一些Bean屬性的修改和校驗操作初始化之前調用一些Aware接口,在初始化之前去獲取Bean的名稱、Beanfactory、ApplicationContext方法中的一些前置操作,比如代理包裝和AOP切面等
初始化Bean(initializeBean):調用initializeBean的afterPropertiesSet()方法或者通過init-method屬性指定的初始化方法
初始化之后,通過一些BeanPostProcessor后置處理器進行一些處理(勾子和后置處理器)
調用Bean:初始化完成,就可以被容器中的Bean使用
銷毀Bean:容器關閉時,Spring有三種銷毀方法:通過@preDestory方法;實現DisposableBean接口的destory()方法;或者可以自定義銷毀方法
DI:依賴注入
然后又可以提出兩個核心概念IOC和AOP:
IOC(控制反轉):是一種控制反轉的思想,通過依賴注入實現的。IOC讓對象的創建和管理都交給容器來實現,而不是對象本身
核心思想:控制其實就是控制對象的創建,ICO容器通過配置文件來創建對象,在對象的生命周期中,在不同時期通過不同配置來進行對象的創建和改造;反轉其實就是關于我們創建對象和注入依賴的過程,本來是由我們程序員在代碼中指定的,而反轉之后,這個動作就交給ICO容器觸發,由主動獲取變成被動得到,在創建對象A的時候,發現依賴對象B,IOC容器就會根據配置文件,創建B,將B注入A中
依賴注入:通過構造器注入、setter注入或者接口注入,將對象所需要的依賴傳遞給它,而不是讓對象自行創建依賴
優點:1.降低耦合度:減少代碼之間的直接依賴,代碼更容易維護和擴展。 2.提高可測試性:更容易替換依賴進行單元測試,測試更方便。 3.增強靈活性:依賴關系可以通過配置文件或注解更改,代碼更靈活。 4.促進接口編程:有助于編寫更靈活的代碼,遵循面向接口編程的原則。 5.簡化管理:容器管理對象的創建和生命周期,開發更高效。 6.組件重用:組件可以更方便地組合使用,減少重復代碼。
AOP(面向切面編程):是一種編程范式,通過橫切關注點將與業務邏輯無關的分離出來,允許開發者通過“切面”將通用功能模塊化,將其應用到程序中的多個地方,避免代碼重復。(例如日志記錄,控制權限,安全檢查,事務管理等)
核心思想:將與業務邏輯無關的橫切關注點抽取出來,通過動態代理的方式應用到業務方法,而不是將代碼直接寫入業務邏輯
基本概念:
切面:橫切關注點的模塊化,比如日志、事務等。可以包含多個通知;
連接點:程序執行中的某個點或者位置,例如方法調用,異常處理、字段訪問等;
通知:定義在連接點處執行的動作,可以是一些方法前后,方法拋出異常時的操作
切入點:定義在哪些連接點處的應用通知
動態代理:
JDK動態代理:通過代理接口來創建一個代理對象,代理對象實現目標類所實現的接口。依賴Java反射機制,由Proxy類和InvocationHandler接口來實現,代理類在運行時,調用其中的invoke()方法
優點:簡單高效,代理類是接口的實現類,不需要第三方庫,Java自帶支持
缺點:只能為實現了接口的類生成代理,如果目標沒事實現接口,就無法使用JDK動態代理
public class test1 {public static void main(String[] args) {Car car = new CarImpl();//生成代理對象,在調用方法時會轉發到invoke方法Car proxy = (Car) Proxy.newProxyInstance(car.getClass().getClassLoader(),car.getClass().getInterfaces(),new CarInvocationHandler(car));//這里drive方法會觸發invoke方法proxy.drive();} } interface Car {void drive(); } class CarImpl implements Car {@Overridepublic void drive() {System.out.println("Car is driving...");} }class CarInvocationHandler implements InvocationHandler {//在創建對象的時候,接受一個target代理目標對象private Object target;public CarInvocationHandler(Object target) {this.target = target;}//invoke方法時InvocationHander接口中必須實現的方法,所有通過代理對象調用的方法都會進入這里@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method " + method.getName());Object result = method.invoke(target, args);System.out.println("After method " + method.getName());return result;} }
CGLIB動態代理:通過生成目標類的子類,并重寫目標類中的方法來實現代理,代理類會覆蓋目標類中的方法,并在調用方法前后進行邏輯加強
優點:能夠處理沒有實現接口的類,這是相比較JDK動態代理的優勢,可以適用于所有類
缺點:生成目標子類,如果在大量創建代理對象的話,開銷可能會很大,性能可能也會比較低,不能代理final類或者final方法,因為final不能被繼承
class Car {public void drive() {System.out.println("Car is driving...");} }class CarInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before method " + method.getName());Object result = proxy.invokeSuper(obj, args); // 調用父類方法System.out.println("After method " + method.getName());return result;} }public class test2 {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Car.class);enhancer.setCallback(new CarInterceptor());Car carProxy = (Car) enhancer.create();carProxy.drive();} }
?
**循環依賴問題**:就是指兩個或者多個模塊,類,組件之間相互依賴,相乘閉環;簡單舉一個例子,當A實例化完成以后,要去進行DI屬性注入,去getBean(B),但是B還沒有創建,那我就轉去實例化B,同樣,在B中DI屬性注入的時候也拿不到A,導致了循環依賴的發生;
- **解決方法**(自定):一個簡單的解決方案,在Bean對象實例化完成后,就放入到一個緩存容器中,當其他Bean對象需要獲取到他時可以去緩存中調用,利用緩存去提前暴露對象
- **解決方法**:其實無論用什么方法關鍵都是得提前暴露未創建完畢的Bean
? ??? ?在Spring中主要使用三級緩存來解決緩存依賴:
? - 一級緩存:用于存儲完全初始化完成的單例Bean
- 二級緩存:存儲沒有完全初始換,但是已經實例化的Bean,用于提前暴露對象,避免出現循環依賴問題
- 三級緩存:儲存對象工廠,當需要時,可以通過工廠創建早期的Bean