領先一步
VMware 提供培訓和認證,助您加速進步。
瞭解更多在Spring框架中,許多技術特性都依賴於代理使用。我們將使用三個示例深入探討這個主題:事務、快取和Java配置。
這篇部落格文章中顯示的所有程式碼示例都可以在我的github帳戶上找到。
@Service
public class AccountServiceImpl implements AccountService {
//…
//Not specifying a transaction policy here!
public void create(Account account) {
entityManager.persist(account);
}
}
由於“create”方法不是事務性的,它很可能會丟擲異常(因為此 Account 物件不應在事務外部持久化)。
@Service
public class AccountServiceImpl implements AccountService {
@PersistenceContext
private EntityManager entityManager;
@Transactional
public void create(Account account) {
entityManager.persist(account);
}
}
以下是相應的 Spring 配置
<bean id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven/>
在 Spring 泛型配置中,我們使用了 <tx:annotation-driven />。這意味著所有 @Transactional 註解都應該在啟動時被掃描,並且目標方法應該成為事務性的。那麼事務行為發生在何處呢?
啟動前,我們仍然擁有與之前相同的檔案
在啟動時,會建立一個名為 proxy 的新類。該類負責新增事務行為,如下所示:
生成的代理類位於 AccountServiceImpl 之上。它為其添加了事務行為[1]。
那麼如何確保確實使用了代理呢?為了您自己的理解,回溯程式碼並親眼看到您確實在使用代理是很有趣的。
一種簡單的方法是列印類名
AccountService accountService = (AccountService) applicationContext.getBean(AccountService.class);
String accountServiceClassName = accountService.getClass().getName();
logger.info(accountServiceClassName);
在我的電腦上,它顯示以下輸出
INFO : transaction.TransactionProxyTest - $Proxy13
這個類是一個動態代理,由 Spring 使用 JDK 反射 API 生成(更多資訊在此處)。
在關閉時(例如,當應用程式停止時),代理類將被銷燬,您將只在檔案系統上擁有 AccountService 和 AccountServiceImpl
@Controller public class AccountController { private AccountService accountService;
private void setAccountService(AccountService accountService) { this.accountService=accountService; }
//… }
<a href="http://blog.springsource.org/wp-content/uploads/2012/05/proxy-and-target1.png"><img class="aligncenter size-full wp-image-11128" title="proxy-and-target" src="http://blog.springsource.org/wp-content/uploads/2012/05/proxy-and-target1.png" alt="" width="316" height="255" /></a>
</div>
<div>
The attribute accountService is of type AccountService (interface). The variable dependency is on the interface type AccountService, not the implementation type, which reduces the coupling between classes. This is a best practice.
As seen before, both AccountServiceImpl and the generated Proxy implement the interface AccountService.
• If there is a proxy, Spring injects the proxy
• If not, Spring injects the instance of type AccountServiceImpl.
</div>
<h3><a name="cache">Caching</a></h3>
<div>
Declarative caching is a new feature in Spring 3.1 that works like Spring’s declarative transaction support.
The @Cacheable annotation should be used in that way:
</div>
<div>
```java
public class AccountServiceImpl implements AccountService {
@Cacheable(value="accounts", key="#id")
public Account findAccount (long id) {
// only enter method body if result is not in the cache already
}
}
您還應該在 Spring 配置中啟用快取,如下所示
<cache:annotation-driven />
以下是預期的結果
accountService.findAccount (1); // Result stored into cache for key “1”
accountService.findAccount (1); // Result retrieved from cache. Target method not called.
accountService.findAccount (2); // Result stored into cache for key “2”
在執行時,使用代理來新增快取行為。
注意:Spring 3.1 內建了一個相當簡單的快取實現。通常建議使用其他實現,例如 ehcache。在此處可用的示例應用程式 (https://github.com/michaelisvy/proxy-samples) 中,您會找到同時使用內建快取實現和 ehcache 的一些示例。預設情況下,如果您的 bean 沒有實現介面,Spring 會使用技術繼承:在啟動時,會建立一個新類。它繼承自您的 bean 類並在子方法中新增行為。
注意:本節需要一些 Spring Java 配置的背景知識。如果您不熟悉這種新的配置風格,請隨意跳過。
@Configuration public class JavaConfig {
@Bean public AccountService accountService() {
return new AccountServiceImpl((accountRepository()); } @Bean public AccountRepository accountRepository () { //… }
}
Spring calls the method accountService() every time it needs to wire an instance of the bean “accountService” and this one returns a “new” object of type AccountService. If 10 beans are using a dependency of type AccountService, this method is called 10 times.
However, no matters the Spring configuration has been made using Java Configuration or not, every bean should be a singleton by default. How is that possible and where is the magic happening?
This diagram explains how things work internally:
</div>
<div><a href="http://blog.springsource.org/wp-content/uploads/2012/05/java-config.png"><img class="aligncenter size-full wp-image-11131" title="java-config" src="http://blog.springsource.org/wp-content/uploads/2012/05/java-config.png" alt="" width="507" height="328" /></a></div>
<div>
So the Proxy is adding behavior there. In the case that your bean should be a singleton, the action to turn your Plain Old Java Object into a singleton is performed by a child class (Proxy).
</div>
<div>
<h3>Conclusion</h3>
We’ve seen some use-cases on how proxies are used inside the Spring framework. There are many other examples: Aspect Oriented Programming, Security (using Spring Security), thread safety, scopes, etc…
If you would like to know more on the impact on performance when using proxies, you can read <a href="http://blog.springsource.org/2007/07/19/debunking-myths-proxies-impact-performance/">Alef Arendsen’s blog entry here</a>.
</div>
<div>
<hr size="1" />
<div><a name="note">[1]</a>to be exact: the proxy class does not contain the transaction code internally. It delegates transaction handling to some classes that are part of the Spring framework. Spring will then handle transactions according to the Transaction Manager you have declared.
</div>
</div>