點擊進入我的博客
1 Spring容器與Bean配置信息
Bean配置信息
Bean配置信息是Bean的元數據信息,它由一下4個方面組成:
- Bean的實現類
- Bean的屬性信息,如數據庫的連接數、用戶名、密碼。
- Bean的依賴關係,Spring根據依賴關係配置完成Bean之間的裝配。
- Bean的行爲信息,如生命週期範圍及生命週期各過程的回調函數。
Bean元數據信息
Bean元數據信息在Spring容器中的內部對應物是一個個BeanDefinition形成的Bean註冊表,Spring實現了Bean元數據信息內部表示和外部定義之間的解耦。
Spring支持的配置方式
Spring1.0僅支持基於XML的配置,Spring2.0新增基於註解配置的支持,Spring3.0新增基於Java類配置的支持,Spring4.0則新增給予Groovy動態語言配置的支持。
2 基於XML的配置
2.1 理解XML與Schema
<?xml version="1.0" encoding="utf-8" ?>
<beans (1)xmlns="http://www.springframework.org/schema/beans"
(2)xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:(3)context=(4)"http://www.springframework.org/schema/context"
xsi:(5)schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
- (1)處是默認命名空間,無命名空間前綴的元素屬於默認命名空間。
- (2)xsi標準命名空間,用於指定自定義命名空間的Schema文件
- (3)自定義命名空間的簡稱,可以任意命名
- (4)自定義命名空間的全稱,必須在xsi命名空間爲其指定空間對應的Schema文件,可以任意命名,習慣上用文檔發佈機構的相關網站目錄。
- (5)爲每個命名空間指定Schema文件位置,
詳解xmlns
- 定義:xml namespace的縮寫,可譯爲“XML命名空間”。
- 作用:防止XML文檔含有相同的元素命名衝突,如<table>既可以表示表格,又可以表示桌子。如果增加了命名空間如<table>和<t:table>就可以使兩者區分開來。
- 使用:
xmlns:namespace-prefix="namespaceURI"
,其中namespace-prefix爲自定義前綴,只要在這個XML文檔中保證前綴不重複即可;namespaceURI是這個前綴對應的XML Namespace的定義。
理解xsi:schemaLocation
xsi:schemaLocation定義了XML Namespace和對應的 XSD(Xml Schema Definition)文檔的位置的關係。它的值由一個或多個URI引用對組成,兩個URI之間以空白符分隔(空格和換行均可)。第一個URI是定義的 XML Namespace的值,第二個URI給出Schema文檔的位置,Schema處理器將從這個位置讀取Schema文檔,該文檔的targetNamespace必須與第一個URI相匹配。例如:
<beans
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
這裏表示Namespace爲http://www.springframework.or...://www.springframework.org/schema/context/spring-context.xsd。
2.2 使用XML裝配Bean
直接裝配Bean
<!--id不可以配置多個: context.getBean("myBean1, myBean2")-->
<bean class="com.ankeetc.spring.MyBean" id="myBean1, myBean2"/>
<!--context.getBean("myBean1") == context.getBean("myBean2")-->
<bean class="com.ankeetc.spring.MyBean" name="myBean1, myBean2"/>
- id:用於表示一個Bean的名稱,在容器內不能重複;不可以配置多個id。
- name:用於表示一個Bean的名稱,在容器內不能重複;可以配置多個名稱,用
,
分割;id和name可以都爲空,此時則可以通過獲取全限定類名來獲取Bean。 - class:全限定類名
靜態工廠方法裝配
- 靜態工廠無需創建工廠類示例就可以調用工廠類方法。
- factory-method:工廠方法
public class MyBeanFactory {
public static MyBean createMyBean() {
return new MyBean();
}
}
<bean id="myBean" class="com.ankeetc.spring.MyBeanFactory" factory-method="createMyBean"/>
非靜態工廠方法裝配
- 非靜態工廠方法必須首先定義一個工廠類的Bean,然後通過factory-bean引用工廠類實例。
- factory-bean:指向定義好的工廠Bean
public class MyBeanFactory {
public MyBean createMyBean() {
return new MyBean();
}
}
<bean id="myBeanFactory" class="com.ankeetc.spring.MyBeanFactory"/>
<bean id="myBean" factory-bean="myBeanFactory" factory-method="createMyBean"/>
Bean的繼承和依賴
- parent:通過設置<bean>標籤的parent屬性,子<bean>將自動繼承父<bean>的配置信息。
- depends-on:通過設置<bean>標籤的depends-on屬性,Spring允許顯示的設置當前Bean前置依賴的Bean,確保前置依賴的Bean在當前Bean實例化之前已經創建好。
自動裝配autowire
<beans>元素提供了一個default-autowire屬性可以全局自動匹配,默認爲no。<bean>元素提供了一個指定自動裝配類型的autowire屬性,可以覆蓋<beans>元素的default-autowire屬性,該屬性有如下選項:
自動裝配類型 | 說明 |
---|---|
no | 顯式指定不使用自動裝配。 |
byName | 如果存在一個和當前屬性名字一致的 Bean,則使用該 Bean 進行注入。如果名稱匹配但是類型不匹配,則拋出異常。如果沒有匹配的類型,則什麼也不做。 |
byType | 如果存在一個和當前屬性類型一致的 Bean ( 相同類型或者子類型 ),則使用該 Bean 進行注入。byType 能夠識別工廠方法,即能夠識別 factory-method 的返回類型。如果存在多個類型一致的 Bean,則拋出異常。如果沒有匹配的類型,則什麼也不做。 |
constructor | 與 byType 類似,只不過它是針對構造函數注入而言的。如果當前沒有與構造函數的參數類型匹配的 Bean,則拋出異常。使用該種裝配模式時,優先匹配參數最多的構造函數。 |
default | 根據 Bean 的自省機制決定採用 byType 還是 constructor 進行自動裝配。如果 Bean 提供了默認的構造函數,則採用 byType;否則採用 constructor 進行自動裝配。 |
通過util命名空間配置集合類型的Bean
<util:list></util:list>
<util:set></util:set>
<util:map></util:map>
2.3 使用XML依賴注入
屬性配置
- Bean有一個無參數的構造器
- 屬性有對應的Setter函數
- 屬性命名滿足JavaBean的屬性命名規範
<bean class="com.ankeetc.spring.MyBean" id="myBean">
<property name="prop" value="prop"/>
</bean>
構造方法
- constructor-arg中的type和index可以沒有,只要能保證可以唯一的確定對應的構造方法即可
- type中基本數據類型和對應的包裝類不能通用
- 循環依賴:如果兩個Bean都採用構造方法注入,而且都通過構造方法入參引用對方,就會造成循環依賴導致死鎖。
<bean class="com.ankeetc.spring.MyBean" id="myBean">
<constructor-arg type="java.lang.String" index="0" value="abc"/>
<constructor-arg type="int" index="1" value="10"/>
</bean>
2.4 注入參數
字面值
- 基本數據類型及其封裝類、String都可以採取字面值注入。
- 特殊字符可以使用
<![CDATA[]]>
節或者轉義序列
引用其他Bean
<ref>元素可以通過以下三個屬性引用容器中的其他Bean:
- bean:通過該屬性可以引用同一容器或父容器的Bean,這是最常見的形式。
- local:通過該屬性只能引用同一配置文件中定義的Bean,它可以利用XML解析器自動檢驗引用的合法性,以便在開發編寫配置時能夠及時發現並糾正配置的錯誤。
- parent:引用父容器中的Bean,如<ref parent="car">的配置說明car的Bean是父容器中的Bean。
內部Bean
- 內部Bean只會被當前Bean引用,不會被容器中其他的Bean引用
- 內部Bean即使提供了id、name、scope也會被忽略,Scope默認爲prototype類型。
<bean id="prop" class="com.ankeetc.spring.Prop">
<property name="value" value="1314"/>
</bean>
<bean id="myBean" class="com.ankeetc.spring.MyBean">
<property name="prop">
<!--內部Bean即使提供了id、name、scope也會被忽略-->
<bean id="prop" class="com.ankeetc.spring.Prop">
<property name="value" value="520"/>
</bean>
</property>
</bean>
null值
- 使用
<null/>
代表null值
級聯屬性
- Spring支持級聯屬性如
prop.value
,而且支持多層級聯屬性 - 級聯屬性必須有初始值,否則會拋出
NullValueInNestedPathException
public class MyBean {
// 必須初始化
private Prop prop = new Prop();
public Prop getProp() {
return prop;
}
public void setProp(Prop prop) {
this.prop = prop;
}
}
<bean id="myBean" class="com.ankeetc.spring.MyBean">
<property name="prop.value" value="1314"/>
</bean>
集合類型屬性
- List、Set、Map:通過<list><set><map><entry>等標籤可以設置List、Set、Map的屬性
- Properties:可以通過<props><prop>等標籤設置Properties的屬性,Properties屬性的鍵值都只能是字符串。
- 集合合併:子Bean可以繼承父Bean的同名屬性集合元素,並且使用merge屬性選擇是否合併,默認不合並。
<bean id="parentBean" class="com.ankeetc.spring.MyBean">
<property name="list">
<list>
<value>1314</value>
</list>
</property>
</bean>
<bean id="myBean" class="com.ankeetc.spring.MyBean" parent="parentBean">
<property name="list">
<list merge="true">
<value>520</value>
</list>
</property>
</bean>
2.5 多配置文件整合
- 可以通過ApplicationContext加載多個配置文件,此時多個配置文件中的<bean>是可以互相訪問的。
- 可以通過XML中的<import>將多個配置文件引入到一個文件中,這樣只需要加載一個配置文件即可。
2.6 Bean的作用域
類型 | 說明 |
---|---|
singleton | 在Spring IoC容器中僅存在一個Bean實例,Bean以單實例的方式存在 |
prototype | 每次從容器中調用Bean時,都返回一個新的實例 |
request | 每次HTTP請求都會創建一個新的Bean,該作用域僅適用於WebApplicationContext環境 |
session | 同一個HTTP session共享一個Bean,不同的HTTP session使用不同的Bean,該作用域僅適用於WebApplicationContext環境 |
globalSession | 同一個全局Session共享一個Bean,一般用於Portlet環境,該作用域僅適用於WebApplicationContext環境 |
singleton作用域
- 無狀態或者狀態不可變的類適合使用單例模式
- 如果用戶不希望在容器啓動時提前實例化singleton的Bean,可以使用lazy-init屬性進行控制
- 如果該Bean被其他需要提前實例化的Bean所引用,那麼Spring將會忽略lazy-init的設置
prototype作用域
- 設置爲scope="prototype"之後,每次調用getBean()都會返回一個新的實例
- 默認情況下,容器在啓動時不會實例化prototype的Bean
- Spring容器將prototype的Bean交給調用者後就不再管理它的生命週期
Web應用環境相關的Bean作用域
見後續章節
作用域依賴的問題
見後續章節
3 FactoryBean
由於實例化Bean的過程比較負責,可能需要大量的配置,這是採用編碼的方式可能是更好的選擇。Spring提供了FactoryBean工廠類接口,用戶可以實現該接口定製實例化Bean的邏輯。當配置文件中<bean>的class屬性配置的是FactoryBean的子類時,通過getBean()返回的不是FactoryBean本身,而是getObject()方法所返回的對象,相當於是FactoryBean#getObject()代理了getBean()方法。
-
T getObject() throws Exception;
:返回由FactoryBean創建的Bean實例,如果isSingleton()返回的是true,該實例會放到Spring容器的實例緩存池中。 -
Class<?> getObjectType();
:返回該FactoryBean創建的Bean的類型 -
boolean isSingleton();
:創建的Bean是singleton的還是prototype
/**
* 實現,分割的方式配置 KFCCombo 屬性
*/
public class KFCFactoryBean implements FactoryBean<KFCCombo> {
private String prop;
public String getProp() {
return prop;
}
// 接受,分割的屬性設置信息
public void setProp(String prop) {
this.prop = prop;
}
// 實例化KFCCombo
public KFCCombo getObject() throws Exception {
KFCCombo combo = new KFCCombo();
String[] props = prop.split(",");
combo.setBurger(props[0]);
combo.setDrink(props[1]);
return combo;
}
public Class<?> getObjectType() {
return KFCCombo.class;
}
// true則放進容器緩存池,false則每次都調用getObject()方法返回新的對象
public boolean isSingleton() {
return false;
}
}
<bean id="combo" class="com.ankeetc.spring.KFCFactoryBean">
<property name="prop" value="ZingerBurger, PepsiCola"/>
</bean>
4 基於註解的配置
4.1 支持的註解
@Component:在Bean的實現類上直接標註,可以被Spring容器識別
@Repository:用於對DAO實現類進行標柱
@Service:用於對Service實現類進行標註
@Controller:用於對Controller實現類進行標註
4.2 掃描註解定義對Bean
Spring提供了一個context命名空間,用於掃描以註解定義Bean的類。
<!--生命context命名空間-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.ankeetc.spring"/>
</beans>
base-package屬性
- 指定一個需要掃描的基類包,Spring容器會掃描這個包下的所有類,並提取標註了相關注解的Bean。
resource-pattern屬性
- 如果不希望掃描base-package下的所有類,可以使用該屬性提供過濾
- 該屬性默認是
**/*.class
,即基包下的所有類
<context:exclude-filter>與<context:include-filter>
- <context:exclude-filter>:表示要排除的目標類
- <context:include-filter>:表示要包含的目標類
- <context:component-scan>可以有多個上述兩個子元素;首先根據exclude-filter列出需要排除的黑名單,然後再根據include-filter流出需要包含的白名單。
類別 | 示例 | 說明 |
---|---|---|
annotation | com.ankeetc.XxxAnnotation | 所有標註了XxxAnnotation的類。該類型採用目標類是否標誌了某個註解進行過濾。 |
assignable | com.ankeetc.XxService | 所有繼承或擴展XXXService的類。該類型採用目標類是否繼承或者擴展了某個特定類進行過濾 |
aspectj | com.ankeetc..*Service+ | 所有類名以Service結束的類及繼承或者擴展他們的類。該類型採用AspectJ表達式進行過濾 |
regex | com.ankeetc.auto..* | 所有com.ankeetc.auto類包下的類。該類型採用正則表達式根據目標類的類名進行過濾 |
custom | com.ankeetc.XxxTypeFilter | 採用XxxTypeFilter代碼方式實現過濾規則,該類必須實現org.springframework.core.type.TypeFilter接口 |
use-default-filters屬性
- use-default-filters屬性默認值爲true,表示會對標註@Component、@Controller、@Service、@Reposity的Bean進行掃描。
- 如果想僅掃描一部分的註解,需要將該屬性設置爲false。
<!-- 僅掃描標註了@Controller註解的類-->
<context:component-scan base-package="com.ankeetc.spring" use-default-filters="false">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
4.3 自動裝配
@Component
public class KFCCombo {
@Autowired
private PepsiCola cola;
@Autowired
private Map<String, Cola> colaMap;
@Autowired
private List<ZingerBurger> burgerList;
private ZingerBurger burger;
@Autowired
public void setBurger(@Qualifier(value = "zingerBurger") ZingerBurger burger) {
this.burger = burger;
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath:/beans.xml"});
KFCCombo combo = context.getBean(KFCCombo.class);
}
}
interface Cola {}
@Order(value = 1)
@Component
class CocaCola implements Cola {}
@Order(value = 2)
@Component
class PepsiCola implements Cola {}
@Component(value = "zingerBurger")
class ZingerBurger {
}
@Autowired註解
- 使用該註解可以按類型自動裝配對應的Bean
- 沒有找到對應的Bean則會拋出NoSuchBeanDefinitionException異常
- 使用required=false屬性可以設置即使找不到對應的Bean(即爲null)也不會拋出異常
- @Autowired可以對類成員變量及方法入參進行標註
@Quaifiler
- 如果容器中有一個以上匹配的Bean時,可以按照Bean名字查找對應的Bean
- @Quaifiler需要與@Autowired配合使用
對集合類進行標註
- 可以使用@Autowired對集合類進行標註,Spring會講容器中按類型匹配對所有Bean注入進來
- 可以使用@Order指定加載順序,值越小的越先加載
@Lazy延遲加載
- 可以使用@Lazy實現延遲加載,不會立即注入屬性值,而是延遲到調用此屬性對時候纔會注入屬性值。
@Resource和@Inject
- Spring支持JSR-250中@Resource註解和JSR-330的@Inject註解
- @Resource採用的是按照名稱加載的方式,它要求提供一個Bean名稱的屬性,如果屬性爲空,則自動採用標註處的變量名或方法名作爲Bean的名稱。
- @Inject是按照類型匹配注入Bean的。
- 由於這兩個註解功能沒有@Autowired功能強大,一般不需要使用。
4.4 Bean作用範圍及生命週期
- 註解配置的Bean默認作用範圍爲singleton,可以使用@Scope顯示指定作用範圍
- 可以使用@PostConstruct和@PreDestroy註解來達到init-method和destroy-method屬性的功能。
- @PostConstruct和@PreDestroy註解可以有多個
5 基於Java類的配置
5.1 @Configuration註解
- JavaConfig是Spring的一個子項目,旨在通過Java類的方式提供Bean的定義信息。
- 普通的POJO標註了@Configuration註解,就可以被Spring容器提供Bean定義信息。
- @Configuration註解本身已經標註了@Component註解,所以任何標註了@Configuration的類都可以作爲普通的Bean。
5.2 @Bean註解
- @Bean標註在方法上,用於產生一個Bean
- Bean的類型由方法的返回值的類型確定,Bean名稱默認與方法名相同,也可以顯示指定Bean的名稱。
- 可以使用@Scope來控制Bean的作用範圍。
5.3 啓動Spring容器
通過@Configuration類啓動Spring容器
- 可以直接設置容器啓動要註冊的類
- 可以向容器中註冊新的類,註冊了新的類要記得refresh
- 可以通過@Import將多個配置類組裝稱一個配置類
public class Main {
public static void main(String[] args) {
// (1)可以直接設置容器啓動要加載的類
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(DaoConfig.class);
// (2)可以向容器中註冊新的類
((AnnotationConfigApplicationContext) applicationContext).register(ServiceConfig.class);
// (3)註冊了新的類要記得refresh
((AnnotationConfigApplicationContext) applicationContext).refresh();
}
}
@Configuration
class DaoConfig {
@Bean
public String getStr() {
return "1314";
}
}
@Configuration
@Import(DaoConfig.class)
// (4)可以通過@Import將多個配置類組裝稱一個配置類
class ServiceConfig {
}
通過XML配置文件引用@Configuration的配置
標註了@Configureation的配置類本身也是一個bean,它可以被Spring的<context:component-scan>掃描到。如果希望將此配置類組裝到XML配置文件中,通過XML配置文件啓動Spring容器,僅在XML文件中通過<context:component-scan>掃描到相應的配置類即可。
<context:component-scan base-package="com.ankeetc.spring" resource-pattern="Config.class"/>
通過@Configuration配置類引用XML配置信息
在標註了@Configuration的配置類中,可以通過@ImportResource引入XML配置文件。
@Configuration
@ImportResource("classpath:beans.xml")
public class Config {
}