Spring是分層的Java SE/EE應用一站式的輕量級開源框架,以IoC(Inverse of Control)和AOP(Aspect Oriented Programming)為內核,提供了展現層Spring MVC和持久層Spring JDBC以及業務層事務管理等眾多的企業級應用技術,此外,Spring整合了開源世界里眾多的第三方框架和類庫。
Spring的體系結構:
Spring整個框架按其所屬功能可划分為5個主要模塊:數據訪問和集成、Web及遠程操作、測試框架、AOP和IoC。
IoC:Spring的核心模塊實現了IoC的功能,它將類和類之間的依賴從代碼中脫離出來,用配置的方式進行依賴關系描述,由IoC容器負責依賴類之間的創建、拼接、管理、獲取等工作。BeanFactory接口是Spring框架的核心接口,它實現了容器許多核心的功能。Context模塊構建於核心模塊之上,擴展了BeanFactory的功能,添加了i18n國際化、Bean生命周期控制、框架事件體系、資源加載透明化等多項功能。此外,該模塊還提供了許多企業級服務的支持。ApplicationContext是Context模塊的核心接口。表達式語言模塊是統一表達式語言的一個擴展,該表達式語言用於查詢和管理運行期的對象,支持設置或獲取對象屬性,調用對象方法、操作數組、集合等。還提供了邏輯表達式運算、變量定義等功能。使用它可以方便地通過表達式串和Spring IoC容器進行交互。
AOP模塊:AOP是進行橫切邏輯編程的思想,開拓了人們考慮問題的思路。在AOP模塊里,Spring提供了滿足AOP Alliance規范的實現,此外,還整合了AspectJ這種AOP語言級的框架。Java 5.0引入java.lang.instrument,允許在JVM啟動時啟用一個代理類,通過該代理類在運行期修改類的字節碼,改變一個類的功能,實現AOP的功能。
數據訪問和集成:Spring站在DAO的抽象層面,建立了一套DAO層統一的異常體系,同時將各種訪問數據的檢查型異常轉換成非檢查型異常,為整個各種持久層框架提供基礎。其次,Spring通過模版化技術對各種數據訪問技術進行了薄層的封裝,將模式化的代碼隱藏起來,使數據訪問的程序得到了大幅簡化。
Web及遠程調用:該模塊建立在ApplicationContext模塊之上,提供了Web應用的各種工具類,若通過Listener或Servlet初始化Spring容器,將Spring容器注冊到Web容器中。其次,該模塊還提供了多項面向Web的功能。此外,Spring還可以整合Struts、WebWork、Tapestry Web等MVC框架。
Spring注解:
在ApplicationContext文件中,使用Spring的<context:component-scan base-package="">掃描指定類包下的所有類,這樣在類中定的Spring注解才能產生作用。
@Repository:定義一個DAO Bean
@Autowired:將Spring容器中的Bean注入進來
@Service:將一個類標注為一個服務層的Bean
@ContextConfiguration:指定Spring的配置容器
@Controller:將一個類標注為Spring MVC的Controller
@RequestMapping(value="/index.html"):負責處理一個xxx.html請求
IoC
DI(Dependency Injection):讓調用類的某一接口實現類的依賴關系由第三方(容器或協作類)注入,以移出調用類對某一接口實現類的的依賴。
從注入方法上來看,主要可以划分為3種類型:構造函數注入、屬性注入和接口注入。Spring支持構造函數注入和屬性注入。
構造函數注入:在構造函數注入中,我們通過調用類的構造函數,將接口實現類通過構造函數變量傳入。
屬性注入:屬性注入可以有選擇地通過Setter方法完成調用類所需依賴的注入。
接口注入:將調用累所有依賴注入的方法抽取到一個接口中,調用類通過實現該接口提供相應的注入方法。
java的反射機制
Java語言允許通過程序化的方式間接對Class的對象實例操作,Class文件由類裝載器裝載后,在JVM中將形成一份描述Class結構的元信息對象,通過該元信息對象可以獲知Class的結構信息:構造函數、屬性和方法等。Java允許用戶借由這個Class相關的元信息對象間接調用Class對象的功能.
例:
public Class Car{
private String brand;
private String color;
private int maxSpeed;
public Car(){}
public Car(String brand,String color,int maxSpeed){
this.brand = brand;
this.color = color;
this.maxSpeed = maxSpeed;
}
public void introduce(){
System.out.println("brand"+brand+",color"+color+",+maxSpeed"+maxSpeed);
}
...
}
import java.lang.reflect.Construcor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectTest{
public static Car initByDefaultConst() throws Throwable{
//通過類加載器獲取Car類對象
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class clazz = loader.loadClass(Car):
//獲取類的默認構造器對象並通過它實例化Car
Constructor cons = clazz.getDeclardConstructor((Class[])null);
Car car = (Car)cons.newInstance():
//通過反射方法設置屬性
Method setBrand = clazz.getMethod("setBrand",String.class);
setBrand.invoke(car,"WCA72");
Method setColor = clazz.getMethod("setColor ",String.class);
setColor .invoke(car,"black");
Method setMaxSpeed = clazz.getMethod("setMaxSpeed ",int.class);
setMaxSpeed .invoke(car,200);
return car;
}
public static void main(String[] args) throws Throwable{
Car car = initByDefaultConst();
car.introduce();
}
}
類裝載器ClassLoader
工作機制:
類裝載器就是尋找類的字節碼文件並構造出類在JVM內部表示的對象組件。在Java中,類裝載器裝入JVM中,要經過以下步驟:
1.裝載:查找和導入Class文件
2.鏈接:執行校驗、准備和解析步驟,其中解析步驟是可以選擇的:
校驗:檢查載入Class文件數據的正確性
准備:給類的靜態變量分配存儲空間
解析:將符號引用轉成直接引用
3.初始化:對類的靜態變量、靜態代碼塊執行初始化工作
類加載器工作由ClassLoader及其子類負責,ClassLoader是一個重要的Java運行時系統組件,它負責在運行時查找和裝入Class字節碼文件。JVM在運行時會產生三個ClassLoadre:根裝載器、ExtClassLoader和AppClassLoader。根裝載器不是ClassLoader的子類,負責裝載JRE的核心類庫。ExtClassLoader和AppClassLoader都是ClassLoader的子類。其中,EctClassLoader負責裝載JRE擴展目錄ext中的JAR類包,AppClassLoader負責裝載Classpath路徑下的類包。
JVM裝載類時使用“全盤負責委托機制”,“全盤負責”是指當一個ClassLoader裝載一個類時,除非顯式地使用另一個ClassLoader,該類所依賴及引用的類也由這個ClassLoader載入:“委托機制”是指先委托父裝載器尋找目標類,只有在找不到的情況下才從自己的類路徑中查找並裝載目標類。
ClassLoader的重要方法:
Class loadClass(String name):name參數指定類裝載器需要裝載類的名字,必須使用全限定類名。該方法有一個重載方法loadClass(String name,boolean resolve),resolve參數告訴類裝載器是否需要解析該類。在初始化類之前,應考慮進行類解析的工作,但並不是所有的類都需要解析,若JVM值需知道該類是否存在或找出該類的超類,那么就不需要進行解析。
Class defineClass(String name,byte[] b,int off,int len):將類文件的字節數組轉換成JVM內部的java.lang.Class對象。字節數組可以從本地文件系統、遠程網絡獲取。name為字節數組對應的全限定類名。
Class findSystemClass(String name):從本地文件系統載入Class文件,若本地文件系統更不存在該Class文件,將拋出ClassNotFoundException異常。
Class findLoadedClass():調用該方法來查看ClassLoader是否已裝入某個類。若已裝入,則返回java.lang.Class對象,否則返回null。
ClassLoader getParent():獲取類裝載器的父裝載器。
反射對象類在java.reflect包中定義,下面是最主要的三個反射類:
Constructor:類的構造函數反射類,通過Class#getContructors()方法可以獲得類的所有構造函數反射對象數組。在JDK 5.0中,還可以通過getContructor(Class parameterTypes)獲取擁有特定入參的構造函數反射對象。Constructor的一個主要方法是newInstance(Object[] initargs),通過該方法可以創建一個對象類的實例,相當於new關鍵字。
Method:類方法的反射類,通過Class#getDeclaredMehtods()方法可以獲取類的所有方法反射類對象數組Method[]。在 JDK 5.0中可以通過getDeclaredMehtods(String name,Class parameterTypes)獲取特定簽名的方法,name為方法名;Class為方法入參類型列表。Method最主要的方法是invoke(Object obj,Object[] args),obj表示操作的目標對象,args為方法入參。
Method還有很多用於獲取類方法更多信息的方法:
Class getReturnType():獲取方法的返回值類型
Class[] getParameterTypes():獲取方法的入參類型數組
Class[] getExceptionTypes():獲取方法的一場類型數組
Annotationp[][] getParamerterAnnotations():獲取方法的注解信息
Field:類的成員變量的反射類,通過Class#getDeclaredFields()方法可以獲取類的成員變量反射對象數組,通過Class#getDeclaredFields(String name)則可獲取某個特定名稱的成員變量反射對象。Field類最主要的方法是set(Object obj,Object value),obj表示操作目標評對象,通過value為目標對象的成員變量設置值。若成員變為為基礎類型,用戶可以使用Field類中提供的帶類型名的值設置方法。
通過反射機制可以調用私有變量和私有方法。但在訪問private、protected成員變量和方法時必須通過setAccessible(boolean acess)方法取消java語言檢查,否則拋出IllegalAccessException。若JVM的安全管理器設置了相應的安全機制,調用該方法將拋出SecurityException。
Spring設計了一個Resource接口,它為應用提供了更強的訪問底層資源的能力。該接口擁有對應不同資源類型的實現類。Resource接口的主要方法:
boolean exists():資源是否存在
boolean isOpen():資源是否打開
URL getURL() throws IOException:若底層資源可以表示成URL,該方法返回對應的URL對象
File getFile() throws IOException:若底層資源對應一個文件,該方法返回對應的File對象
InputStream getInputStream() throws IOException:返回資源對應的輸入流
Spring框架使用Resource裝載各種資源,Resource的具體實現類如下:
ByteArrayResource:二進制數組表示的資源,二進制數組資源可以在內存中通過程序構造
ClassPathResource:類路徑下的資源,資源以相對於類路徑的方式表示
FileSystemResource:文件系統資源,資源以文件系統路徑的方式表示
InputStreamResource:對應一個InputStream的資源
ServletContextResource:為訪問web容器上下文中的資源而設計的類,負責以相對於Web應用根目錄的路徑加載資源,它支持已流和URL的方式訪問,在WAR解包的情況下,也可以通過File的方式訪問,還可以直接從JAR包中訪問資源。
UrlResource:封裝了java.net.URL,它使用戶能夠訪問任何可以通過URL表示的資源。
對資源進行編碼:
EncodedResource encRes = new EncodedResource(res,"UTF-8");
資源類型的地址前綴
地址前綴 示例
classpath classpath:com/beans.xml
對應資源類型:從類路徑中加載資源,classpath:和classpath:/是等價的,都是相當於類的跟路徑。資源文件可以在標准的文件系統中,也可以在jar或zip的類包中
file: file:/com/beans.xml
對應資源類型:使用UrlResource從文件系統目錄中裝載資源,可采用絕對或相對路徑
http:// http://www.beans.xml
對應資源類型:使用UrlResource從Web服務器中裝載資源
ftp:// ftp://www.beans.xml
對應資源類型:使用UrlResource從FTP服務器中裝載資源
沒有前綴 com/beans.xml
對應資源類型:根據ApplicationContext具體實現類采用對應的類型的Resource
Ant風格資源地址支持3種匹配符:
?:匹配文件名中的一個字符
*:匹配文件名中任意字符
**:匹配多層路徑
Spring定義一套資源加載的接口,並提供了實現類。ResourceLoader接口僅有一個getResource(String location)的方法,可以根據一個資源地址加載文件資源。不過這個文件資源僅支持帶資源類型前綴的表達式,不支持Ant風格的資源路徑表達式。ResourcePatternResolver擴展了ResourceLoader接口,定義了一個新的接口方法:getResources(String locationPattern),該方法支持帶資源類型前綴及Ant風格的資源路徑的表達式。PathMatchingResourcePatternResolver是Spring提供了標准實現類。
Spring為BeanFactory提供了多種實現,最常用的XmlBeanFactory。
BeanFactory最主要的方法就是getBean(String beanName),該方法從容器中返回特定該名稱的Bean。BeanFactory的其他接口:
ListableBeanFactory:該接口定義了訪問容器中Bean基本信息的若干方法
HierarchicalBeanFactory:父子級聯IoC容器的接口,子容器可以通過接口方法訪問父容器。
ConfigurableBeanFactory:增強IoC容器的可定制性,它定義了設置類裝載其、屬性編輯器、容器初始化后置處理器等方法
AutowireCapableBeanFactory:定義了將容器中的Bean按某種規則進行自動裝配的方法
SingletonBeanRegistry:定義了允許在運行期間向容器注冊單實例Bean的方法
BeanDefinitionRegistry:Spring配置文件中每一個<bean>節點元素在Spring容器里都通過一個BeanDefinition對象表示,他描述了Bean的配置信息。而BeanDefinition Registry接口提供了向容器手工注冊BeanDefinition對象的方法。
ApplicationContext的主要實現類是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,前者默認從類路徑加載配置文件,后者默認從文件系統中裝載配置文件。ApplicationContext繼承了HierarchicalBeanFactory和ListableBeanFactory接口。
ApplicationEventPublisher:讓容器擁有發布應用上下文事件的功能,包括容器啟動事件、關閉事件等。實現了ApplicationListener事件監聽接口的Bean可以接受到容器事件,並對事件進行響應處理。在ApplicationContext抽象實現類AbstractApplicationContext中,我們可以發現存在一個ApplicationEventMulticaster,它負責保存所有監聽器,以便在容器產生上下文事件時通知這些事件監聽者。
MessageSource:為應用提供il18n國際化消息訪問功能。
ResourcePatternResolver:所有ApplicationContext實現類都實現了類似於PathMatchingResourcePatternResolver功能,可以通過帶前綴的Ant風格的資源文件路徑裝載Spring的配置文件。
LifeCycle:該接口提供了start()和stop()兩個方法,主要用於控制異步處理過程。在具體使用時,ApplicationContext及具體的Bean都必須同時實現該接口,ApplicationContext會將start/stop的信息傳遞給容器中所有實現了該接口的Bean,已達到管理和控制JMX、任務調度等目的。
ConfigurableApplicationContext擴展與ApplicationContext,它新增了refresh()和close(),讓ApplicationContext具有啟動、刷新和關閉應用上下文的能力。在應用上下文關閉的情況下調用refresh()即可啟動應用上下文,在已經啟動的狀態下,調用refresh()則清理緩存並重新裝載配置信息,而調用close()則可關閉應用上下文。
ApplicationContext和BeanFactory的重大區別:BeanFactory在初始化容器時,並未實例化Bean,直到第一次訪問某個Bean時才實例目標Bean;而ApplicationContext則在初始化應用上下文時就實例化所有單實例的Bean。因次,ApplicationContext的初始化事件會比BeanFactory稍長,但之后的調用沒有第一次懲罰的問題。
WebApplicationContext是專門為Web應用准備的,它允許從相對於Web根目錄的路徑中裝載配置文件完成初始化工作。從WebApplicationContext中可以獲得ServletContext的引用,整個Web應用上下文對象將作為屬性放置到ServletContext中,以便Web應用程序可以訪問Srping應用上下文。Spring專門為次提供一個工具類WebApplicationContextUtils,通過該類的getWebApplicationContext(ServletContext sc)方法,既可以從ServletContext中獲取WebApplicationContext是實例。在WebApplicationContext中還為Bean添加了三個新的作用域:request作用域、session作用域和global session作用域。而在為Web應用環境下,Bean只有singleton和prototype兩種作用域。
WebApplicationContext定義了一個常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,在上下文啟動時,WebApplicationContext實例即以此為鍵放置在ServletContext的屬性列表中。
ConfigurableApplicationContext允許通過配置的方式實例化WebApplicationContext。
setServletContext(ServletContext servletContext):為Spring設置Web應用上下文,以便兩者整合
setConfigLocations(String[] configLocations):設置Spring配置文件地址,一般情況下,配置文件地址是相對於Web根目錄的地址。
WebApplicationContext的初始化方式和BeanFactory、ApplicationContext有所區別。WebApplicationContext秀奧ServletContext實例,也就是說它必須在擁有Web容器的前提下才能完成啟動的工作。
Spring分別提供了用於啟動WebApplicationContext的Servlet的Web容器監聽器:
org.springframework.web.context.ContextLoaderServlet、org.springframework.web.context.ContextLoaderListener兩者的內部都實現了啟動WebApplicationContext實例的邏輯,我們只要根據Web容器的具體情況選擇兩只之一,並在web.xml中完成配置就可以了。
由於WebApplicationContext需要使用日志功能,用戶可以將Log4J的配置文件放置到類路徑的WEB-INF/classes下,這時Log4J引擎即可順利啟動。Spring為啟動Log4J引擎提供了兩個類似於啟動WebApplicationContext的實現類:Log4jConfigServlet和Log4jConfigListener。
<context-param>
<param-name>contextConfigLocation</param-name>
<paramm-value>
/WEB-INF/xxx.xml,/WEB-INF/xxxx.xml
</param-value>
</context-param>
<context-param>
<param-name>log4jConfigLocation</param-name>
<paramm-value>
/WEB-INF/log4j.properties
</param-value>
</context-param>
<servlet>
<servlet-name>log4jConfigServlet</servlet-name>
<servlet-class>org.springframework.web.util.Log4jConfigServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>springContextLoaderServlet</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
通過HierarchicalBeanFactory接口,Spring的IoC容器可以建立父子層級關聯的容器體系,子容器可以訪問父容器中的Bean,但父容器不能訪問子容器的Bean。在容器內,Bean的id必須是唯一的,但子容器可以擁有一個和父容器id相同的bean。父子容器層級體系增強了Spring容器架構的擴展性和靈活性。
Bean的生命周期:
1.當調用者通過getBean(beanName)向容器請求某一個Bean時,若容器注冊了org.springframework.beans.factory.InstantiationAwareBeanPostProcessor接口,在實例化Bean之前,將調用接口的postProcessBeforeInstantiation()方法。
2.根據配置情況調用Bean構造函數或工廠方法實例化Bean
3.若容器注冊了InstantiationAwareBeanPostProcessor接口,在實例化Bean之后,調用該接口的postProcessAfterInstantiation()方法,可在這里對已經實例化的對象進行相關操作。
4.若Bean配置了屬性信息,容器在這一步着手將配置值設置到Bean對應的屬性中,不過在設置每個屬性之前先將調用InstantiationAwareBeanPostProcessor接口的postProcessPropertyValues()方法。
5.調用Bean的屬性設置方法設置屬性值
6.若Bean實現了org.springframework.beans.factory.BeanNameAware接口,將調用setBeanName()接口方法,將配置文件中該Bean對應的名稱設置到Bean中。
7.若Bean實現了org.springframework.beans.factory.BeanFactoryAware接口,將調用setBeanFactory()接口方法,將BeanFactory容器實例設置到Bean中。
8.若BeanFactory裝配了org.springframework.beans.factory.config.BeanPostProcessor后處理器,將調用BeanPostProcessor的Object postProcessBeforeInitialization(Object bean,String beanName)接口方法對Bean進行加工操作。其中參數bean是當前正在處理的bean,而beanName的hi當前Bean的配置名,返回的對象為加工處理后的Bean。BeanPostProcessor在Spring框架中占有重要地位,為容器提供對Bean進行后加工處理的切入點。
9.若Bean實現了InitializingBean的接口,將調用接口的afterPropertiesSet()方法
10.若在<bean>通過init-method屬性定義了初始化方法,將執行這個方法
11.BeanPostProcessor后處理器定義了兩個方法:其一是postProcessBeforeInitializatiopn()在第8步調用;其二是Object postProcessAfterInitialization(Object bean,String beanName)方法,這個方法在此時調用,容器在此獲得對Bean進行加工處理的機會。
12.若在<bean>中指定Bean的作用范圍為scope='prototype',將Bean返回給調用者,調用者負責調用者后續生命的管理,Spring不再管理這個Bean的生命周期。若作用范圍設置為scope='singleton',則將Bean放入到Spring IoC容器的緩存池中,並將Bean引用返回給調用者,Spring繼續對這些Bean進行后續的生命管理。
13.對於scope='singleton'的Bean,當容器關閉時,將觸發Spring對Bean的后續生命周期的管理工作,首先,若Bean實現了DisposableBean接口,則將調用接口的afterPropertiesSet()方法,可以在次編寫釋放資源、記錄日志等操作。
14.對於scope='singleton'的Bean,若通過<bean>的destroy-method屬性指定了Bean的銷毀方法,Spring將執行Bean這個方法,完成Bean資源的釋放等操作。
Bean的完整生命周期從Spring容器着手實例化Bean開始,知道最終銷毀Bean,這當中經過了許多關鍵點,每個關鍵點都涉及特定的方法調用,可以將這些方法大致划分為三類:
Bean自身的方法:若調用Bean構造函數實例化Bean,調用setter設置Bean的屬性值以及通過<bean>的init-method和destroy-method所制定的方法;
Bean級生命周期接口方法:如BeanNameAware、BeanFactoryAware、InitializingBean和DisposableBean,這些接口方法由Bean直接實現
容器級生命周期接口方法:后處理器接口一般不由Bean本身實現,他們獨立於Bean,實現類以容器附加裝置的形式注冊到Spring容器中並通過接口反射為Spring容器預先識別。Spring容器創建Bean時,這些后處理器都會發生作用,所以這些后處理器的影響是全局的。
ApplicationContext和BeanFactory的最大區別在於前者會利用Java的反射機制自動識別出配置文件中定義的BeanPostProcessor、IntantiationAwareBeanPostProcessor和BeanFactoryProcessor,並將他們注冊到應用上下文中,而后者需要在代碼中通過手工調用addBeanPostProcessor()方法進行注冊。