Spring 學習筆記
Spring的疑問:
一、爲什麼需要Spring,其定位是什麼?
二、Spring有什麼好處,如何實現的?
三、怎麼使用Spring?
Spring實現的優勢:
無需實現框架指定的接口。Spring依賴注入,減少代碼間的依賴,便於重用。
類與類之間的聯繫傳統方法通過構造輔助類來完成。Spring則將輔助類的事物從代碼中剝離到配置文件中
IOC(Inverse of control):
配置文件聯繫類與類,優勢之一就是IOC。IoC,用白話來講,就是由容器控制程序之間的關係,而非傳統實現中,由程序代碼直接操控。這也就是所謂“控制反轉”的概念所在:控制權(其實是依賴關係)由應用代碼中轉到了外部容器,控制權的轉移,是所謂反轉。
DI(Dependency Injection):
所謂依賴注入,即組件之間的依賴關係由容器在運行期決定,形象的來說,即由容器動態的將某種依賴關係注入到組件之中。
概念:反向控制(IoC)和依賴注入(DI)
一般來說,IoC和DI代表同一個意思,就是:模塊不依賴框架,而是框架反向依賴模塊;模塊不主動去取得它所依賴的服務,而是由框架主動將它所需要的服務“注入”到模塊中。
還是有點繞,配合圖解來說明。“反”是對模塊而言,正向的直接依賴,變成了對目標對象的上層的依賴,變得更爲靈活。至於如下Copier的建立,則在構造中傳入兩個對象實例即可。
From 《ioc從過去到現在》@baobao
“反向”還體現在開發順序上。在非DIP的例子中,下層模塊必須先於上層模塊完工,否則上層模塊將無法編譯。但是即便是這樣,總得有人將兩邊串聯起來。比如:在main中設置:
Copier copy = Copier(new KeboardReader(), new DisplayWriter())
這樣一來,還是回到了開始的時候,依賴關係只是遷移到了main裏。如果使用反射機制(反射的好處就是不用寫明類名,將類名寫到string中,這個可以放到文件裏):
Class readerClass = Class.forName("KeyboardReader");
Class writerClass = Class.forName("DisplayWriter");
Copier copier = new Copier((Reader) readerClass.newInstance(),
(Writer) writerClass.newInstance());
這樣一來就是把依賴關係從編譯時刻遷移到了運行時(進一步就是類似反射機制,使用文件配置,這樣就可以脫離類)。使用Spring怎麼做(reader、writer配置在bean中):
Copier copier = new Copier((Reader) BeanFactory.getBean("reader"),
(Writer) BeanFactory.getBean("writer"));
也有問題:這種模式有什麼問題呢?最大的問題就是Copier必須依賴BeanFactory(或者Service Locator或其它名稱)的引用。由於BeanFactory是屬於框架的,因此這種耦合並不會增加應用程序模塊之間的耦合性。在許多情況下,模塊對框架的這種依賴性並不會帶來什麼問題。然而,如果我們希望我們的模塊能夠工作在許多不同的環境下,而不僅僅是工作於指定框架下,那麼這種對框架的依賴性可能成爲一種阻礙。
在Spring的實際使用中,不只是這樣,類的構造也在託付給String,就是說直接調用beanFactory.getBean(“Copier”)可以得到你配置的實例。
<beans> <bean id="reader" class="KeyboardReader"/> <bean id="writer" class="DisplayWriter"/> <bean id="copier" class="Copier"> <constructor-arg ref="reader"/> <constructor-arg ref="writer"/> </bean> </beans>
這一次,依賴關係從main中終於脫離開來,直接寫入到了配置文件中,從模塊對框架的依賴,編程了框架也對模塊依賴(相互依賴?),這種框架倒過來依賴模塊的行爲就是IOC,控制反轉。
物極則反
事物的另一方面是,IoC框架在簡化了程序編寫的同時,大大複雜化了模塊的配置。各IoC框架的主要不同點,幾乎就在於如何配置模塊,如何讓框架知曉模塊之間的依賴關係。當配置的複雜程序達到一定程度時,其配置的難度、維護的複雜度會不亞於直接寫一段程序。IoC框架的最大問題也在於此。
Tip:
程序運行中,實例該在哪裏產生?Spring由容器管理這些實例,在運行中生成;傳統方法在類中生成實例,對外提供接口,這些差別帶來的影響有多大?Spring有多少優勢?
Spring的對象注入通過接口(如HttpServletRequest的doGet、doPost傳遞的request、response)、設值(seTter方法)、構造(在創立時注入),這些方式保證的是:由容器(Spring)來管理這些類的對象的創建。通過注入的方式調用功能。
無侵入性(Reflection機制):
Spring的設計時成爲一個高度擴展的無侵入框架,無需代碼中涉及Spring的專有類,即可將其納入Spring管理。EJB則設定了許多的接口與編碼規範,造成的後果是對代碼的遷移,重構都有很大苦難。
注意<value></value>代表一個空字符串,如果需要將屬性值設定爲null,必須使用<null/>節點。
org.springframework.beans 和org.springframework.context是spring IoC容器的基礎。
org.springframework.beans.factory.BeanFactory初始化和配置Bean,裝配它們之間的依賴關係。
ApplicationContext 是擴展了BeanFactory ,提供了更多的功能,在使用中Spring作者推薦是使用ApplicationContext。
幾個重要的知識點:
(1) spring注入的方式:
Setter注入和構造器注入。如果採用Setter方法注入,別忘了在類定義的時候加上setter方法,這一點開發者常常會忘掉,導致NullPointerException。
(2) 自動裝配:
Spring提供多種自動裝配的功能,其中byName是最好用、最常用的。可以大大簡化bean的定義。例子
<beans default-autowire="byName">
比如我們定義了一個bean 類,它包含了master屬性,並且含有setMaster方法,那麼spring會自動在IoC容器中尋找名爲master的bean,然後調用setMaster將master對象賦值給master。
(3)Bean的作用域
Singleton
Spring容器只存在一個共享的bean實例,默認的配置爲TRUE
<bean id="example" class="com.yz.bean.Example"/>
Prototype每次對bean的請求都會創建一個新的bean實例。
<bean id="example" class="com.yz.bean.Example" singleton=”false”/>
兩者如何選擇?原則:所有有狀態的bean都使用Prototype作用域,而對無狀態的bean則應該使用singleton作用域。
(4) 熟悉Spring的配置文件xml中bean的定義,有助於我們靈活地定義bean。
Bean的定義的格式基本如下:
<bean id="example" class="com.yz.bean.Example"/>
id唯一標識該bean,要使用某個bean,只需在容器中通過id獲得之,就可以獲得它的一個實例。
Bean的屬性定義如:<properyt name=””>
其屬性值可以爲:
l 其它bean:
ref bean=
ref local=
l 集合
<list> <set> <map> <props>
l 數字,字符串。
(6) bean定義中的繼承關係,使用這種繼承關係同樣可以簡化bean的定義,實際開發中經常被使用。
如果你希望你的java bean的實例能繼承另外一個java bean的配置信息。那麼你在定義該bean的時候要使用parent屬性。
<bean id="childExample" class="com.yz.bean.ChildExample" parent="example"></bean>
儘管這兩個bean實際上沒有類的繼承關係,也可以使用parent屬性。這種parent屬性的方式實際上提供了模板的形式。
(7) PropertyPlaceholderConfigure
用途:將spring中分佈在不同的xml文件的一些屬性值統一到一個文件來維護。這樣可以提高我們程序的維護性,我們在實際開發中經常使用它。
<bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="/com/yz/bean/common.properties"/> </bean> <bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>