在獨立的環境中通過使用基於 Java 的配置創建和使用Spring 容器

創建一個名爲com.wiley.beginning.spring.ch2 的程序包,並在其中創建如下所示的
Java 類:
public class Account {
private long id;
private String ownerName;
private double balance;
private Date accessTime;
Spring 入門經典
20
//getters & setters...
}
public interface AccountDao {
public void insert(Account account);
public void update(Account account);
public void update(List<Account> accounts);
public void delete(long accountId);
public Account find(long accountId);
public List<Account> find(List<Long> accountIds);
public List<Account> find(String ownerName);
public List<Account> find(boolean locked);
}
public class AccountDaoInMemoryImpl implements AccountDao {
private Map<Long,Account> accountsMap = new HashMap<>();
{
Account account1 = new Account();
account1.setId(1L);
account1.setOwnerName("John");
account1.setBalance(10.0);
Account account2 = new Account();
account2.setId(2L);
account2.setOwnerName("Mary");
account2.setBalance(20.0);
accountsMap.put(account1.getId(), account1);
accountsMap.put(account2.getId(), account2);
}
@Override
public void update(Account account) {
accountsMap.put(account.getId(), account);
}
@Override
public Account find(long accountId) {
return accountsMap.get(accountId);
}
//other method implementations
}
第2 章使用 Spring 進行依賴注入
21
public interface AccountService {
public void transferMoney(
long sourceAccountId, long targetAccountId, double amount);
public void depositMoney(long accountId, double amount)
throws Exception;
public Account getAccount(long accountId);
}
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transferMoney(
long sourceAccountId, long targetAccountId, double amount) {
Account sourceAccount = accountDao.find(sourceAccountId);
Account targetAccount = accountDao.find(targetAccountId);
sourceAccount.setBalance(sourceAccount.getBalance() - amount);
targetAccount.setBalance(targetAccount.getBalance() + amount);
accountDao.update(sourceAccount);
accountDao.update(targetAccount);
}
@Override
public void depositMoney(long accountId, double amount) throws Exception {
Account account = accountDao.find(accountId);
account.setBalance(account.getBalance() + amount);
accountDao.update(account);
}
@Override
public Account getAccount(long accountId) {
return accountDao.find(accountId);
}
}
(4) 創建如下所示的基於Java 的Bean 定義類:
@Configuration
public class Ch2BeanConfiguration {
@Bean
public AccountService accountService() {
AccountServiceImpl bean = new AccountServiceImpl();
bean.setAccountDao(accountDao());
return bean;
Spring 入門經典
22
}
@Bean
public AccountDao accountDao() {
AccountDaoInMemoryImpl bean = new AccountDaoInMemoryImpl();
//depedencies of accountDao bean will be injected here...
return bean;
}
}
(5) 使用main 方法創建一個Main 類,並將上一步驟所創建的基於Java 的配置類作爲
構造函數參數來實例化Spring 容器:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(Ch2BeanConfiguration.class);
}
}
(6) 在Spring 容器內部訪問accountService Bean,如下所示:
AccountService accountService =
applicationContext.getBean("accountService", AccountService.class);
System.out.println("Before money transfer");
System.out.println("Account 1 balance :" +
accountService.getAccount(1).getBalance());
System.out.println("Account 2 balance :" +
accountService.getAccount(2).getBalance());
accountService.transferMoney(1, 2, 5.0);
System.out.println("After money transfer");
System.out.println("Account 1 balance :" +
accountService.getAccount(1).getBalance());
System.out.println("Account 2 balance :" +
accountService.getAccount(2).getBalance());
示例說明
首先,我們創建了一個名爲Account 的域類。然後,分別使用AccountDaoInMemoryImpl
類和AccountServiceImpl 類創建了接口AccountDao(對應DAO 層)和AccountService(對應服
務層)。AccountService 聲明瞭一個transferMoney 方法,該方法可以在由accountIds 標識的
兩個Account 對象之間移動給定數額的資金。AccountServiceImpl 類首先需要獲取這兩個
Account 對象並在完成轉賬操作之後更新這些對象。因此,AccountServiceImpl 依賴
第2 章使用 Spring 進行依賴注入
23
AccountDao 接口,該接口聲明瞭用來在給定Account 上執行基本持久化操作的方法以及用
來查找Account 實例的Finder 方法(通過使用一些查詢參數)。
然後創建一個名爲Ch2BeanConfiguration的Spring Bean定義類並使用org.springframework.
context.annotation.Configuration 註解進行標記。該註解告訴Spring,該類是一個Bean 並且
包含配置元數據。在該配置類中,創建了兩個工廠方法並使用org.springframework.context.
annotation.Bean 註解進行標記。這些方法在啓動期間被Spring 容器調用,而返回值則被視
爲Spring 管理的Bean。默認情況下,方法的名稱就是Bean 的名稱。而在工廠方法中,首
先通過調用Setter 方法設置所需的依賴項,然後使用具體類創建一個Bean 並將其返回。此
外,依賴項還可以構造函數參數的形式賦予。
注意,工廠方法的返回類型被定義爲接口而不是具體類。雖然使用接口並不是強制性
的,但這樣做可以更容易地使用不同的Bean 實現類來配置系統。例如,可以添加另一個
帶有新配置元數據的Bean 定義,而該定義返回實現了AccountDao 接口的JDBC,同時
AccountService Bean 仍然保持工作,而不必對其實現過程或Bean 定義進行任何修改。
你可能已經注意到,AccountService Bean 的AccountDao 依賴是通過在AccountServic()
方法中調用accountDao()方法而獲取的。有些人可能會認爲,如果其他工廠方法也多次調
用accountDao()方法,那麼在系統中不是就有多個accountDao Bean 了嗎?如果僅想要這些
Bean 中的一個實例作爲一般情況下的服務和存儲庫Bean,那麼這難道不是一個很大的問
題嗎?這些問題的答案是沒有的,在本系統中,針對一個Bean 定義並不會有多個Bean 實
例。默認情況下,每一個Bean 都有一個被稱爲單實例作用域(singleton scope)的單個實例 (本
章的後面將會詳細介紹Bean 作用域)。目前只需要知道僅會生成accountDao Bean 的一個實
例,多個不同方法調用並不會創建多個實例。Spring 在運行時動態地擴展@Configuration
類並且使用@Bean 註解來重寫工廠方法,以便處理該問題。因此,不管是從類中多次調用
工廠方法,還是從其他@Configuration 類中多次調用工廠方法,在第一次創建了Bean 實例
之後就不會再創建任何新的Bean 實例。對於連續調用,工廠方法將會返回相同的Bean
實例。
接下來,創建Spring 容器實例。如前所述,Spring 容器也是一個Java 對象,並負責
管理應用程序中的其他對象。org.springframework.context.ApplicationContext 接口表示
Spring 接口;事實上,術語Spring Container 和AppliationContext 通常可以交換使用。可
以根據ApplicationContext 實例處理Bean 配置元數據文件或類的方式以及處理的位置來選
擇使用ApplicationContext 的多個不同的實現過程。org.springframework.context.annotation.
AnnotationConfigApplicationContext 類被用來處理基於Java 的配置元數據類。雖然,這裏僅
提供了一個,但我可以將多個配置類作爲輸入參數提供給AnnotationConfigApplicationContext
類。
Spring 容器(或者說ApplicationContext)在創建之後就可以使用了。目前,可以通過
Spring 容器獲取Bean,並使用它們滿足系統需求。獲取Spring 管理的Bean 的過程被稱爲
“Bean 查找”。本章的後面將詳細介紹Bean 查找。此時只需要知道可以通過名稱獲取對任
何Bean 的引用。ApplicationContext.getBean()方法用來執行Bean 查找。除了向該方法傳入
需要查找的Bean 名稱之外,還要提供類型參數,以便自動將返回的Bean 實例轉換爲該類

型。在獲取了對某個Bean 的引用後,就可以調用Bean 契約(contract)中的任何方法。可以
調用transferMoney()方法並將accountIds 和amount 作爲輸入參數。
也可以創建並使用基於XML 的配置元數據。下面所示的“試一試”演示了具體的操

作過程。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章