目錄
Spring
標籤(空格分隔): Spring
Spring通過配置文件去描述
Bean
和Bean
之間的依賴關係, 利用Java
的反射功能實例化Bean
並建立Bean
之間的依賴關係.Spring IoC
在完成這些底層工作的基礎之上還提供了Bean
的實例緩存, 生命週期管理, Bean代理實例, 事件發佈, 資源裝載等高級服務.
- 實例緩存: 對於 單例模式的
Bean
,Spring IoC
在將其實例化之後會進行緩存, 以供下次使用, 當我們向容器索取Bean
的時候, 第一步就是從緩存中讀取, 如果沒有的話 纔回去創建新的實例. - 生命週期管理: 可以配置單例模式的
init-method
@PostConstruct
(構造函數之後) 和destory-method
@PreDestory
(銷燬之前). Bean
代理實例: 通過代理模式給實例添加額外功能.- 事件發佈: 給某個事件 加上一個監聽器, 以對事件的不同結果, 實行不同的操作.
- 資源裝載: 裝載類外資源.
生命週期管理
---
Bean工廠(
com.springframework.beans.factory.BeanFactory
): 是Spring框架中最核心的接口, 它提供了高級IoC的配置機制. BeanFactory使管理不同類型的Java對象成爲可能.
應用上下文(
com.springframework.context.ApplicationContext
):建立在BeanFactory
基礎之上提供了更多面向引用的功能, 它提供了國際化支持和框架事件體系, 更加易於創建實際應用.
我們一般稱BeanFactory
爲IoC
容器, 而稱ApplicationContext
爲應用上下文或者Spring容器.
BeanFactory是Spring框架的基礎設置, 面向Spring本身; ApplicationContext面向使用Spring框架的開發者, 幾乎所有的應用場合都可以直接使用ApplicationContext
.
Spring框架是工廠, 而被創建的類對象本身也可能是一個工廠, 這就形成了創建工廠的工廠.
BeanFactory
BeanFactory是類的通用工廠. 他可以創建並管理各種類的對象, 這些類就是POJO, Spring稱這些被創建並管理的對象爲Bean.
1. 初始化
我們在Spring配置文件中創建Bean People, 然後通過BeanFactory加載配置文件, 啓動IoC容器.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="people" class="net.deniro.springBoot.spring4.IoC.People"
p:name="deniro"
p:age="25"
/>
</beans>
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource resource = resolver.getResource("classpath:beans.xml");
System.out.println("getURL:" + resource.getURL());
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
System.out.println("Bean 工廠已完成初始化");
People people = factory.getBean("people", People.class);
System.out.println("People 已被創建");
System.out.println(people.toString());
ApplicationContext
ApplicationContext由BeanFactory派生而來, 提供了很多的實際應用的功能. 在BeanFactory中, 很多功能需要編程實現, 而在ApplicationContext中則可以通過配置的方式去實現解耦.
//從類路徑加載配置文件
// 提供一組配置文件, Spring IoC在資源裝配的時候, 會將其進行整合.
ApplicationContext context3 = new ClassPathXmlApplicationContext(new
String[]{"beans.xml", "beans2.xml"});
//從文件系統加載配置文件
ApplicationContext context2 = new FileSystemXmlApplicationContext("d:/beans.xml");
Spring支持註解配置, 主要功能來自於Spring的一個名爲JavaConfig
的子項目, 目前JavaConfig
已經升級爲Spring核心框架的一部分, 一個標註@Configuration
註解的POJO既可以爲Spring
提供所需的Bean
配置信息.
@Configuration//表示這個類包含配置信息
public class Beans {
//定義 Bean
@Bean(name = "people")
public People build() {
People people = new People();
people.setAge(25);
people.setName("deniro");
return people;
}
}
ApplicationContext context = new AnnotationConfigApplicationContext(Beans.class);
People people = context.getBean("people", People.class);
Assert.assertNotNull(people);
Bean的生命週期
- Spring Bean生命週期簡單易懂. 在一個
bean
實例被初始的時候, 需要執行一系列的操作以達到可用的狀態. 同樣的, 當一個類不再被調用的時候需要執行相關的析構函數, 並將其從容器中移除. Spring bean factory
負責管理在Spring
容器中被創建的bean
的生命週期. Bean的生命週期由兩組回調函數組成.- 初始化之後調用的毀掉方法.
- 銷燬之前調用的毀掉方法.
- Spring框架提供了以下四種方式來管理bean的生命週期事件 :
- 配置文件中提供構造類中
init-method
和destory-method
的方法名, 用於構造函數之後,和析構函數之前的調用. - 註解方式的情況下在類中的方法上加
@PostConstruct
和@PreDestory
註解. - InitializingBean 和 DisposableBean 回調接口
- 針對特殊行爲的其他 Aware 接口
Spring IoC 如何實現
Bean工廠(
com.springframework.beans.factory.BeanFactory
): 是Spring框架中最核心的接口, 它提供了高級IoC的配置機制. BeanFactory使管理不同類型的Java對象成爲可能.
應用上下文(
com.springframework.context.ApplicationContext
):建立在BeanFactory
基礎之上提供了更多面向引用的功能, 它提供了國際化支持和框架事件體系, 更加易於創建實際應用.
我們一般稱BeanFactory
爲IoC
容器, 而稱ApplicationContext
爲應用上下文或者Spring容器.
BeanFactory是Spring框架的基礎設置, 面向Spring本身; ApplicationContext面向使用Spring框架的開發者, 幾乎所有的應用場合都可以直接使用ApplicationContext
.
Spring框架是工廠, 而被創建的類對象本身也可能是一個工廠, 這就形成了創建工廠的工廠.
Spring AOP
有了IOC其中CGLIB
解決了靜態代理和動態代理對類的接口的依賴性和功能依賴性的問題之後, 使得將所有類(非final修飾的類)都實現代理模式成爲可能(CHLIB是基於繼承的方式做的動態代理). 所以IOC是AOP的基礎.
面向切面編程, 在我們的應用中, 經常需要做一些事情, 但是這些事情與核心業務無關, 比如要記錄所有update的執行時間, 操作人信息等等 到日誌. 通過 AOP就可以在不修改原有代碼的情況下完成需求.
AOP思想: 基於動態代理把各個事務單獨抽取出來, 然後通過配置去尋找他的前置,後置,異常等方法.
實際上就是通過 動態代理設計模式的動態代理方法, 使用 InvocationHandler傳入類加載器和實現的接口 以 獲取代理運行的被代理類的函數的方法名和傳入的參數, 然後通過反射創建被代理類完成其功能, 並在其前和其後加入其它功能.
JDK動態代理類和委託類需要實現同一個接口, 也就是說只有實現了某個接口的類可以使用Java動態代理機制. 但是, 事實上使用中並不是遇到的所有類都會有實現接口. 因此, 對於沒有實現接口的類, 就不能使用該機制. CGLIB則可以實現對類的動態代理.
Spring事務實現方式
編程式事務指的是通過編碼方式實現事務, 即類似於JDBC編程實現事務管理.
聲明式事務管理又有兩種實現方式:
- 基於xml配置文件的方式
- 另一個是在業務方法上進行
@Transaction
註解, 將事務規則應用到業務邏輯中.
劃分處理單元IOC
通過IOC將事務的各種配置放到IOC容器之中.
由於Spring解決的問題是對單個數據庫進行局部事務處理的, 具體的實現首先用Spring中的IOC劃分了事務處理單元. 並且將對事務的各種配置放到了IOC容器中(設置事務管理器, 設置事務的傳播特性以及隔離機制).
AOP攔截需要進行事務處理的類
Spring事務處理模塊是通過AOP功能來實現聲明式事務處理的, 具體操作(比如事務實行的配置和讀取, 事務對象的抽象), 用TransactionProxyFactoryBean
接口來使用AOP功能, 生成proxy代理對象. 通過TransactionProxyFactoryBean
完成對代理方法的攔截, 將事務的處理功能編織到攔截的方法中. 讀取IOC容器事務配置屬性, 轉化爲Spring事務處理需要的內部數據結構,轉化爲TransactionAttribute
表示的數據對象.
SpringMVC 運行流程
SpringMVC
將所有的請求都交給DispatcherServlet
.DispatcherServlet
查詢一個或多個HandlerMapping
, 找到處理請求的Controller
.DispatcherServlet
將請求提交到目標Controller
.Controller
進行業務邏輯處理之後, 會返回一個ModelAndView
.DispatcherServlet
查詢一個或多個ViewResolver
視圖解析器, 找到ModelAndView
對象指定的視圖對象.- 視圖對象負責渲染並返回給客戶端.
SpringMVC的啓動流程
<!-- 分發所有的HTTP 請求和響應. -->
<servlet>
<servlet-name>springServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/spring-mvc*.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
web.xml
中爲SpringMVC
的Servlet
配置了load-on-startup
, 然後程序啓動的時候會初始化SpringMVC
, 在HTTPServlet
中配置的contextConfigLocation
屬性到Servlet
中, 然後在FrameWorkServlet
中創建了WebApplicationContext
,DispatcherServlet
根據contextConfigLocation
配置的classpath
下的xml
文件初始化了SpringMVC
總的組件.load-on-startup
: 大於等於0的時候,表示容器在啓動的時候就加載並初始化這個Servlet; 小於0或者不設置的時候, 表示容器在該Servlet被選擇使用的時候纔回去加載.
SpringMVC啓動過程大致分爲兩個階段:
- ContextLoaderListener初始化, 實例化IOC容器, 並將此容器註冊到ServletContext中.
- DispatcherServlet初始化, 建立自己的上下文, 也註冊到ServletContext中.
Spring單例實現原理.
我們實現的單例,不論是懶漢,餓漢或者是雙重加鎖且進制代碼重排, 都需要在定義類的時候對其單例模式進行定義, 但是在Spring中卻並沒有這樣做.
- Spring對Bean實例的創建時用單例註冊表的方式實現的, 這個註冊表的緩存是
ConcurrentHashMap
對象, 在我們需要實例的時候, 去向IOC索取, 如果發現緩存中不存在的話就去實例化該對象, 然後放入緩存並返回. 如果緩存有的話就直接返回. - 通過這種方式實現了Spring的單例模式, 並且減少了因爲頻繁的創建和銷燬實例付出的代價.
單例模式 / 原型模式
單例模式: 確保每個類只有一個實例, 而且自行實例化並向整個系統提供這個實例.
當多用戶同時請求一個服務的時候, 容器會給每一個請求分配一個線程, 這時多個線程會併發執行該請求對應的業務邏輯(成員方法). 重要的是, 如果這時候邏輯處理中有對該單例屬性的修改(有狀態Bean), 則必須考慮線程同步問題.
同步機制的比較
ThreadLocal和線程同步機制相比有什麼優勢呢?
- ThreadLocal和線程同步機制都是爲了解決多線程中相同變量的訪問衝突問題.
- 同步機制: 通過對象的鎖機制保證同一時間只有一個線程訪問變量, 這時候變量是多個線程共享的, 使用同步機制要求程序員縝密的分析什麼時候對變量進行讀寫, 什麼時候需要鎖定某個對象, 什麼時候釋放對象鎖等複雜的問題, 程序設計和編寫的難度比較大.
- ThreadLocal: 從另一個角度去避免上述的困難並且解決併發訪問, ThreadLocal會爲每一個線程提供一個獨立的變量副本, 從而隔離了多個線程對數據的訪問衝突. 因爲每一個線程都有自己的變量副本, 從而也就沒必要對改變量進行同步了. ThreadLocal提供了線程安全的共享對象, 在編寫多線程代碼的時候, 可以把不安全的對象封裝進ThreadLocal.
- 在算法常用的角度來說就是 同步機制 以犧牲時間複雜度的代價降低空間複雜度. ThreadLocal以犧牲空間複雜度的代價降低時間複雜度.
Spring使用ThreadLocal解決線程安全問題. 按上述只有無狀態的Bean纔可以在多線程情況下共享. 但是在Spring中絕大部分的Bean都可以聲明爲Singleton, 這是因爲Spring對一些 有狀態的Bean使用了ThreadLocal進行處理.
原型模式的使用情況: 自己生命的有狀態的Bean在高併發的情況下, 可以將其聲明爲Prototype.
這樣在基本情況下我們使用 單例模式就可以了 , 對於我們聲明的一些 有狀態Bean, 對其
@Scope(ConfigurableFactory.SCOPE_PROTOTYPE)
裝飾.