本部分講述如何將 Bean 注入 Spring IoC容器中。
全部章節(jié)傳送門:
Spring學(xué)習(xí)筆記(一):Spring IoC 容器
Spring學(xué)習(xí)筆記(二):Spring Bean 裝配
Spring學(xué)習(xí)筆記(三): Spring 面向切面
Spring學(xué)習(xí)筆記(四): Spring 數(shù)據(jù)庫編程
Spring學(xué)習(xí)筆記(五): Spring 事務(wù)管理
依賴注入的方式
依賴注入可以分為3種方式:
- 構(gòu)造器注入。
- setter注入。
- 接口注入。
其中構(gòu)造器注入和 setter 注入是主要的方式。
構(gòu)造器注入
構(gòu)造器注入依賴于構(gòu)造方法的實(shí)現(xiàn),構(gòu)造方法可以是有參或者無參的。
創(chuàng)建一個(gè)實(shí)體類 Role 。
package com.wyk.springdemo.pojo;
public class Role {
private Long id;
private String roleName;
private String note;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public Role(String roleName, String note) {
this.roleName = roleName;
this.note = note;
}
}
在配置文件中添加 Bean 。其中 index 指定參數(shù)的位置,而 value 設(shè)置值。
<bean id="role1" class="com.wyk.springdemo.pojo.Role">
<constructor-arg index="0" value="總經(jīng)理" />
<constructor-arg index="1" value="公司管理者" />
</bean>
構(gòu)造器注入還是比較簡單的,缺點(diǎn)是當(dāng)參數(shù)較多時(shí),構(gòu)造方法比較復(fù)雜。
使用 setter 注入
setter 注入是 Spring 中最主流的注入方式,它會(huì)首先調(diào)用無參的構(gòu)造方法,然后使用 setter 注入為其設(shè)置對(duì)應(yīng)的值。
為前面的實(shí)體類添加一個(gè)無參構(gòu)造方法。
public Role() {}
然后在配置文件中添加 Bean 。
<bean id="role2" class="com.wyk.springdemo.pojo.Role">
<property name="roleName" value="高級(jí)工程師" />
<property name="note" value="重要人員" />
</bean>
接口注入
當(dāng)資源來自外界的時(shí)候,可以通過采用接口注入的方式獲取它,比如數(shù)據(jù)庫連接資源。
裝配 Bean 概述
下面講述如何將 Bean 裝配到 Spring IoC 容器中,常用的方法有3種:
- 在XML中顯示配置。
- 在 Java 的接口和類中實(shí)現(xiàn)配置。
- 隱式 Bean 的發(fā)現(xiàn)機(jī)制和自動(dòng)裝配原則。
在實(shí)際開發(fā)中,建議使用的方式是:
- 基于約定由于配置的原則,最優(yōu)先的應(yīng)該是通過隱式 Bean 的發(fā)現(xiàn)機(jī)制和自動(dòng)裝配原則。
- 在無法使用自動(dòng)裝配原則的情況下優(yōu)先考慮 Java 接口和類中實(shí)現(xiàn)配置。
- 最后考慮XML配置。
通過 XML 配置裝配 Bean
使用 XML 裝配 Bean 需要引入對(duì)應(yīng)的 XML 模式(XSD)。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
</beans>
裝配簡易值
看一個(gè)簡單的 Bean 。
<bean id="orangeJuice" class="com.wyk.springdemo.pojo.Drinks">
<property name="fruit" value="橙汁" />
<property name="sugar" value="少糖的" />
<property name="size" value="2" />
</bean>
簡單說明一下。
- id 屬性是 Spring 找到這個(gè) Bean 的編號(hào),不過 id 不是一個(gè)必需的屬性,如果沒聲明它,那么 Spring 將會(huì)采用 “全限定名#{number}”的格式生成編號(hào),例如上面的例子,如果沒有聲明 id , 那么它的編號(hào)就是“com.wyk.springdemo.pojo.Drinks#0”。
- class 是一個(gè)類全限定名。
- property定義類的屬性。
如果需要注入一些自定義的類,則可以通過 ref 屬性。
<bean id="orangeJuice" class="com.wyk.springdemo.pojo.Drinks">
<property name="fruit" value="橙汁" />
<property name="sugar" value="少糖的" />
<property name="size" value="2" />
</bean>
<bean id="juiceMaker" class="com.wyk.springdemo.pojo.JuiceMaker"
init-method="init" destroy-method="myDestroy">
<property name="beverageShop" value="pig" />
<property name="source" ref="orangeJuice" />
</bean>
裝配集合
本部分講述如何裝配復(fù)雜元素,比如Set、Map、List等。
創(chuàng)建一個(gè) Bean 。
package com.wyk.springdemo.pojo;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class ComplexAssembly {
private Long id;
private List<String> list;
private Map<String, String> map;
private Properties props;
private Set<String> set;
private String[] array;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public Set<String> getSet() {
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
public String[] getArray() {
return array;
}
public void setArray(String[] array) {
this.array = array;
}
public Properties getProps() {
return props;
}
public void setProps(Properties props) {
this.props = props;
}
}
在 XML 中裝配這個(gè)實(shí)體。
<bean id="complexAssembly" class="com.wyk.springdemo.pojo.ComplexAssembly">
<property name="id" value="1" />
<property name="list">
<list>
<value>value-list-1</value>
<value>value-list-2</value>
<value>value-list-3</value>
</list>
</property>
<property name="map">
<map>
<entry key="key1" value="value-key-1" />
<entry key="key2" value="value-key-2" />
<entry key="key3" value="value-key-3" />
</map>
</property>
<property name="props">
<props>
<prop key="prop1">value-prop-1</prop>
<prop key="prop2">value-prop-2</prop>
<prop key="prop3">value-prop-3</prop>
</props>
</property>
<property name="set">
<set>
<value>value-set-1</value>
<value>value-set-2</value>
<value>value-set-3</value>
</set>
</property>
<property name="array">
<array>
<value>value-array-1</value>
<value>value-array-2</value>
<value>value-array-3</value>
</array>
</property>
</bean>
簡單介紹一下集合類型的屬性如何裝配:
- List 屬性通過<list>元素進(jìn)行裝配,然后通過<value>元素設(shè)置值。
- Map 屬性通過<map>元素進(jìn)行裝配,然后通過<entry>元素設(shè)置值,只是entry包含一個(gè)key和一個(gè)value。
- Properties 屬性通過<props>元素進(jìn)行裝配,然后通過多個(gè)<prop>元素設(shè)置值,每個(gè)prop包含一個(gè)key。
- Set 屬性通過<set>元素進(jìn)行裝配,然后通過<value>元素設(shè)置值。
- 數(shù)組通過<array>元素進(jìn)行裝配,然后通過<value>元素設(shè)置值。
上面討論了各個(gè)集合的裝載,當(dāng)集合的元素是實(shí)體對(duì)象的時(shí)候,情況會(huì)更復(fù)雜。
首先創(chuàng)建2個(gè)實(shí)體類,一個(gè)是前面使用過的 Role 類,另一個(gè)是下面的 User 類。
package com.wyk.springdemo.pojo;
public class User {
private Long id;
private String userName;
private String note;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
然后創(chuàng)建一個(gè)復(fù)雜一些的實(shí)體類。
package com.wyk.springdemo.pojo;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class UserRoleAssembly {
private Long id;
private List<Role> list;
private Map<Role, User> map;
private Set<Role> set;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<Role> getList() {
return list;
}
public void setList(List<Role> list) {
this.list = list;
}
public Map<Role, User> getMap() {
return map;
}
public void setMap(Map<Role, User> map) {
this.map = map;
}
public Set<Role> getSet() {
return set;
}
public void setSet(Set<Role> set) {
this.set = set;
}
}
然后在 XML 文件中裝配實(shí)體。
<bean id="role1" class="com.wyk.springdemo.pojo.Role">
<constructor-arg index="0" value="總經(jīng)理" />
<constructor-arg index="1" value="公司管理者" />
</bean>
<bean id="role2" class="com.wyk.springdemo.pojo.Role">
<property name="roleName" value="高級(jí)工程師" />
<property name="note" value="重要人員" />
</bean>
<bean id="user1" class="com.wyk.springdemo.pojo.User">
<property name="id" value="1" />
<property name="userName" value="xiaoming" />
<property name="note" value="haha" />
</bean>
<bean id="user2" class="com.wyk.springdemo.pojo.User">
<property name="id" value="2" />
<property name="userName" value="xiaohong" />
<property name="note" value="hehe" />
</bean>
<bean id="userRoleAssembly" class="com.wyk.springdemo.pojo.UserRoleAssembly">
<property name="id" value="1" />
<property name="list">
<list>
<ref bean="role1"/>
<ref bean="role2"/>
</list>
</property>
<property name="map">
<map>
<entry key-ref="role1" value-ref="user1" />
<entry key-ref="role2" value-ref="user2" />
</map>
</property>
<property name="set">
<set>
<ref bean="role1"/>
<ref bean="role2"/>
</set>
</property>
</bean>
從上例可以看出:
- List 屬性通過<list>元素定義注入,然后通過多個(gè)<ref>元素的 Bean 屬性去引用定義好的 Bean。
- Map 屬性通過<map>元素定義注入,然后通過多個(gè)<entry>元素的key屬性去引用定義好的 Bean 作為鍵, value屬性去引用定義好的 Bean 作為值。
- Set 屬性通過<set>元素定義注入,然后通過多個(gè)<ref>元素的 Bean 屬性去引用定義好的 Bean。
命名空間裝配
Spring 還提供了對(duì)應(yīng)的命名空間的定義,只是在使用命名空間的時(shí)候需要先引入對(duì)應(yīng)的命名空間和 XSD 文件。
前面講到可以通過構(gòu)造器和 setter 進(jìn)行依賴注入,在有了p命名空間和c命名空間時(shí)我們可以簡單的把它們當(dāng)做 Bean 的一個(gè)屬性來進(jìn)行定義。先看一個(gè)例子。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<bean id="role1" class="com.wyk.springdemo.pojo.Role" c:_0="role_name_1"
c:_1="role_note_1"/>
<bean id="role2" class="com.wyk.springdemo.pojo.Role" p:id="2"
p:roleName="role_name_2" p:note="role_note_2"/>
</beans>
使用p命名空間和c命名空間時(shí)需要先聲明使用對(duì)應(yīng)的命名空間,即在beans元素上加入xmlns:p="http://www.springframework.org/schema/p" 和xmlns:c="http://www.springframework.org/schema/c"。
其中,使用c進(jìn)行構(gòu)造器注入,c:_0代表第一個(gè)參數(shù),以此類推,如果想引用其它 Bean,添加-ref即可,比如 c:_1-ref 。
使用p進(jìn)行 setter 注入,比如p:id="2",以2為值,使用的setId方法設(shè)置,同樣,如果引用其它 Bean , 使用-ref,比如 p:id-ref。
補(bǔ)充一下,如果在IDEA中報(bào)錯(cuò)URI is not registered,需要在File →Settings→Schemas and DTDs中添加命名空間的引用。
還可以使用util命名空間xmlns:util="http://www.springframework.org/schema/util" (對(duì)應(yīng)java.util包)引入集合類型。
<util:list id="userList" value-type="java.lang.String">
<value>張三</value>
<value>李四</value>
<value>王五</value>
</util:list>
通過注解裝配 Bean
通過注解的方式可以減少 XML 的配置,注解功能更為強(qiáng)大,它既能實(shí)現(xiàn) XML 的功能,也提供了自動(dòng)裝配的功能。符合“約定優(yōu)于配置”的開發(fā)原則。
Spring 中,有兩種發(fā)現(xiàn) Bean 的方式:
- 組件掃描。通過定義資源的方式,讓 Spring IoC 容器掃描對(duì)應(yīng)的包,從而把 Bean 裝配進(jìn)來。
- 自動(dòng)裝配。通過注解定義,使得一些依賴關(guān)系可以通過注解完成。
使用@Component裝配 Bean
首先定義一個(gè)實(shí)體類。
package com.wyk.beanDemo.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value="role")
public class Role {
@Value("1")
private Long id;
@Value("xiaoming")
private String roleName;
@Value("haha")
private String note;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
注意其中的注解。
- 注解 @Component 代表 Spring IoC 會(huì)把這個(gè)類掃描成 Bean ,其中的 value 屬性代表這個(gè)類在 Spring 中的 id 。也可以簡寫成 @Component("role") ,甚至直接寫成 @Component ,這時(shí)id會(huì)默認(rèn)成首字母小寫的類名。
- 注解 @Value 代表值的注入。
這時(shí)候還需要一個(gè) Java Config告訴 Spring 去哪里掃描 Bean 。
package com.wyk.beanDemo.pojo;
import com.wyk.beanDemo.service.impl.RoleServiceImpl;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
public class PojoScan {
}
需要注意的是, @ComponentScan 代表進(jìn)行掃描,默認(rèn)掃描的當(dāng)前的包,因此實(shí)體和配置所在的包必須一致。
最后,需要通過 Spring IoC 容器的實(shí)現(xiàn)類 AnnotationConfigApplicationContext 去生成容器。
package com.wyk.beanDemo;
import com.wyk.beanDemo.pojo.PojoScan;
import com.wyk.beanDemo.pojo.Role;
import com.wyk.beanDemo.service.RoleService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class mainApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(PojoScan.class);
Role role = context.getBean(Role.class);
System.err.println(role.getId());
}
}
上面的 @ComponentScan 只掃描當(dāng)前包路徑,有很大的局限。這里講一下它的2個(gè)配置項(xiàng):
- basePackages 。它的值是 Java 包的數(shù)組,Spring 會(huì)根據(jù)它的配置掃描對(duì)應(yīng)的包和子包。
- basePackageClasses 。它可以配置多個(gè)類,Spring 會(huì)根據(jù)配置的所在的包,為包和子包掃描裝配對(duì)應(yīng)配置的 Bean 。
創(chuàng)建一個(gè)接口。
package com.wyk.beanDemo.service;
import com.wyk.beanDemo.pojo.Role;
public interface RoleService {
public void printRoleInfo(Role role);
}
再創(chuàng)建一個(gè)接口的實(shí)現(xiàn)類, @Component 代表它是一個(gè) Spring 的 Bean 。
package com.wyk.beanDemo.service.impl;
import com.wyk.beanDemo.pojo.Role;
import com.wyk.beanDemo.service.RoleService;
import org.springframework.stereotype.Component;
@Component
public class RoleServiceImpl implements RoleService {
public void printRoleInfo(Role role) {
System.out.println(role.getId());
System.out.println(role.getRoleName());
System.out.println(role.getNote());
}
}
修改配置類。
package com.wyk.beanDemo.pojo;
import com.wyk.beanDemo.service.impl.RoleServiceImpl;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan(basePackageClasses = {Role.class, RoleServiceImpl.class})
//@ComponentScan(basePackages = {"com.wyk.beanDemo.pojo","com.wyk.beanDemo.service"})
public class PojoScan {
}
這里使用 basePackages 或者 basePackageClasses 均可,一般來說,basePackages 可讀性較好,優(yōu)先使用它;但在大量需要重構(gòu)的工程中,盡量不要使用 basePackages ,因?yàn)楹芏鄷r(shí)候修改包名需要反復(fù)配置,而IDE不會(huì)進(jìn)行提示,而使用 basePackageClasses 的話,IDE會(huì)報(bào)錯(cuò)提示,并且可以輕松處理這些錯(cuò)誤。
另外,建議只使用一個(gè) @ComponentScan 注解,因?yàn)槿绻捎枚鄠€(gè),Spring 會(huì)為每一個(gè) @ComponentScan 注解生成一個(gè)新的對(duì)象,這往往不是我們需要的。如果同一個(gè) @ComponentScan 注解定義重復(fù)的包或者存在其子包的定義,也不會(huì)配置生成多個(gè)對(duì)象。
進(jìn)行測試。
package com.wyk.beanDemo;
import com.wyk.beanDemo.pojo.PojoScan;
import com.wyk.beanDemo.pojo.Role;
import com.wyk.beanDemo.service.RoleService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class mainApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(PojoScan.class);
Role role = context.getBean(Role.class);
RoleService roleService = context.getBean(RoleService.class);
roleService.printRoleInfo(role);
}
}
自動(dòng)裝配 @Autowired
所謂自動(dòng)裝配技術(shù),是由 Spring 自己發(fā)現(xiàn)對(duì)應(yīng)的 Bean ,自動(dòng)完成裝配工作的方式,它會(huì)應(yīng)用到注解 @Autowired ,這時(shí)候 Spring 會(huì)根據(jù)類型去尋找定義的 Bean 然后將其注入。
創(chuàng)建一個(gè)接口 RoleService2 。
package com.wyk.beanDemo.service;
public interface RoleService2 {
void printRoleInfo();
}
這個(gè)接口采用了 Spring 推薦的接口方式,這樣更為靈活,因?yàn)槎x和實(shí)現(xiàn)進(jìn)行了分離,接下來創(chuàng)建實(shí)現(xiàn)類。
package com.wyk.beanDemo.service.impl;
import com.wyk.beanDemo.pojo.Role;
import com.wyk.beanDemo.service.RoleService2;
import org.springframework.beans.factory.annotation.Autowired;
public class RoleServiceImpl2 implements RoleService2 {
@Autowired
private Role role;
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
public void printRoleInfo() {
System.out.println(role.getId());
System.out.println(role.getRoleName());
System.out.println(role.getNote());
}
}
在這里使用了 @Autowired 注解, Spring 會(huì)按照類型找到定義的實(shí)例,將其注入。
IoC 容器有時(shí)候會(huì)尋找失敗,在默認(rèn)情況下尋找失敗會(huì)拋出異常,如果不希望拋出異常,可以通過配置項(xiàng) required 來改變它,設(shè)置為 @Autowired(required=false)。
@Autoeired 除可以配置在屬性上,還可以配置到方法上,常見的 Bean 的 setter 方法也可以使用它來完成注入。
@Autowired
public void setRole(Role role) {
this.role = role;
}
自動(dòng)裝配的歧義性
在一些情況下,一個(gè)接口可能會(huì)有多個(gè)實(shí)現(xiàn)類,這樣在注入的時(shí)候,會(huì)由于無法判斷使用哪個(gè)實(shí)現(xiàn)類,導(dǎo)致注入失敗。
為了消除歧義性,Spring 提供了2個(gè)注解 @Primary 和 @Qualifier 。
注解 @Primary 代表首要,當(dāng) Spring 通過一個(gè)接口或抽象類注入對(duì)象的時(shí)候,該注解會(huì)告訴 Spring 首先使用哪個(gè)類。
@Component("roleSerivce3")
@Primary
public class RoleServiceImpl3 implements RoleService {
}
如果有多個(gè)實(shí)現(xiàn)類都標(biāo)注了 @Primary , Spring 會(huì)拋出異常,@Primary只能解決首要性問題,無法解決選擇性問題。
@Qualifier 注解改為使用按照名稱查找的方法,而不是按照類型,這樣就可以消除歧義性。
public class RoleController {
@Autowired
@Qualifier("roleService3")
private RoleService roleService;
...
}
裝載帶有參數(shù)的構(gòu)造方法類
對(duì)于帶有參數(shù)的構(gòu)造方法,也允許我們直接通過注解進(jìn)行注入。
public RoleController(@Autowired RoleService roleService) {
this.roleService = roleService;
}
使用 @Bean 裝配 Bean
前面通過 @Component 裝配 Bean 只能注解到類上,注解 @Bean 可以注解到方法上,適用于第三方包引入不方便添加 @Component 的類。
@Bean(name="dataSource")
public DataSource getDataSource() {
...
}
@Bean 有4個(gè)配置項(xiàng):
- name: 是一個(gè)字符串?dāng)?shù)組,允許配置多個(gè) BeanName 。
- autowire: 標(biāo)志是否是一個(gè)引用的 Bean 對(duì)象,默認(rèn)值是 autowire.NO 。
- initMethod: 自定義初始化方法。
- destroyMethod: 自定義銷毀方法。
裝配的混合使用
在現(xiàn)實(shí)中,一般的使用方法是對(duì)自己工程中所開發(fā)的類盡量使用注解的方法,對(duì)于引入的第三方包或者服務(wù),盡量使用 XML 方式, 這樣的好處是可以盡量減少對(duì)第三方包或者服務(wù)細(xì)節(jié)的理解,也更加清晰和明朗。
在程序中,可以通過注解 @ImportResource引入 XML 文件,其中配置的內(nèi)容是一個(gè)數(shù)組可以引入多個(gè) XML 文件。
@ComponentScan
@ImportResource({"classpath:spring-dataSource.xml"})
public class ApplicationConfig {
}
然后我們就可以使用 @Autowired 注入在 spring-dataSource.xml 中定義的 Bean 了。
如果有多個(gè)類似 ApplicationConfig 的配置類需要注入,可以使用 @Import 注解。
@ComponentScan
@Import({ApplicationConfig2.class, ApplicationConfig3.class})
public class ApplicationConfig {
}
同樣,可以通過 import 元素在一個(gè) XML 文件中引入其他 XML 文件。
<import resource="spring-dataSource.xml" />
也許你希望使用 XML 加載 Java 配置類,不過目前 Spring 不支持。但是 Spring 支持通過 XML 的配置掃描主機(jī)的包,只要通過<context:component-scan>定義掃描的包就可以了。
<context:component-scan base-package="com,.wyk.beanDemo.pojo" />
使用 Profile
在軟件開發(fā)中,有很多情況下需要準(zhǔn)備多套環(huán)境,Spring 也會(huì)對(duì)這樣的場景進(jìn)行支持,在 Spring 中我們可以定義 Bean 的 Profile 。
使用注解 @Profile 配置
首先看注解 @Profile 如何配置,下面舉個(gè)例子,配置兩個(gè)環(huán)境,一個(gè)用于開發(fā)(dev),一個(gè)用于測試(test)。
@Component
public class ProfileDataSource {
@Bean(name="devDataSource")
@Profile("dev")
public DataSource getDevDataSource() {
...
}
@Bean(name="testDataSource")
@Profile("test")
public DataSource getTestDataSource() {
...
}
}
使用 XML 定義 Profile
在 XML 中 beans 標(biāo)簽中添加 Profile 屬性即可,但直接添加會(huì)導(dǎo)致整個(gè)文件都屬于同一個(gè) Profile , 所以一般放在內(nèi)部,這樣可以在一個(gè) XML 文件中配置多個(gè) Profile 。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd>
<beans profile="test">
...
</beans>
<beans profile="dev">
...
</beans>
</beans>
啟動(dòng) Profile
如果直接啟動(dòng)程序, Profile 相關(guān)的 Bean 并不會(huì)加載到 Spring 之中,這是因?yàn)樾枰孕屑せ?Profile 。激活 Profile 的方法有以下5種:
- 在使用 Spring MVC 的情況下可以配置 Web 上下文參數(shù),或者DispatchServlet參數(shù)。
- 作為 JNDI 條目。
- 配置環(huán)境變量。
- 配置 JVM 啟動(dòng)參數(shù)
- 在集成測試環(huán)境中使用 @ActiveProfiles 。
下面對(duì)幾種常用的方法進(jìn)行介紹。
在測試代碼中激活 Profile , 需要使用注解 @ActiveProfiles 。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=ProfileConfig.class)
@ActiveProfiles("dev")
public class ProfileTest {
@Autowired
private DataSource dataSource;
@Test
public void test() {
...
}
}
如果需要在服務(wù)器上運(yùn)行,最好還是配置 Java 虛擬機(jī)的啟動(dòng)項(xiàng),關(guān)于定制 Profile 的參數(shù)存在兩個(gè):
- spring.profiles.default: 默認(rèn)啟動(dòng)的 Profile ,如果系統(tǒng)沒有配置有關(guān) Profile 參數(shù)的時(shí)候,那么它將啟動(dòng)。
- spring.profiles.active: 啟動(dòng)的 Profile ,如果配置了它,那么 spring.profiles.default將失效。
比如需要啟動(dòng) test ,可以在 JVM 中配置:
JAVA_OPTS="-Dspring.profiles.active=test"
在大部分情況下需要啟動(dòng) Web 服務(wù)器,如果使用的是 Spring MVC,那么可以設(shè)置 Web 環(huán)境參數(shù)或者 DispatcherServlet參數(shù)來選擇對(duì)應(yīng)的Profile,比如可以在 web.xml中進(jìn)行配置。
......
<!--使用Web環(huán)境參數(shù)-->
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>test</param-value>
</context-param>
......
<!--使用 Spring MVC 的DispatcherServlet 環(huán)境參數(shù)-->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
<init-param>
<param-name>spring.profiles.active</param-name>
<param-value>test</param-value>
</init-param>
</servlet>
......
加載屬性文件
在 Spring 項(xiàng)目中,屬性(properties)文件經(jīng)常用做配置文件,比如使用 properties 文件配置數(shù)據(jù)庫文件。
jdbc.database.driver=com.mysql.jdbc.Driver
jdbc.database.url=jdbc:mysql://localhost:3306/wyk
jdbc.database.username=wyk
jdbc.database.password=123123
在 Spring 中可以通過注解或者 XML 的方式加載屬性文件。
使用注解方式加載屬性文件
Spring 提供注解 @PropertySource 來加載屬性文件,它有以下配置項(xiàng):
- name: 字符串,配置這次屬性配置的名稱。
- value: 字符串?dāng)?shù)組,可以配置多個(gè)屬性文件。
- ignoreResourceNotFound: boolean值,默認(rèn)為 false ,其含義為如果找不到對(duì)應(yīng)的屬性文件是否進(jìn)行忽略處理,由于默認(rèn)值為 false ,所以在默認(rèn)情況下找不到配置文件會(huì)拋出異常。
- encoding: 編碼,默認(rèn)為空。
@Configuraton
@PropertySource(value={"classpath:database-config.properties"},ignoreResourceNotFound=true)
public class ApplicationConfig {
}
然后可以通過環(huán)境來獲取對(duì)應(yīng)的配置屬性。
ApplicationContext context = new AnnotationConfigApplicaitonContext(ApplicationConfig.class);
String url = context.getEnvironment().getProperty("jdbc.database.url");
但是這種方式?jīng)]有解析屬性占位符(即${})的能力,因此更加推薦的方式是使用一個(gè)屬性文件解析類(PropertySourcesPlcaholderConfigurer)進(jìn)行處理。
首先修改 Java 配置類。
@Configuraton
@ComponentScan
@PropertySource(value={"classpath:database-config.properties"},ignoreResourceNotFound=true)
public class ApplicationConfig {
@Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
這樣就可以通過 @Value 注解使用屬性占位符。
@Component
public class DataSourceBean {
@Value("${jdbc.database.driver}")
private String driver;
@Value("${jdbc.database.url}")
private String url;
@Value("${jdbc.database.username}")
private String username;
@Value("${jdbc.database.password}")
private String password;
...
}
使用 XML 方式加載屬性文件
使用 XML 方式加載屬性文件,只需要使用<context:property-placeholder>元素加載一些配置項(xiàng)即可。
<?xml version="1.0" encoding="UTF-8" ?>
<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/beans/spring-context-4.0.xsd">
<context:property-placeholder ignore-resource-not-found="true"
location="classpath:database-config.properties" />
</beans>
上面的方式在屬性文件較多時(shí)會(huì)導(dǎo)致 location 屬性較長。還可以在 bean 標(biāo)簽中加載屬性文件,這樣可以分別加載多個(gè)屬性文件,這時(shí)需引入 PropertyPlaceholderConfigurer 類。
<bean id="appProperty" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<array>
<value>classpath:quartz.properties</value>
<value>classpath:db.properties</value> </array>
</property>
</bean>
條件化裝配 Bean
在某些情況下不需要去裝配 Bean ,比如當(dāng)某些配置文件不存在的時(shí)候。這個(gè)時(shí)候需要條件化判斷,Spring 提供了注解 @Conditonal 去配置類,但是這些類需要實(shí)現(xiàn) Condition (org.springframework.context.annotation.Condition)接口。
@Bean
@Conditional({DataSourceCondition.class})
public DataSource getDataSource() {
@Value("${jdbc.database.driver}") String driver;
@Value("${jdbc.database.url}") String url;
@Value("${jdbc.database.username}") String username;
@Value("${jdbc.database.password}") String password;
...
}
這里通過 @Value 引入了屬性文件中的配置,但是我們無法確定屬性是否存在,因此通過 @Conditional 注解引入一個(gè)類 DataSourceCondition 去進(jìn)行判斷。
public class DataSourceCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
return env.containsProperty("jdbc.database.driver")
&& env.containsProperty("jdbc.database.url")
&& env.containsProperty("jdbc.database.username")
&& env.containsProperty("jdbc.database.password")
}
}
Spring Bean 作用域
Spring 框架支持以下五個(gè)作用域,分別為singleton、prototype、request、session和global session,5種作用域說明如下所示。
| 作用域 | 描述 |
|---|---|
| singleton | 在spring IoC容器僅存在一個(gè)Bean實(shí)例,Bean以單例方式存在,默認(rèn)值 |
| prototype | 每次從容器中調(diào)用Bean時(shí),都返回一個(gè)新的實(shí)例,即每次調(diào)用getBean()時(shí),相當(dāng)于執(zhí)行newXxxBean() |
| request | 每次HTTP請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的Bean,該作用域僅適用于WebApplicationContext環(huán)境 |
| session | 同一個(gè)HTTP Session共享一個(gè)Bean,不同Session使用不同的Bean,僅適用于WebApplicationContext環(huán)境 |
| global-session | 一般用于Portlet應(yīng)用環(huán)境,該運(yùn)用域僅適用于WebApplicationContext環(huán)境 |
在 Bean 中的添加方式如下。
<bean id="juiceMaker" class="com.wyk.springdemo.pojo.JuiceMaker" scope="prototype">
<property name="beverageShop" value="pig" />
<property name="source" ref="orangeJuice" />
</bean>
Spring 表達(dá)式
待補(bǔ)充