领先一步
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 注解都应在启动时扫描,并且目标方法应变为事务性的。 那么事务行为在哪里发生呢?
在启动之前,我们仍然拥有与之前相同的文件
在启动时,会创建一个新类,称为代理。 此类负责添加事务行为,如下所示
生成的代理类位于 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”
在运行时,代理用于添加缓存行为。
默认情况下,如果您的 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>