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