引言
Spring裝配Bean的方式有3種:
- 通過XML裝配Bean
- 通過注解裝配Bean @Component
- 自動裝配
一、通過XML裝配Bean
通過XML裝配Bean其實就是通過setter方法注入,通過在XML檔案中設定Property值,使得對象在建立的時候就調用setter方法将初始值注入給對象。這裡不再贅述。
二、通過注解裝配Bean
除了通過xml檔案,Spring IoC還可以通過元件掃描的方式來發現Bean 。具體做法如下:
package package1;
@Component(value = "role")
public class Role{
@value("1")//賦初始值,裝配
private Long id;
@value("role_name_1")
private String roleName;
@value("role_note_1")
private String note;
/***************setter and getter*******/
}
@ComponentScan(basePackages = {"package1"})
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext cxt = new AnnotationConfigApplicationContext(Test.class);
Person role = cxt.getBean(Person.class);
System.err.println(role.getName());
cxt.close();
}
}
@Component 代表Spring IoC會把該類掃描生成Bean,@Value 就是給Bean賦初值。
給Test類加上 @ComponentScan 注解之後,代表該類會去package1包裡面去掃描component,上面表示在package1中掃描,如果Role類所在包不是package1,就會導緻掃描不到。
@ComponentScan的 basePackages 屬性規定了掃描的包名,其實是規定了一個包數組,可以寫多個包在裡面,掃描的時候隻會去這些包中掃描。
三、自動裝配
如果一個對象Book是另一個對象Student的Field,那麼要想将這種依賴關系注入進去,就需要用到 @AutoWired注解,用法如下:
package package3;
public interface Person {
void say();
}
package package3;
import ...;
@Component
public class Student implements Person {
@Autowired
private Book book;//将Book對象注入Student
@Override
public void say() {
System.out.println("a book named " + book.getContent());
}
}
package package3;
@Component
public class Book {
@Value("Float")
private String content;
/*********setter and getter*******/
}
package package3;
@ComponentScan
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test.class);
Student s = context.getBean(Student.class);
s.say();
}
}
Student中的@AutoWired注解會執行個體化一個Book對象并注入給他。
歧義性
現在來了一個Worker類,它也是Person接口的子類:
package package3;
@Component
public class Worker implements Person {
@Override
public void say() {
System.out.println("I am a worker");
}
}
School類持有一個Person:
package package3;
@Component
public class School {
@Autowired
private Person person;//将Person對象注入School
public void makePersonSay(){
person.say();
}
}
下面測試:
School school = context.getBean(School.class);
school.makePersonSay();
這時候就會出錯,因為School持有的是Person,Spring不知道是Worker還是Student
解決方法一:利用@Primary
這個情況就需要 @Primary注解,在Student或者Worker的@Component注解下面加上 @Primary,就表示将哪個對象注入到School中。
解決方法二:利用@Qualifier
在School的@AutoWired注解下面加上@Qualifier(“Student”),就表示将Student對象注入到School中。
四、使用@Bean裝配Bean
@Component注解隻能加在類名上方,不能注解到方法上,但是有時候用到第三方Jar包的時候就無法使用@Component了,這時候就需要用 @Bean注解。
例如,下面以一個DBCP的例子說明:
DataSourceService.java
package package4;
@Component
public class DataSourceService {
@Autowired
private DataSource dataSource = null; //Spring會在運作時将DataSource對象注入
public void getMessage(){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();//從DataSource擷取資料庫連接配接
ps = conn.prepareStatement("select * from mytable");
rs = ps.executeQuery();
while (rs.next()){
int id = rs.getInt("id");
String note = rs.getString("note");
System.out.print("name: " + id);
System.out.println("age: " + note);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//System.out.println("method run");
}
}
ServiceConfig.java
package package4;
@ComponentScan
public class MyBean {
//在方法中定義Bean,這樣Spring會掃描到該Bean,并和其他Bean一樣處理
//前提是這個方法必須有傳回值,傳回值的類型就是這個Bean的類型
@Bean(name = "dataSource")
public DataSource createDataSource(){
Properties props = new Properties();
props.setProperty("driver","com.mysql.jdbc.Driver");
props.setProperty("url","jdbc:mysql://localhost:3306/test");
props.setProperty("username","root");
props.setProperty("password","123456");
DataSource dataSource = null;
try {
dataSource = BasicDataSourceFactory.createDataSource(props);
} catch (Exception e) {
e.printStackTrace();
}
return dataSource;
}
}
Test.java
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ServiceConfig.class);
DataSourceService service = context.getBean(DataSourceService.class);
service.getMessage();
五、其他設定
1、使用Profile
靈活開發中,測試通常都是小階段的,可能是開發人員使用一套環境,而測試人員使用另外一套,這兩套環境的資料庫環境通常是不同的。這時就需要給Bean定義Profile。
假設現在配置兩個資料庫連接配接池,一個用于開發(dev),一個用于測試(test)。
(1)通過注解@Profile定義Profile
給用于測試的Bean加上@Profile(“test”),給用于開發的Bean加上@Profile(“dev”),如果想測試的話,就加上@ActiveProfiles(“test”),如果想開發的話,就加上@ActiveProfiles(“dev”)
(2)通過XML定義Profile
在Beans.xml檔案中的< beans>< /beans>标簽中加上profile屬性,如果為"dev",那麼這個标簽中的所有bean都會用于開發。
2、加載屬性檔案(.properties)
上文中的資料庫資訊其實可以通過建立一個properties檔案來加載,如建立一個database-config.properties檔案,内容如下:
jdbc.database.driver=com.mysql.jdbc.Driver
jdbc.database.url=jdbc:mysql://localhost:3306/test
jdbc.database.username=root
jdbc.database.password=123456
這樣就不用跑到java代碼中去修改資料庫資訊了,直接修改properties檔案即可。
那麼如何加載.properties檔案呢,也有兩種方式:
(1)通過注解加載
給使用了資料庫資訊的Bean添加 @PropertySource注解,并且設定相關field,并給field加上@Value注解,通過 ${jdbc.database.driver} 等屬性占位符來給field指派:
@ComponentScan
//通過@PropertySource注解加載properties檔案資訊
@PropertySource(value={"classpath:database-config.properties"}, ignoreResourceNotFound=true)
public class MyBean {
@Value("${jdbc.database.driver}")//直接從屬性占位符擷取資料
private String driver = null;
@Value("${jdbc.database.url}")
private String url = null;
@Value("${jdbc.database.username}")
private String username = null;
@Value("${jdbc.database.password}")
private String password = null;
@Bean(name = "dataSource")
public DataSource createDataSource(){
Properties props = new Properties();
props.setProperty("driver",driver);//driver、url等field會被Spring注入
props.setProperty("url",url);
props.setProperty("username",username);
props.setProperty("password",password);
DataSource dataSource = null;
try {
dataSource = BasicDataSourceFactory.createDataSource(props);
} catch (Exception e) {
e.printStackTrace();
}
return dataSource;
}
}
(2)使用XML方式加載屬性檔案
在beans的xml檔案中,對< context:property-placeholder>元素加載一些配置項即可,
如:
< context:property-placeholder ignore-resource-not-found="true" location="classpath:database-config.properties"/>
3、條件化裝配@Conditional
如果一個Bean加上了@Conditional這個注解,那麼,在建立這個Bean的時候會先判斷條件是否滿足,不滿足條件就不去建立這個bean。用法如下:
給一個Bean加上 @Conditional({MyCondition.class})
其中MyCondition是自己實作的一個類,這個類實作了Conditional接口,會重寫matches方法,可以在該方法中加上一定條件,如果傳回true才會去建立該Bean。
六、Bean的作用域
預設情況下,Spring IoC隻會為每個Bean建立一個執行個體。Spring提供了4種作用域:
- 單例(singleton) 預設情況,隻會生成一個Bean的執行個體
- 原型(prototype) 每次注入或者從IoC中擷取都會建立一個新的執行個體
- 會話(session) 在Web應用中使用,每次會話隻建立一個執行個體
- 請求(request) 在Web應用中使用,一次請求中産生一個執行個體,不同的請求會産生不同的執行個體
可以通過 @Scope 注解來規定一個Bean的作用域:
如
@Scope(ConfigureableBeanFactory.SCOPE_PROTOTYPE)
就能将一個bean的作用域變為prototype。
七、Spring表達式(Spring EL)
Spring使用了Spring EL來提供更靈活的注入方式,他的功能如下:
- 使用Bean的id來引用Bean
- 調用指定對象的方法和通路對象的屬性
- 進行運算
- 提供正規表達式比對
- 集合配置
舉例如下:
ElBean.java
@Component("elBean")
public class ElBean {
//通過beanName擷取bean,然後注入
@Value("#{role}")
private Role role;
//擷取bean的屬性id
@Value("#{role.id}")
private Long id;
//調用bean的getNote方法,擷取角色名稱
@Value("#{role.getNote().toString()}")
private String note;
/***********setter and getter**********/
}
Role.java
@Component("role")
public class Role {
// 指派long型
@Value("#{1}")
private Long id;
// 字元串指派
@Value("#{'role_name_1'}")
private String roleName;
// 字元串指派
@Value("#{'note_1'}")
private String note;
/***********setter and getter**********/
}