Day121 深入理解Spring與SpringBoot

Spring基礎

Spring是一個 輕量級 的 控制反轉(IoC)面向切面(AOP) 的容器框架,意在解決Java企業應用開發的複雜性。

IOC

IOC意在降低程序的耦合度,將對象創建和對象之間調用關係交給IOC容器管理,參與開發的每一成員只要實現自己的類就可以了,不需要依賴其他類。

原理

  • IOC容器就是一個管理對象的工廠

  • IOC原理:工廠模式 + 反射 +(xml解析)

  • 爲什麼:爲了儘可能的降低程序的耦合度

    • new方法:原始方法用new來創建調用對象耦合度太高,一旦調用的對象路徑方法發生變化,那麼調用者就也得跟着改,並且調用的地方會非常多,修改起來很麻煩。
    • 工廠模式:所以可以把new對象的過程交給工廠,哪裏需要調用對象直接往工廠裏拿就可以了,有變動只需要修改工廠類就可以了。
    • IOC:在xml中配置對象信息,需要修改不用改程序,只需要改配置文件就可以;在工廠類中讀取xml的配置信息,用反射機制來創建對象,做到不用類名就可以實例化對象,進一步降低了耦合度。
// IOC雛形
class BeanFactory {
	public static UserDao getDao(){
		String classValue = class屬性值; //xml解析
		Class clazz = Class.forName(classValue); //通過反射創建對象
		return (UserDao)clazz.newInstance();
	}
}

應用

  • 怎麼做
  • Spring中的工廠類:類名爲BeanFactory,在程序中通常BeanFactory的子類ApplicationContext。Spring相當於一個大的工廠類,在其配置文件中通過元素配置用於創建實例對象的類名和實例對象的屬性。
  • BeanFactory:加載配置文件時不會加載對象,獲取對象使用纔會去創建對象。
  • ApplicationContext:加載配置文件時就會把配置文件中的對象進行創建,在Web項目中把這些耗時的操作先完成,可以有效提升客戶端訪問的速度。
  • 常用實現類:ClassPathXmlApplicationContext,在classpath下找文件名
public class Client {
	public static void main(String[] args) {
		//1.使用ApplicationContext接口,獲取spring容器
		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
		//2.根據bean的id獲取對象
		IAccountService aService = (IAccountService) ac.getBean("accountService");
	}
}
  • IOC操作Bean來實現【對象的創建】與【屬性的注入】,有【基於xml】與【基於註解】兩種實現方式。
    • 基於xml創建對象id屬性是獲取對象的唯一標識,class屬性代表全限定類名,用於反射創建對象。默認情況下調用無參構造函數。
<bean id="user" class="com.wangc.spring5.User"></bean>
  • 基於註解創建對象:@Component,@Service,@Controller,@Repository
//<bean id="userService" class=".."/>
@Component(value = "userService") 
public class UserService {
}

注入方式

DI:依賴注入,主要指注入屬性。它是spring框架核心ioc的具體實現。在當前類需要用到其他類對象,有spring爲我們提供,我們只需要在配置文件中說明依賴關係的維護,就稱之爲依賴注入。

  1. 使用set方法進行注入:最常用,創建對象時沒有明確的限制,可以直接使用默認構造函數
<bean id="accountService" class="com.wangc.service.impl.AccountServiceImpl2">
 	<!--使用 property 完成屬性注入
 		name:類裏面屬性名稱
		 value:向屬性注入的值
	 -->
    <property name="name" value="TEST" ></property>
    <property name="age" value="21"></property>
    <property name="birthday" ref="now"></property>
</bean>
  1. 使用有參構造進行注入:在獲取bean對象時,注入數據是必須的操作,否則對象無法創建成功。
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
    <constructor-arg name="name" value="泰斯特"></constructor-arg>
    <constructor-arg name="age" value="18"></constructor-arg>
    <constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
  1. 基於註解注入屬性
    • @Autowired:根據屬性類型進行自動裝配
    • @Qualifier:根據名稱進行注入
    • @Resource:可以根據類型注入,可以根據名稱注入
    • @Value:注入普通類型屬性
