Spring的核心是個lightweitht 的container,他是實現IOC容器、非侵入性(No Intrusive)的框架,并提供AOP概念的實現方式,提供Persistence、transaction的支持,提供MVC Web框架的實現,并對一些常用的企業服務API提供一致的模型封裝,是一個全方位的Application Framework。
一、Spring部分術語介紹
No Intrusive:應用程序幾乎感覺不到框架的存在,減低應用程序在框架移植時的負擔,進一步增加應用程序組件的Reusability。
控制反轉(IoC):依賴關系的轉移。程序不應依賴實現,而是依賴于接口。
即如果A依賴B,則B擁有控制權。依賴關系的反轉即是控制關系的反轉,將控制權由實現的一方轉移至抽象的一方,藉由讓抽象方擁有控制權,可以獲得組件的殼重用性。
在例子中,整個控制權從實際的FloppyWriter轉移到抽象的IDeviceWriter接口上m而讓Business依賴于IDeviceWriter接口,而FloppyWriter和UsbDiskWriter也依賴于IDeviceWriter接口。
依賴注入(Dependency Injection):IoC模式是一個高層的模式概念,實現IoC有兩種方式:Dependency Injection和Service Locator,Spring采用的是DI。
保留抽象接口,讓組件依賴于抽象接口,當組件要與其他實際的對象發生依賴關系時,通過抽象接口來注入依賴的實際對象。
依賴注入的三種實現方式:Interface Injection、Setter Injection、Constructor Injection。
二、第一個Spring程序。
載入配置文件:
1
Resource rs = new ClassPathResource("SpringDemo\\applicationContext.xml");
2
BeanFactory factory = new XmlBeanFactory(rs);
ClassPathResource:從系統的類路徑中加載,在上述的代碼中,目錄的層次結構如下圖所示:
FileSystemResource:從文件系統加載,比如說自己指定配置文件的全路徑
InputStreamResource:從輸入流中加載
ServletContextResource:從Servlet 上下文環境中加載
UrlResource:從指定的Url加載
一、Bean、消息、事件
1. BeanFactory、ApplicationContext
BeanFactory負責讀取Bean定義文件,管理對象的加載、生成,維護Bean對象與Bean對象之間的依賴關系,負責Bean的生命周期。
ApplicationContext具備如BeanFactory基本的容器管理功能之外,還提供一個應用程序所需的更完整的框架功能,如取得資源文件的更方便的方法,提供文字消息解析的方法,支持國際化消息,事件的發布、處理與傳播等。
建議用ApplicationContext替代BeanFactory。在實現ApplicationContext的類中,最常用的是以下三個:
org.springframework.context.support.FileSystemXmlApplicationContext:可指定XML定義文件的相對路徑或者絕對路徑來讀取定義文件。
org.springframework.context.support.ClassPathXmlApplicationContext:從Classpath設定路徑中來讀取XML定義文件。
2. 使用Constructor Injection
即使使用Constructor Injection,也建議定義一個無參構造方法,以讓Spring可以有使用無參構造方法來生成對象的彈性。
在Bean定義文件中,必須指定構造方法上參數的順序,如下
<bean id="hello" class="SpringDemo.HelloBean">
<constructor-arg index="0" value="codingliyi" />
<constructor-arg index="1" value="Hello" />
</bean>
建議使用Setter構造方法。有時需要隱藏某屬性的Setter方法時(如使該屬性變為只讀或私有),可使用Constructor Injection。
3. 屬性綁定
如某個Bean實例只被某個屬性參考一次,之那么可以在屬性定義時使用Bean標簽,并僅需指定其class屬性即可。如
1
<property name="date">
2
<bean class="java.util.Date"/>
3
</property>
Spring也支持隱式的自動綁定。
1
<bean id="helloBean" class="SpringDemo.HelloBean" autowire="byType|byName|constructor|autodetect" dependency-check="simple|objects|all|none"/>
若使用byType無法完成綁定,則拋出異常;
若使用byName無法完成綁定,則對應得Setter僅維持未綁定狀態;
若使用constructor,Spring容器會試圖比較對應容器中的Bean實例類型,及相關構造方法上的參數類型;
若使用autodetect,Spring首先嘗試constructor,不行的話再嘗試byType。
dependency-check指定依賴檢查的方式,默認為none。如進行依賴檢查時發現有未完成的伊拉關系,則執行時會跑出異常。
4. 集合類屬性的注入。
<property name="list|set">
<list|set>
<value>hello</value>
<ref bean="helloBean"/>
</list|set>
</property>
<property name="map">
<map>
<entry key="name" value="codingliyi" />
<entry key="person">
<ref bean="codingliyi"/>
</entry>
</map>
</property>
<property name="properties">
<props>
<prop key="name">codingliyi</prop>
</props>
</property>
5. Bean的生命周期
使用BeanFactory來生成及管理Bean實例時:
Bean的建立:讀取Bean定義文件,生成Bean的實例。
屬性注入:執行相關Bean屬性的依賴注入。
BeanNameAware的setBeanName():
如Bean實現了BeanNameAware接口,則執行。
BeanFactoryAware的setBeanFactory():
如Bean實現了BeanFactoryAware接口,則執行。
BeanPostProcessors的processBeforeInitialization():
如任何BeanPostProcessors實例與Bean實例關聯,則執行。
InitializingBean的afterPropertiesSet()
如Bean實現了InitializingBean接口,則執行。
Bean定義文件中定義的init-method
如定義了init-method,則執行設定的方法名稱。
BeanPostProcessors的processAfterInitialization():
如任何BeanPostProcessors實例與Bean實例關聯,則執行。
DisposableBean的destory()
如Bean實現了DisposableBean接口,則執行。
Bean定義文件中定義的destory-method
如定義了destory-method,則執行設定的方法名稱。
若使用ApplicationContext,在執行setBeanFactory()后,若bean有實現ApplicationContextAware接口,則執行setApplicationContext(),接著再繼續執行之后的流程。
6. Bean高級管理
Aware接口
有時為了善用Spring所提供的一些功能,必須讓Bean知道Spring容器管理的一些細節,或者讓它知道BeanFactory,ApplicationContext的存在。
Spring中提供了一些Aware接口。如BeanNameAware,BeanFactoryAware,ApplicationContextAware。
當Bean實現了上述接口后,在依賴關系設定完成之后,初始化方法之前,Spring容器會注入對應的實例。
(在暑期實習中做的那個Flex項目,需要在Action層使用ApplicationContext得到服務層組件。當時使用的是直接在代碼中用new創建。其實現在看來可以實現ApplicationContextAware接口)
BeanPostProcessor接口
1
public interface BeanPostProcessor
{
2
public Object postProcessAfterInitialization(Object bean, String name)throws BeansException;
3
4
public Object postProcessBeforeInitialization(Object bean, String name)throws BeansException;
5
}
6
}
例如將注入的String改為大寫,代碼如下:
1
public Object postProcessBeforeInitialization(Object bean, String name)
2
throws BeansException
{
3
Field[] fields = bean.getClass().getDeclaredFields();
4
for(Field field : fields)
{
5
if(field.getType().equals(String.class))
{
6
try
{
7
String original = (String)field.get(bean);
8
field.set(bean, original.toUpperCase());
9
} catch (IllegalArgumentException e)
{
10
e.printStackTrace();
11
} catch (IllegalAccessException e)
{
12
e.printStackTrace();
13
}
14
}
15
}
16
return bean;
17
}
7. 資源、消息、事件
ApplicationContext除了具備如BeanFactory基本的容器管理功能之外,并支持更多應用框架的特性,像是資源的取得、消息解析、事件的處理與傳播。
資源的取得:ApplicationContext繼承了ResourceLoader接口,可使用getResource()方法并指定資源文件的URL來取得一個實現Resource接口的實例。
Resource resource = context.getResource("classpath:admin.properties");
也可以指定其他標準的URL,如file:或http:等。
在Spring應用程序執行期間,ApplicationContext本身就會發布一連串的事件,而所有發布的時間都是抽象類ApplicationEvent的子類。
ApplicationContext會在ApplicationEvent發布時通知實現了ApplicationListener的Bean類實例。
如果打算發布事件通知ApplicationListener的實例,則可使用ApplicationContext的publishEvent()方法。