注明:這篇文章一是當成學習筆記,二是給大家提供另一個快速理解學習Spring的參考。歡迎留言討論,持續更新中~
(該部分是Spring的入門和Spring容器裝配管理Bean的方法)
第一章 開始Spring之旅
- Applet可以用來創建動態的Web應用,在html文件中通過<applet>標識,一種已經被淘汰的技術。
<applet code="Bubbles.class" width="350" height="350"> Java applet that draws animated bubbles. </applet>
POJO實質上可以理解為簡單的實體類,如果項目中使用了Hibernate框架,有一個關聯的***.hbm.xml文件,就可以使對象與數據庫中的表對應。
負責輕量級POJO開發就是Spring框架,由此引入Spring。。。
1.1 Spring是什么?
Spring是一個輕量級的DI和AOP容器框架,目標:高內聚、松耦合。
- DI(Dependency Injection,依賴注入):對象不是從容器中查找它的依賴類,而是容器在實例化對象的時候主動將它的依賴類注入給它。
- AOP(Aspect Oriented Programming,面向切面):通過將業務邏輯從應用服務中分離出來,實現了內聚開發。(比如:開發者可以先專注開發一個裸系統,然后再加上用戶系統,如Spring Security)
- Spring容器:可以通過配置來設定你的Bean是單一實例,還是每次請求產生一個實例,用容器去管理應用對象的生命周期和配置。
- scope="singleton"單實例模式,默認配置; scope="prototype" 每次請求都會創建一個實例。
- 配合Struts2時候要配置scope="prototype" ,因為Struts2中的Action是非單例的,每次請求都會創建一個新的Action,讓Struts2的Action由Spring創建,Spring默認創建出的對象都是單實例的,要加上scope="prototype",讓spring每次請求創建一個實例。
- 如果響應類中沒有成員變量,那么單例模式是線程安全的,否則需要對全局變量做保護,信號量控制。
<bean id="adTagFacade" class="outfox.ead.adbrand.domain.impl.AdTagFacadeImpl" scope="singleton"> <property name="adTagDao" ref="adTagDao" /> </bean>
1.2 開始Spring之旅
還是從Hello World!開始~
<bean id="greetingSerivice" class="com.springinaction.hello.GreetingServiceImpl"> <property name="greeting" value="Hello World!"/> </bean>
通過配置<bean>元素中的<property>設置屬性值,告訴Spring容器通過調用Bean的setGreetring方法設置其屬性值,(BeanFactory)factory.getBean("greetingService")的時候實例化類,同時注入greetring的值,最后打印出來。
1.3 理解依賴注入
耦合是一個雙頭怪物,一方面,緊密耦合的代碼難以測試,難以重用難以理解,帶來典型的“摧毀大堤”bug。另一方面,完全沒有耦合的代碼什么也做不了。耦合是必須的,但需要小心管理。
減少耦合的一個通常的做法是將具體實現隱藏在接口下面,這樣具體實現的替換不會影響到引用類。
DI的全部:簡言之,協調依賴對象之間合作的責任從對象自身中轉移出來。(通過XML配置,Spring容器去管理)
1.4 應用AOP
DI是軟件組件松散連接成為可能,AOP讓你能夠捕捉應用中經常使用的功能,把它轉化為可重用組件。
以下是將log轉化成了切面:
<aop:config> <aop:aspect ref="log"> //聲明一個切面,log提供了該切面的功能,這里是一個普通的bean。 <aop:pointcut id="pointCut" expression="excution(*.doTask(..))"/> //切面的切入點,執行doTask可以出發該切入點 <aop:before method="doTaskBefore" pointcut-ref="pointCut"/> //log下的doTaskBefore方法應該在切入點之前調用,下同 <aop:after-returning method="doTaskAfter" pointcut-ref="pointCut"/> </aop:aspect> </aop:config>
第2章 基本Bean裝配
2.1 容納你的Bean
- Spring的容器:Bean工廠 and 應用上下文
- BeanFactory:
常用的是根據XML文件中的定義裝載Bean,比如:
BeanFactory factory = new XMLBeanFactory(new FileSystemResource("c:/beans.xml"));
Bean是被延遲載入到Bean工廠中的,就是說Bean工廠會立即把Bean定義信息載入進來,但是Bean只有在被需要的時候才被實例化。為了從BeanFactory得到一個Bean,只要簡單的調用getBean()方法。
MyBean myBean = (MyBean) factory.getBean("myBean");
工廠就會實例化Bean並且使用依賴注入設置Bean的屬性。
-
- 應用上下文:
也有諸多實現,比如ClassPathXmlApplicationContext
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
應用上下文ApplicationContext接口擴展自BeanFactory接口,一個重要的區別是關於單實例Bean是如何被載入的。應用上下文會在上下文啟動后預載入所有的單實例Bean。通過預載入單實例Bean,確保當你需要的時候它們已經准備好了,你的應用不需要等待它們被創建。
- 注意區別:應用上下文比起Bean工廠提供了一些附加功能,幾乎所有的應用系統都選擇ApplicationContext而不是BeanFactory,只有在資源很少的情況下,才會考慮采用Beanfactory,如在移動設備上。
2.2 創建Bean
兩種方式:
- 通過構造函數注入依賴,優點少些了Setter代碼,強制使用強依賴契約,因為如果沒有提供所有需要的依賴,一個Bean就無法被實例化了。
<bean id="duke" class="com.springinaction.springidol.ProticJuggler"> <constructor-arg value="15"/> <constructor-arg ref="sonnet29"/> </bean>
- Setter方法注入依賴,優點當依賴很多的時候,比較靈活。利於自身的繼承。因為構造函數是不能被繼承的。子類只能調用super()方法給父類的私有變量賦值。(推薦)
<bean id="duke" class="com.springinaction.springidol.ProticJuggler"> <property name="beanBags" value="15"> </bean>
2.3 注入Bean屬性
- 內部Bean,一種注入依賴Bean的方式,但是無法復用,內部Bean只用於注入,且不能被其他Bean所引用。
一般寫法: <bean id="piano" class="com.springinaction.springidol.Piano"/> <bean id="kenny" class="..."> <property name="instrument" ref="piano"> </bean> 若要用內部類如下: <bean id="kenny" class="..."> <property name="instrument"> <bean class="om.springinaction.springidol.Piano"> </property> </bean>
- Spring配置基本類型(通過value)和其他類(通過ref),但value和ref只有在屬性是單一的時候才有效。那么當屬性是復數(就是集合)時,Spring怎么做呢?
- Spring裝配支持的集合類型:<list> <set> <map> <props>,常用<list>,<map>
List:屬性可重復,對應java里的collection,常用 <property name="instruments"> <list> <ref bean="guitar"> <ref bean="cymbal"> </list> </property> Set:屬性不重復 <property name="instruments"> <set> <ref bean="guitar"> <ref bean="cymbal"> <ref bean="cymbal"> //自動忽略重復的屬性 </set> </property> Maps:key-value的映射,常用 <property name="instruments"> <map> <entry key="GUITAR" value-ref="guitar"> <entry key =......> </map> </property> Properties:配置String-to-String的映射 <property name="instruments"> <props> <prop key="GUITAR">STRUM STRUM STRUM</prop> <prop key="CYMBAL">CRASH CRASH CRASH</prop> </props> </property>
2.4 自動裝配
四種自動裝配的類型,byName byType constructor autodetect,自動裝配雖然節省了XML代碼,但是也帶來了很多問題,比如屬性名字設計時候相當小心,重構代碼時候要連帶修改,自動裝配的最大缺點就是缺乏透明,一般不采用這個Spring IOC的自動裝配特性。
2.5 控制Bean創建
初始化和銷毀Bean,兩種方式:
- 在聲明<bean>的時候加上init-method和destroy-method參數,指定在Bean初始化的時候調用的方法。(推薦使用)
- java實現類的時候,實現InitializingBean和DisposableBean的afterPropertiesSet和destory方法,spring會允許它們勾入到Bean的生命周期中。(缺點:應用Bean和Spring API相互耦合)
第三章 高級Bean裝配
3.1Spring配置實現繼承
為了實現Bean的繼承,<bean>元素提供了兩個特殊屬性:
- parent:指明Bean的id。他對於<bean>的作用就相當於關鍵字extents對於Java類的作用。
- abstract:如果設置為true,就表示<bean>聲明是抽象的,不能被Spring實例化。
<bean id="baseBean" class="..." abstract="true"> <property name="" ref="" /> </bean> <bean id="childBean" parent="baseBean" />
3.2 方法注入(不常用)
- 基本方法替換<replaced-method>
- 獲取器注入<lookup-method>
把一個方法(一般是抽象的)聲明為返回某種類型的Bean
public abstract Instrument getInstrument(); xml配置覆蓋: <lookup-method name="getInstrument" bean="guitar"> //這就類似如下的java代碼 publc Instrument getInstrument() { ApplicationContext ctx = ...; return (Instrument) ctx.getBean("guitar"); }
3.3腳本化Bean(Ruby Groovy BeanShell)
比如Ruby的實現:
<lang:jruby id="lime" script-source="classpath:com/springinaction/scriptring/Lime.rb" script-interfaces="com.springinaction.scripting.Lime" />
使用腳本實現特定代碼,而不是用靜態編譯的Java編寫它的一個最大好處在於,我們可以隨時修改它,而不必重新編譯或重新部署程序。當然取決於Spring刷新腳本的頻率refresh-check-delay