@Service
public class UserService {
	//定義 dao 類型屬性
	//不需要添加 set 方法
	//添加註入屬性註解
	@Autowired
	private UserDao userDao;
	public void add() {
	}
}

AOP

意在把應用業務邏輯和系統服務分開,就是系統中有很多各不相干的類的方法(業務邏輯),在這些衆多方法中要加入某種系統功能的代碼,其實也是爲了解耦。

  • 爲什麼:AOP可以看做是OOP的補充和完善,OP允許你定義從上到下的關係,但並不適合定義從左到右的關係,例如日誌功能。日誌代碼往往水平地散佈在所有對象層次中,而與它所散佈到的對象的核心功能毫無關係,普通的OOP設計中,會導致了大量代碼的重複,不利於各個模塊的重用。
  • AOP是通過CGLib動態代理方式實現的,系統功能作爲對業務邏輯的增強
  • JDK中的動態代理實現,創建代理對象實現增強的邏輯,傳入Proxy.newProxyInstance方法對原方法進行增強。
    在這裏插入圖片描述
  • 應用:在系統中業務邏輯中要加入某種系統功能的代碼,例如,加入日誌,加入權限判斷,加入異常處理,這種應用稱爲AOP。

深入理解 Spring

Spring bean 就是被 Spring IOC 容器創建管理 java 對象,這些beans可以以XML文件中的形式定義。理解 Spring bean 的機制對我們深入IOC有着極其重要的作用。

Bean 基礎

作用範圍

  • 單例對象:scope=“singleton”:一個應用只有一個對象的實例。它的作用範圍就是整個引用。(默認)
    • 生命週期:
      • 對象出生:當應用加載,創建容器時,對象就被創建了。
      • 對象活着:只要容器在,對象一直活着。
      • 對象死亡:當應用卸載,銷燬容器時,對象就被銷燬了。
  • 多例對象:scope=“prototype”:每次訪問對象時,都會重新創建對象實例。
    • 生命週期:
      • 對象出生:當使用對象時,創建新的對象實例。
      • 對象活着:只要對象在使用中,就一直活着。
      • 對象死亡:當對象長時間不用時,被java的垃圾回收器回收了。

加載順序

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao"></bean>

depends-on是bean標籤的屬性之一,表示一個bean對其他bean的依賴關係。這manager和accoutDao會先於beanOne被實例化,會慢於beanOne被銷燬,而beanOne不引用accountDao(或者說beanOne不會將accountDao注入到自己的屬性中)。這就是depends-on的主要作用。

Bean 原理

問題:
bean是如何被spring加載爲實例對象的?(加載機制)
bean 從創建到銷燬經歷了什麼樣的過程?(生命週期)

普通的Java對象直接new出來就可以了,但JavaBean生命週期就比較複雜了,可以簡單理解爲 默認構造方法實例化對象—set方法設置屬性值—調用初始化方法—獲取對象—銷燬對象 五步,但實際上Bean實例化之前經歷了一系列複雜的解析和加載的過程,大題可簡化爲下圖:
在這裏插入圖片描述

  1. 解析:xml或註解方式定義bean的信息統一由 BeanDefinitionReader 解析爲 BeanDefinition對象
  2. BeanDefinition:非常重要的一步, 裏面記錄了Bean的所有信息,一些信息決定了Bean將如何加載爲對象,比如對象的作用範圍是否爲 singleton,比如是否爲懶加載 lazy-init=ture,再比如是否有dependon屬性等
  3. BeanFactoryPostProcessor: bean工廠的bean屬性的後置處理器,Spring可擴展性的保證, 可以管理我們的bean工廠內所有的 BeanDefinition 數據。
  4. BeanFactory:這時候拿着記錄了所有對象信息的 BeanDefinition,放入工廠根據通過構造器創建反射創建Bean實例。
  5. BeanPostProcessor:Bean實例的後置處理器,Spring可擴展性的保證。
  6. 把Bean實例放入map中容器中管理,到此爲止ClassPathXmlApplicationContext("bean.xml");,Bean實例化並且放入IOC容器的過程就完成了。
  7. ac.getBean("xxx"),拿到bean對象。
  8. 當容器關閉時候,調用bean 的銷燬的方法(需要進行配置銷燬的方法)

