Spring容器配置与AOP技术详解
立即解锁
发布时间: 2025-08-19 02:32:41 阅读量: 3 订阅数: 11 


Spring框架下的Java开发实践与进阶
### Spring 容器配置与 AOP 技术详解
#### 1. Spring 容器配置的替代方案
Spring 容器的配置格式通常使用 XML,但实际上还有其他方式可以将 bean 定义加载到容器中。
##### 1.1 属性文件定义
Java 属性文件和 ResourceBundles 是一种替代的声明式定义格式。虽然属性文件定义格式不如 XML 那样强大,无法表达构造函数注入、方法注入、嵌套 bean 或复杂集合类型的设置,但对于使用 Setter 注入的简单基本 bean 定义,它更加简洁,并且在不需要引入 XML 解析器的环境中很有用。
例如,将以下 XML 配置:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="weatherService" class="ch02.sample2.WeatherServiceImpl">
<property name="weatherDao">
<ref local="weatherDao"/>
</property>
</bean>
<bean id="weatherDao" class="ch02.sample2.StaticDataWeatherDaoImpl"/>
</beans>
```
转换为属性文件格式为:
```properties
weatherService.class=ch02.sample2.WeatherServiceImpl
weatherService.weatherDao(ref)=weatherDao
weatherDao.class=ch02.sample2.StaticDataWeatherDaoImpl
```
初始化使用属性文件配置的容器需要多步操作,使用 `PropertiesBeanDefinitionReader` 来解析该格式。以下是一个测试用例:
```java
public void testBeanFactoryLoadedFromProperties() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
PropertiesBeanDefinitionReader bdReader = new PropertiesBeanDefinitionReader(bf);
Resource def = new ClassPathResource("ch03/sample4/beans.properties");
bdReader.loadBeanDefinitions(def);
}
```
对于应用上下文的测试用例如下:
```java
public void testApplicationContextLoadedFromProperties() throws Exception {
GenericApplicationContext ctx = new GenericApplicationContext();
PropertiesBeanDefinitionReader bdReader = new PropertiesBeanDefinitionReader(ctx);
Resource def = new ClassPathResource("ch03/sample4/beans.properties");
bdReader.loadBeanDefinitions(def);
ctx.refresh();
}
```
##### 1.2 编程式 bean 定义
Spring 容器的功能以及 XML 和属性文件定义格式都是基于一个可编程的 API 来创建和使用 bean 定义元数据的。涉及到的类包括:
- `RootBeanDefinition`:用于指定没有父 bean 的 bean 定义。
- `ChildBeanDefinition`:允许定义从其父 bean 继承设置的 bean。
- `MutablePropertyValues`:允许简单地操作属性。
- `ConstructorArgumentValues`:保存 bean 的构造函数参数值。
以下是一个完全通过编程方式处理 bean 工厂定义的测试用例:
```java
public void testBeanFactoryLoadedProgrammatically() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
MutablePropertyValues mpv = new MutablePropertyValues();
mpv.addPropertyValue("weatherDao", new RuntimeBeanReference("weatherDao"));
RootBeanDefinition weatherService = new RootBeanDefinition(WeatherServiceImpl.class, mpv);
bf.registerBeanDefinition("weatherService", weatherService);
mpv = new MutablePropertyValues();
RootBeanDefinition weatherDao = new RootBeanDefinition(StaticDataWeatherDaoImpl.class, mpv);
bf.registerBeanDefinition("weatherDao", weatherDao);
}
```
#### 2. Spring AOP 概述
面向切面编程(AOP)是一项重要的技术,非常适合解决许多常见问题。Spring 提供 AOP 功能来补充其 IoC 功能,确保企业服务代码不会污染应用程序对象。
##### 2.1 AOP 的目标
企业应用程序经常面临横切关注点,传统的面向对象编程(OOP)难以很好地解决这些问题。例如:
- 每个 `AccountManager` 接口上的操作都应该在事务中执行。
- 某些昂贵方法的返回值应该在指定时间内缓存。
- 当域对象中的实例字段被修改时,该对象应该被标记为脏。
- 公司进行为期一个月的营销活动,需要分析其对用户活动的影响,临时分析代码最好从服务层外部化。
传统的 OOP 解决方案,如装饰器设计模式,会导致代码重复,难以维护。而 AOP 提供了一种新的方式来模块化这些横切关注点,将额外的行为应用到应用程序结构中的一组方法或其他点上。
##### 2.2 AOP 示例
假设我们有一个 `AccountManager` 业务接口,要求在任何业务方法抛出异常时,向系统管理员发送电子邮件通知。
不使用 AOP 的实现如下:
```java
public class AccountManagerImpl implements AccountManager {
private MailSender mailSender;
private SimpleMailMessage message;
private AccountDao accountDao;
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
public void setMessage(SimpleMailMessage message) {
this.message = message;
}
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
private void sendMail(Exception ex) {
SimpleMailMessage msg = new SimpleMailMessage(this.message);
msg.setText("Encountered exception " + ex.getMessage());
this.mailSender.send(msg);
}
public Account getAccount(String accountId) throws AccountNotFoundException, DataAccessException {
try{
return this.accountDao.findAccount(accountId);
}
catch(AccountNotFoundException ex){
sendMail(ex);
throw ex;
}
catch (DataAccessException ex) {
sendMail(ex);
throw ex;
}
}
public void createAccount(Account account) throws DataAccessException {
try{
if (isInvalid(account)) {
throw new InvalidAccountException(account);
}
this.accountDao.saveAccount(account);
}
catch (IOException ex) {
sendMail(ex);
throw ex;
}
catch (DataAccessException ex) {
sendMail(ex);
throw ex;
}
}
}
```
这种实现方式存在代码重复、难以维护等问题。使用 AOP 可以将异常通知功能模块化,应用到多个对象上。
首先,编写一个实现异常通知策略的切面类:
```java
public class EmailNotificationThrowsAdvice implements ThrowsAdvice {
private MailSender mailSender;
private SimpleMailMessage message;
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
public void setMessage(SimpleMailMessage message) {
this.message = message;
}
public void afterThrowing(Exception ex) throws Throwable {
SimpleMailMessage msg = new SimpleMailMessage(this.message);
msg.setText("Encountered exception " + ex.getMessage());
this.mailSender.send(msg);
}
}
```
然后,将该通知应用到多个对象上:
```xml
<bean id="emailNotificationThrowsAdvice"
class="com.mycompany.EmailNotificationThrowsAdvice">
<property name="mailSender"><ref bean="mailSender"/></property>
<property name="message"><ref bean="mailMessage"/></property>
</bean>
<bean id="accountManagerBeanNameProxyCreator"
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames"><value>accountManagerTarget</value></property>
<property name="interceptorNames">
<list>
```
0
0
复制全文
相关推荐










