/**
作者:Willpower
来源:Rifoo Technology(http://www.rifoo.com)
时间:2006-03-06
备注:转载请保留以上声明
**/
上一节我们完成了一个审计服务来帮助我们跟踪程序的改变情况。现在,我们将把这个服务加到我们的代码里,在我们的实验里配置该服务。
那么我们如何去做呢?
拦截器策略使用了三个对象:目标(这里是我们的facade),Spring为我们创建的代理对象(proxy object)和一个我们自己创建的拦截器(interceptor)。我们要做三件事情:
术语说明一下:
通知(Advice):如何将before通知、afterReturning通知和afterThrowing通知声明为bean。
切入点(Pointcut):如何声明静态切入点逻辑以将XML Spring Bean Configuration文件中的所有内容联系在一起。
Advisor:关联切入点定义与通知bean的方式。
1 配置通知(advice)对象
2 配置advisor对象,包括目标对象,目标方法和通知对象
3 配置目标对象(target object)
4 配置使用这个advise的代理对象
目标对象现在已经存在:就是我们前面完成的facade类。proxy代理对象也存在,因为我们使用它来完成事务。因此,我们只需要配置通知(advice)对象, 并将它加到我们的proxy中。参看下面的范例代码:
Example 6-3. RentABike-servlet.xml
<beans>
<bean id="transactionManager"
class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref local="transactionManager"/>
</property>
<property name="transactionAttributeSource">
<value>
com.springbook.HibernateRentABike.save*=PROPAGATION_REQUIRED
</value>
</property>
</bean>
<bean id="loggingBeforeInterceptor"
class="com.springbook.interceptors.LoggingBefore">
<property name="factory">
<ref local="sessionFactory"/>
</property>
</bean>
<bean id="rentaBikeTarget" class="com.springbook.HibRentABike">
<property name="storeName">
<value>Bruce's Bikes</value>
</property>
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>
<bean id="rentaBike"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.springbook.RentABike</value>
</property>
<property name="interceptorNames">
<list>
<value>loggingBeforeInterceptor</value>
<value>transactionInterceptor</value>
<value>rentaBikeTarget</value>
</list>
</property>
</bean>
<!-- etc. -->
</beans>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost/bikestore</value>
</property>
<property name="username"><value>bikestore</value></property>
</bean>
我们这个例子使用了简单的before advice。现在,我们来看看最复杂的around advice。如果我们能够理解最高级的流程,那么其他的都将变成很简单了。下图告诉我们advice在在Spring中是如何运作的。BikeDAO是一个目标对象(target object)。它有很多业务代码。要配置一个拦截器,我们这里要指定一个代理(proxy)。这个代理负责维护一串拦截器(即拦截器链)。一个对象通过代理去调用方法而不是直接通过目标对象去调用。当我们调用代理上的一个方法或引发一个异常时,代理对象会调用拦截器链中的第一个拦截器。每个拦截器会做它们自己的工作并唤醒下一个拦截器。最后一个拦截器会去调用目标对象的方法或异常。.在目标对象处理完调用后,拦截器们仅仅只是返回。
在本例中,我们在目标对象上配置了一个代理,并告诉代理将应用我们的拦截器到目标对象的所有方法。但是我们也能够仅仅使用正则表达式来指定一个方法的子集。下面是拦截器的代码:
Example 6-4. LoggingAround.java
public class LoggingAround implements MethodInterceptor {
private SessionFactory factory;
public SessionFactory getFactory( ) {
return factory;
}
public void setFactory(SessionFactory factory) {
this.factory = factory;
}
private void logEvent(String methodName, String message)
throws Exception {
Session s = null;
LogEvent le = new LogEvent(methodName, new Date( ), message);
try {
s = factory.openSession( );
s.save(le);
} catch (Exception ex) {
//log the exception
} finally {
s.close( );
}
}
public Object invoke(MethodInvocation methodInvocation)
throws Throwable {
logEvent(methodInvocation.getMethod( ).getName( ), "Entering call.");
Object result = methodInvocation.proceed( );
logEvent(methodInvocation.getMethod( ).getName( ), "Leaving call.");
return result;
}
}
下图显示新的配置文件:
Example 6-5. RentABike-servlet.xml
<bean id="loggingAround" class="com.springbook.interceptors.LoggingAround">
<property name="factory"><ref local="sessionFactory"/></property>
</bean>
<bean id="saveAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="loggingAround"/>
</property>
<property name="patterns">
<list>
<value>.*save.*</value>
</list>
</property>
</bean>
<bean id="rentaBikeTarget" class="com.springbook.HibRentABike">
<property name="storeName"><value>Bruce's Bikes</value></property>
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
<bean id="rentaBike"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.springbook.RentABike</value>
</property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
<value>saveAdvisor</value>
<value>rentaBikeTarget</value>
</list>
</property>
</bean>
下表显示了before和after advice仅仅被save的相关方法调用后的结果。
Example 6-6. Examining the contents of the logging table
+---------+--------------+------------+----------------+
| eventId | methodName | dateTime | message |
+---------+--------------+------------+----------------+
| 14 | saveBike | 2004-10-13 | Entering call. |
| 15 | saveBike | 2004-10-13 | Leaving call. |
| 16 | saveCustomer | 2004-10-13 | Entering call. |
| 17 | saveCustomer | 2004-10-13 | Leaving call. |
| 18 | saveBike | 2004-10-13 | Entering call. |
| 19 | saveBike | 2004-10-13 | Leaving call. |
+---------+--------------+------------+----------------+
最后的结果确实是我们想得到的。每一次指定的一个方法调用目标对象时,advice就会被触发。我们已经完成了这个声明式的审计功能(declarative auditing)。实际上,我们能够编写任何服务并使它成为声明型的。
作者:Willpower
来源:Rifoo Technology(http://www.rifoo.com)
时间:2006-03-06
备注:转载请保留以上声明
**/
上一节我们完成了一个审计服务来帮助我们跟踪程序的改变情况。现在,我们将把这个服务加到我们的代码里,在我们的实验里配置该服务。
那么我们如何去做呢?
拦截器策略使用了三个对象:目标(这里是我们的facade),Spring为我们创建的代理对象(proxy object)和一个我们自己创建的拦截器(interceptor)。我们要做三件事情:
术语说明一下:
通知(Advice):如何将before通知、afterReturning通知和afterThrowing通知声明为bean。
切入点(Pointcut):如何声明静态切入点逻辑以将XML Spring Bean Configuration文件中的所有内容联系在一起。
Advisor:关联切入点定义与通知bean的方式。
1 配置通知(advice)对象
2 配置advisor对象,包括目标对象,目标方法和通知对象
3 配置目标对象(target object)
4 配置使用这个advise的代理对象
目标对象现在已经存在:就是我们前面完成的facade类。proxy代理对象也存在,因为我们使用它来完成事务。因此,我们只需要配置通知(advice)对象, 并将它加到我们的proxy中。参看下面的范例代码:
Example 6-3. RentABike-servlet.xml
<beans>
<bean id="transactionManager"
class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref local="transactionManager"/>
</property>
<property name="transactionAttributeSource">
<value>
com.springbook.HibernateRentABike.save*=PROPAGATION_REQUIRED
</value>
</property>
</bean>
<bean id="loggingBeforeInterceptor"
class="com.springbook.interceptors.LoggingBefore">
<property name="factory">
<ref local="sessionFactory"/>
</property>
</bean>
<bean id="rentaBikeTarget" class="com.springbook.HibRentABike">
<property name="storeName">
<value>Bruce's Bikes</value>
</property>
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>
<bean id="rentaBike"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.springbook.RentABike</value>
</property>
<property name="interceptorNames">
<list>
<value>loggingBeforeInterceptor</value>
<value>transactionInterceptor</value>
<value>rentaBikeTarget</value>
</list>
</property>
</bean>
<!-- etc. -->
</beans>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost/bikestore</value>
</property>
<property name="username"><value>bikestore</value></property>
</bean>
我们这个例子使用了简单的before advice。现在,我们来看看最复杂的around advice。如果我们能够理解最高级的流程,那么其他的都将变成很简单了。下图告诉我们advice在在Spring中是如何运作的。BikeDAO是一个目标对象(target object)。它有很多业务代码。要配置一个拦截器,我们这里要指定一个代理(proxy)。这个代理负责维护一串拦截器(即拦截器链)。一个对象通过代理去调用方法而不是直接通过目标对象去调用。当我们调用代理上的一个方法或引发一个异常时,代理对象会调用拦截器链中的第一个拦截器。每个拦截器会做它们自己的工作并唤醒下一个拦截器。最后一个拦截器会去调用目标对象的方法或异常。.在目标对象处理完调用后,拦截器们仅仅只是返回。
在本例中,我们在目标对象上配置了一个代理,并告诉代理将应用我们的拦截器到目标对象的所有方法。但是我们也能够仅仅使用正则表达式来指定一个方法的子集。下面是拦截器的代码:
Example 6-4. LoggingAround.java
public class LoggingAround implements MethodInterceptor {
private SessionFactory factory;
public SessionFactory getFactory( ) {
return factory;
}
public void setFactory(SessionFactory factory) {
this.factory = factory;
}
private void logEvent(String methodName, String message)
throws Exception {
Session s = null;
LogEvent le = new LogEvent(methodName, new Date( ), message);
try {
s = factory.openSession( );
s.save(le);
} catch (Exception ex) {
//log the exception
} finally {
s.close( );
}
}
public Object invoke(MethodInvocation methodInvocation)
throws Throwable {
logEvent(methodInvocation.getMethod( ).getName( ), "Entering call.");
Object result = methodInvocation.proceed( );
logEvent(methodInvocation.getMethod( ).getName( ), "Leaving call.");
return result;
}
}
下图显示新的配置文件:
Example 6-5. RentABike-servlet.xml
<bean id="loggingAround" class="com.springbook.interceptors.LoggingAround">
<property name="factory"><ref local="sessionFactory"/></property>
</bean>
<bean id="saveAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="loggingAround"/>
</property>
<property name="patterns">
<list>
<value>.*save.*</value>
</list>
</property>
</bean>
<bean id="rentaBikeTarget" class="com.springbook.HibRentABike">
<property name="storeName"><value>Bruce's Bikes</value></property>
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
<bean id="rentaBike"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.springbook.RentABike</value>
</property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
<value>saveAdvisor</value>
<value>rentaBikeTarget</value>
</list>
</property>
</bean>
下表显示了before和after advice仅仅被save的相关方法调用后的结果。
Example 6-6. Examining the contents of the logging table
+---------+--------------+------------+----------------+
| eventId | methodName | dateTime | message |
+---------+--------------+------------+----------------+
| 14 | saveBike | 2004-10-13 | Entering call. |
| 15 | saveBike | 2004-10-13 | Leaving call. |
| 16 | saveCustomer | 2004-10-13 | Entering call. |
| 17 | saveCustomer | 2004-10-13 | Leaving call. |
| 18 | saveBike | 2004-10-13 | Entering call. |
| 19 | saveBike | 2004-10-13 | Leaving call. |
+---------+--------------+------------+----------------+
最后的结果确实是我们想得到的。每一次指定的一个方法调用目标对象时,advice就会被触发。我们已经完成了这个声明式的审计功能(declarative auditing)。实际上,我们能够编写任何服务并使它成为声明型的。