在整個流程的最開始是創建BeanFactory(ApplicationContext),在refresh()方法中

問題:Bean是否線程安全、

不一定,這要看Bean的創建模式,單例不保證線程安全,多例每個線程都會有一個Bean實例,不存在數據共享固然安全。
並且要看Bean是否有實例變量,即是否有存儲數據能力,如果沒有實例變量,稱作Bean是無狀態的,這時候是線程安全的,但如果有狀態,那就要開發者自己去保證線程安全了,最簡單的就是改變 bean 的作用域,把“singleton”變更爲“prototype”,

@Autowired 原理

  • @Autowired:自動按照類型注入。當使用註解注入屬性時,set方法可以省略。它只能注入其他bean類型。當有多個類型匹配時,使用要注入的對象變量名稱作爲bean的id,在spring容器查找,找到了也可以注入成功,找不到就報錯。
    *. 實現原理:Spring在容器啓動階段,會先實例化bean,然後再對bean進行初始化操作。在初始化階段,會通過調用Bean後置處理來完成對屬性的賦值等操作,那麼同理,要想實現@Autowired的功能,肯定也是通過後置處理器來完成的。這個後置處理器就是AutowiredAnnotationBeanPostProcessor。

其他

Spring中涉及哪些設計模式?

  • IOC的工廠模式
  • AOP的動態代理模式
  • 監聽器的觀察者模式
  • bean作用範圍的單例模式

深入理解SpringBoot

SpringBoot是框架簡化是指簡化了Spring衆多框架中所需的大量且繁瑣的配置文件,要想理解SpringBoot首先要理解Spring的原理,另外理解SpringBoot的自動裝配原理和啓動流程。

  • 核心註解:啓動類上面的@SpringBootApplication,包含
    • @SpringBootConfiguration:組合了 @Configuration 註解
    • @EnableAutoConfiguration:打開自動配置的功能
    • @ComponentScan:Spring 組件掃描。

啓動過程

在這裏插入圖片描述

  • 初始化:構造SpringApplication的時候會進行初始化的工作
    • 判斷Web應用類型,None,Servlet或響應式(Spring5新增)
    • setInitializers在 META-INF/spring.factories 中找到所有初始化器 ApplicationListener,設置到initializers屬性中
    • setListener在 META-INF/spring.factories 中找到所有監聽器 ApplicationListener,設置到listeners屬性中
    • 找出運行的主函數
  • 啓動run,進入refresh方法之前都是準備工作
  • 構造一個StopWatch,進行計時
  • 找到並啓動所有 SpringApplicationRunListener,用於監聽run方法的執行。
  • 配置環境模塊
  • 應用上下文模塊
  • 構造Spring容器,進入refresh方法

SpringApplicationRunListener看名字也知道用於監聽SpringApplication的run方法的執行。

它定義了5個步驟:

started(run方法執行的時候立馬執行;對應事件的類型是ApplicationStartedEvent)
environmentPrepared(ApplicationContext創建之前並且環境信息準備好的時候調用;對應事件的類型是ApplicationEnvironmentPreparedEvent)
contextPrepared(ApplicationContext創建好並且在source加載之前調用一次;沒有具體的對應事件)
contextLoaded(ApplicationContext創建並加載之後並在refresh之前調用;對應事件的類型是ApplicationPreparedEvent)
finished(run方法結束之前調用;對應事件的類型是ApplicationReadyEvent或ApplicationFailedEvent)

自動配置原理

資料
https://www.bilibili.com/video/BV1nD4y1Q7ep?t=4047
https://www.bilibili.com/video/BV1ME411e7k8?p=19
https://fangjian0423.github.io/2017/04/30/springboot-startup-analysis/
https://www.bilibili.com/video/BV1Rz4y197uX?p=2
https://thinkwon.blog.csdn.net/article/details/104397516

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章