Core模塊主要的功能是實現了反向控制IOC(Inversion of Control)與依賴注入DI(Dependency Injection)、Bean配置以及加載。Core模塊中有Beans、BeanFactory、BeanDefinitions、ApplicationContext等幾個重要概念。
Beans為Spring里的各種對象,一般要配置在Spring配置文件中:BeanFactory為創建Beans的Factory,Spring通過BeanFactory加載各種Beans;BeanDefinition為Bean在配置文件中的定義,一般要定義id與class:ApplicationContext代表配置文件。
這些類都位於org.springframework.beans和org.springframework.context中。這是Spring最核心的包。Core模塊依賴於Spring的Core類庫。
BeanFactory工廠
BeanFactory是實例化、配置、管理眾多Bean的容器。這些Bean類一般是離散的,但在Spring中被配置為相互依賴。BeanFactory根據配置實例化Bean對象,並設置相互的依賴性。
BeanFactory可用接口org.springframework.beans.factory.BeanFactory表示。BeanFactory有多種實現,最常用的為org.springframework.beans.factory.xml.XmlBeanFactory。XmlBeanFactory能加載XML格式的配置文件。
實例化BeanFactory
在Web程序中用戶不需要實例化BeanFactory,Web程序加載的時候會自動實例化BeanFactory,並加載所有的Beans,將各種Bean設置到各個Servlet中、Struts的Action中、或者Hibernate資源中。開發者直接編寫Servlet、Action、Hibernate相關的代碼即可,無須操作 BeanFactory。
在Java桌面程序中,需要從BeanFactory中獲取Bean,因此需要實例化BeanFactory,構造函數的參數為配置文件的路徑。例如加載ClassPath下的配置文件可以用ClassPathResource加載,然后傳遞給XmlBeanFactory構造函數。代碼如下:
ClassPathResource res = new ClassPathResource("applicationContext.xml"); // 獲取配置資源 XmlBeanFactory factory = new XmlBeanFactory(res); // 獲取對象工廠 IService hello = (IService)factory.getBean("service"); // 獲取對象 ...// 其他代碼略 factory.destorySingletons(); // 銷毀對象
參數applicationContext.xml為ClassPath根目錄下的文件。applicationContext.xml為Spring默認的配置文件名稱,默認存儲在ClassPath根目錄下。或者使用文件流加載任意位置的配置文件,並傳遞給XmlBeanFactory構造函數,例如:
InputStream is = new FileInputStream("C:\\ApplicationContext.xml"); // 獲取配置資源 XmlBeanFactory factory = new XmlBeanFactory(is); // 獲取對象工廠 IService hello = (IService)factory.getBean("service"); // 獲取對象
或者用ClassPathXmlApplicationContext加載多個配置文件(多個配置文件以字符串數組形式傳入),並傳遞給XmlBeanFactory構造函數:
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml", "applicationContext-part2.xml"}); // 多個配置 BeanFactory factory = (BeanFactory)appContext; // ApplicationContext繼承自BeanFactory接口
XmlBeanFactory配置格式
一個BeanFactory中配置了多個Bean。在XmlBeanFactory中,配置文件的根節點為<beans>,里面定義了幾個<bean>子節點,每個<bean>定義一個Bean。格式如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!-- Beans根節點 --> <bean id="..." class="..."> <!--Bean節點,定義Bean--> ... </bean> <bean id="..." class="..."> <!--Bean節點,定義Bean--> <property name="..." value="..."></property> <!-- Property定義屬性 --> <property name="..." ref="..."></property> <!-- Property定義屬性 --> </bean> </beans>
配置Java Bean
一個BeanDefinition定義一個Bean,在XML中對應一個<bean>標記。Spring通過<bean>配置來實例化Bean,設置Bean的屬性,以及設置Bean之間相互的依賴性。
基本配置<bean>
一個<bean>通常需要定義id與class屬性。class屬性是必須的,如果有其它Bean引用了該Bean,則id屬性是必須的。Bean之間通過id屬性相互訪問,如:
<bean id="exampleBean" class="example.ExampleBean"></bean>
上面代碼等價於
ExampleBean exampleBean = new ExampleBean();
其中,對象名exampleBean相當於配置中的id屬性。
工廠模式factory-method
如果一個Bean不能通過new直接實例化,而是通過工廠類的某個方法建的,可以把class設為工廠類,用factory-method指向創建對象的法:
<!-- createInstance()是靜態方法 --> <bean id="exampleBean" class="example.MyFactoryBean" factory-method="createInstance" /> <!-- createInstance()是非靜態方法 --> <bean id="exampleBean2" factory-bean="myFactoryBean" factory-method="createInstance" />
構造函數<constructor-arg>
如果Bean的構造函數帶參數,那就需指定參數,用<constructor-arg>指定
<bean id="exampleBean" class="examples.ExampleBean"><!-- 定義Bean --> <constructor-arg><ref bean="anotherExampleBean" /></constructor-arg><!-- 構造函數參數 --> <constructor-arg><ref bean="yetAnotherBean" /></constructor-arg><!-- 構造函數參數 --> <constructor-arg><value>1</value></constructor-arg><!-- 構造函數參數 --> </bean>
每個<constructor-arg>的參數的順序要與構造函數相同。
public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public ExampleBean(AnotherBean beanOne, YetAnotherBean beanTwo, int i){ this.beanOne = beanOne; this.beanTwo = beanTwo; this.i = i; } }
單態模式singleton
Bean可以定義為單態模式(Singleton)。即在程序中只有一個實例,像數據源等一般設為單例。Spring默認是單態模式,如果想使用非單例模式(稱為Prototype模式),需把singleton屬性置為false:
<bean id="exampleBean" class="examples.ExampleBean" singleton="false" />
非單例下,每次請求該Bean,都會生成一個新的對象。
配置屬性<property>
Spring通過Bean的setter方法設置屬性。因此,需要由Spring注射的屬性一般都具有公共的setter,getter方法。例如下面的配置代碼:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/spring?characterEncoding=UTF-8" /> <property name="username" value="root" /> <property name="password" value="admin" /> </bean>
destroy-method屬性配置關閉方法。在銷毀對象時會調用該方法。
注意:
<!-- 將password設為空字符串 --> <property name="password"><value></value></property> <!-- 將password設為null,也可以不設置 --> <property name="password"><null /></property>
設置對象屬性<ref>
使用ref標簽配置Bean的id屬性使用。如:
<bean id="dao" class="com.helloweenvsfei.forum.dao.impl.DaoImpl"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <bean class="com.helloweenvsfei.forum.service.impl.ThreadServiceImpl"> <property name="dao" ref="dao"></property> </bean>
ref的內容為引用的Bean的id屬性。
也可使用內部配置(或者內聯配置),類似於JAVA中的匿名類對象。因為內部配置一般不會被其他的Bean引用,因此不需要配置內部Bean的id。例如:
<property name="dao">
<bean class="example.DaoImpl"></bean>
</property>
除了使用<ref>的bean屬性,還可以使用local、parent,它們與bean屬性的作用是一樣的。不同的是,local只能使用本配置文件中的bean,而parent只能使用父配置文件中的bean,但bean則沒有任何限制。
配置List屬性<list>
<list>配置java.util.List類型的屬性。list屬性中可配置任意類型對象,如果為java對象,使用ref指定,或者<bean>定義新實例,如果是普通類型如String,int,double等,直接用字符串即可。<list>中的元素會按配置的先后順序排序,例如:
<property name="someList"> <list> <value>String,Integer,Double,Boolean等類型對象</value> <ref bean="myDataSource" /> </list> </property>
配置Set屬性<set>
<set>配置java.util.Set類型的屬性。Set屬性中可配置任意類型對象。如果為java對象,則使用<ref>指定,或者使用<bean>重新定義新實例。如果是普通類型如String,int,double等,直接用字符串即可。例如:
<property name="someSet"> <set> <value>String,Integer,Double,Boolean等類型對象</value> <ref bean="myDataSource" /> <set> </property>
配置Map屬性<map>
<map>配置java.util.Map類型的屬性。<entry>配置Map里的元素,key指定索引,value指定值。如果為java對象,則使用ref指定,或者使用<bean>重新定義新實例,如果key為對象,,使用key-ref屬性,例如:
<property name="someMap"> <map> <entry key="yup an entry"> <value>just some string</value> </entry> <entry key-ref="myDataSource"> <ref bean="serviceImpl" /> </entry> </map> </property>
配置Properties屬性<props>
使用<props>與<prop>配置Properties屬性。<props/>配置一個Properties對象,<prop/>配置一條屬性,屬性key配置索引,例如:
<props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">false</prop> <prop key="hibernate.hbm2ddl.auto">create</prop> </props>
<idref>與<ref>的區別
<idref>與<ref>的作用是一樣的,都是配置java對象的。<idref>的用法也與<ref>基本相同。不同的是,<idref>只有bean與local屬性,沒有parent屬性,如
<idref local="dataSource" />
Spring加載XML配置文件時,會檢查<idref>配置的Bean存在不存在。而<ref>只會在第一次調用時才會檢查。換句話說,如果Bean不存在,<idref>能在啟動程序的時候就拋出錯誤,而<ref>只會在運行中拋出錯誤。
設置destory-method銷毀方法
有的對象(例如數據源,JDBC連接,輸入輸出流等)在使用完畢后需要執行close()方法釋放資源。可以使用destory-method配置。Spring在注銷這些資源時會調用destory-method里配置的方法。例如:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
..... </bean>
設置depends-on依賴對象
Spring默認按照配置文件里Bean配置的先后順序實例化Bean,但有時候在實例化A對象之前需要先實例化后面的B對象。這時候可以使用depends-on,強制先實例化B對象。如:
<bean id="a" class="examples.A" depends-on="b"></bean> <bean id="b" class="examples.B"></bean>
在實例化A的時候會檢查B的實例是否存在,如果不存在,先實例化B
初始化方法init-method
與destory-method相反,有的對象在實例化后需要執行某些初始化代碼,但這些初始化代碼不能寫在構造函數中。這時候可以把這部分初始化代碼寫在一個方法中,如init(),使用init-method屬性,強制Spring執行該方法進行初始化,如:
<bean id="c" class="example.C" init-method="init"></bean>
屬性自動裝配autowire
為了防止配置文件過大,可以使用autowire屬性
配置autowire自動裝配
使用<bean>的autowire屬性后,不需要再用<property name="" value="" />顯示地設置該Bean的屬性、依賴關系。Spring會根據反射,自動尋找符合條件的屬性,設置到該Bean屬性上。如果autowire設置為byType,將會按照屬性的類型自動匹配。如:
<bean id="d" class="examples.D" autowire="byType"></bean>
autowire取值范圍
autowire屬性定義的不是需要自動裝配的屬性名,而是自動裝配的規則。一但配置,所有的屬性都將遵循autowire定義的規則。autowire所有的取值以及意義見下表
如果顯示定義了<property>或者<constructor-arg>,會覆蓋默認裝配。自動裝配一般與下面的依賴檢查連用。
注意:在大型項目中不推薦使用,因為自動裝配會隱藏依賴裝配的細節。降低可讀性與可維護性,並可能帶來意想不到的麻煩。
依賴檢查dependency
有時候某些Bean的屬性配置有錯誤,比如某個屬性沒有設置。這種錯誤在程序啟動的時候不會有任何異常表現,會一直潛伏到Spring調用該Bean時才會被發現。為了防止這種情況,Spring提供依賴檢查,在程序啟動的時候檢查依賴配置。如果有錯誤,啟動時就會拋出異常,以便發現配置錯誤。
配置dependency依賴檢查
依賴檢查能夠檢查屬性是否被設置。如果配置了依賴檢查,程序啟動時會進行配置校驗,以便及時地發現配置錯誤。通過設置<bean>的dependency-check設置依賴檢查規則,如:
<bean id="bean" class="examples.Bean" dependency-check="all"></bean>
dependency屬性取值范圍
dependency屬性有多種取值,分別應對不同的情況。但是需要注意,dependency依賴檢查是很生硬的,例如設置為object,將會檢查所有的java對象屬性,只要有一個屬性沒有設置,就會拋出異常,即某屬性明明不需要設置,但是沒法避免dependency檢查,容易造成“一竿子全打死”的現象。dependency的取值以及意義見表
Bean的高級特性
Spring程序中,JAVA Bean一般與Spring是非耦合的,不會依賴於Spring類庫。這也是Spring的優點。但有時候Java Bean需要知道自己在Spring框架中的一些屬性。Spring提供了一些接口,實例化Java Bean對象后Spring會調用接口的方法。
BeanNameAware接口獲取Bean的id
BeanNameAware接口幫助Java Bean知道自己在配置文件中的id,實現BeanNameAware,實現方法名為setBeanName()方法,初始化該對象后Spring就會執行該回調方法,將id設置進來。Bean中設置一個變量,接受id名稱即可,例如:
package com.helloweenvsfei.spring.example; import org.springframework.beans.factory.BeanNameAware; public class WhatsTheNameBean implements BeanNameAware { private String beanName; public void setBeanName(String beanName) { this.beanName = beanName; } }
提示:setBeanName()方法的回調發生在所有參數被設置完之后,初始化方法(init-method屬性)被執行之前。
BeanFactoryAware接口獲取BeanFactory
BeanFactoryAware接口幫助java Bean知道哪個BeanFactory實例化了自己,BeanFactoryAware接口中有setBeanFactory的回調方法,初始化該對象后,會回調該方法,將BeanFactory傳遞進來。BeanFactoryAware接口的代碼如下:
public interface BeanFactoryAware{ void setBeanFactory(BeanFactory beanFactory) throws BeanException }
用法同BeanNameAware,實現了BeanFactoryAware接口的Java Bean能夠獲取到BeanFactory,從BeanFactory中能夠獲取到BeanFactory中配置的其他Java Bean。Spring不推薦這樣做,因為這樣會與Spring耦合。獲取其它Java Bean一般通過設置getter、setter方法,用依賴注入實現
InitializingBean接口執行初始化方法
實現了InitializingBean接口的Java Bean會在實例后、所有屬性被設置后調用初始化方法。但使用該接口會與Spring代碼發生耦合,因此不推薦使用。InitializingBean接口代碼如下:
public interface InitializingBean{ public void afterPropertiesSet(); }
Spring推薦使用init-method配置,效果等價:
<bean id="d" class="examples.D" init-method="afterPropertiesSet"></bean>
DisposableBean接口執行銷毀方法
實現了DisposableBean接口的Java Bean會在對象丟棄的時候調用銷毀方法。但使用該接口會與Spring代碼發生耦合,因此不推薦使用。DisposableBean接口代碼如下:
public interface DisposableBean{ void destory() throws Exception; }
Spring推薦使用destory-method配置,效果等價:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destory-method="close"></bean>
BeanFactory高級特性
如果Java Bean實現了BeanFactoryAware接口,就能夠獲得BeanFactory對象。BeanFactory有下面幾個常用的方法:
屬性覆蓋器
對於一些參數,更實用更簡單的方法是使用properties配置,而不是配置在Spring的配置文件中。Spring提供屬性替代配置,允許把某些屬性配置在properties文件中。
配置PropertyOverrideConfigurer屬性覆蓋器
PropertyOverrideConfigurer允許把XML配置里的某些參數配置到properties文件中。這在數據庫配置中很常見。配置時需要配置一個PropertyOverrideConfigurer對象,指定properties文件的位置,然后把替換的屬性用形如${jdbc.url}的字符串替代,如:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <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> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:jdbc.properties" />
</bean>
PropertyOverrideConfigurer對象會到指定名稱的properties文件(例如jdbc.properties)中,尋找屬性名為${}變量的配置,${}中最好不要有空格
properties配置
具體的數據庫配置是寫在jdbc.properties里面的。properties中的配置比applicationContext.xml中更便於閱讀、修改與維護。代碼如下:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/forum?characterEncoding=UTF-8
jdbc.username=root
jdbc.password="admin