Spring学习笔记(八)-----Spring in Action
Spring的AOP框架允许你将分散在系统中的功能块放到一个地方-切面。重用通用功能的常用面向对象技术是使用继承和委
托模式。但由于基础类在系统中到处使用,使用继承会引起脆弱的继承关系。委托模式比较笨拙,依然需要重复调用委托对象
。使用AOP,你也是在一个地方定义通用功能,只是你可以声明式定义何时何地应用这些功能,而不一年欧冠在需要新功能的地
方修改代码。交叉业务现在可以被模块化到特定对象切面中。这样做有2个好处,第一,现在每个业务逻辑放在一个地方,而不
是分散到代码的各个角落。第二,我们的服务模块现在更加清晰,因为他们只包含他们的核心功能,辅助功能转移到切面中。
切面(Aspect):切面是你要实现的交叉功能。切面最常用的例子是日志记录。日志记录在系统中到处需要用到。
连接点(Joinpoint):连接点是应用程序执行过程中插入切面的地点。这个地点可以是方法调用,异常抛出,或者是要修改
字段。切面代码在这些地方插入到你的应用流程中,添加新的行为。
通知(Advice):通知切面的实际实现。它通知应用系统新的行为。
切入点(PointCut):切入点定义了通知应该应用在哪些连接点。通知可以应用到AOP框架支持的任何连接点。你并不希望把
所有切面应用到所有可能的连接点上。切入点让你指定通知应用到什么地方。
引入(Introduction):引入允许你为已存在类添加新方法和属性。
目标对象(Target):目标对象是被通知对象。它既可以是你编写的类也可以是你要添加定制行为的第三方类。
代理(Proxy):代理是将通知应用到目标对象后创建的对象。对于客户对象来说,目标对象和代理对象是一样的。
织入(Weaving):织入是将切面应用到目标对象从而创建一个新的代理对象的过程。
切入点定义了哪些连接点要被通知。
Spring的AOP实现:
在Spring中所有的通知都以Java类的形式编写。代理Bean只有在第一次被应用系统需要的时候才被创建。如果你使用的是ApplicationContext,代理对象在BeanFactory载入所有Bean的时候被创建。因为Spring在运行期创建代理,所以使用Spring AOP不需要特殊编译期。
Spring有2种代理创建方式。如果目标对象实现了一个接口暴露的方法,Spring将使用JDK的Java.lang.reflect.Proxy类创建代理。这个类让Spring动态产生一个新的类,它实现了所需的接口,织入了通知,并且代理对目标对象的所有请求。
如果目标对象没有实现任何接口,Spring使用CGLIB库生成目标对象的子类。在创建这个子类的时候,Spring将通知织入,并且对目标对象的调用委托给这个子类。
通知包含了切面的逻辑。所以当你创建一个通知对象的时候,你是在编写实现交叉功能的代码。而且,记住Spring的连接点模型是建立在方法拦截上。这意味着你编写的Spring通知会在方法调用周围的各个地方织入系统中。通知的类型有:Around,Before,After,Throws
前置通知:需要扩展MethodBeforeAdvice接口
public interface MethodBeforeAdvice{
void before(Method method,Object[] args,Object target)throws Throwable;
}
<?
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 ="kwikEMartTarget" class ="com.springinaction.chapter03.store.ApuKwikEMart" ></ bean >
<!-- 创建通知 -->
< bean id ="welcomeAdvice" class ="com.springinaction.chapter03.store.WelcomeAdvice" ></ bean >
<!-- 创建代理对象 -->
< bean id ="kwikEMart" class ="org.springframework.aop.framework.ProxyFactoryBean" >
<!-- 代理类实现的接口 -->
< property name ="proxyInterfaces" >
< value > com.springinaction.chaper03.store.kwikEMart </ value >
</ property >
<!-- 要织入的通知 -->
< property name ="interceptorNames" >
< list >
< value > welcomeAdvice </ value >
</ list >
</ property >
<!-- 要代理的目标对象 -->
< property name ="target" >
< ref bean ="kwikEMartTarget" />
</ property >
</ bean >
</ beans >
<! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
< beans >
<!-- 创建代理目标对象 -->
< bean id ="kwikEMartTarget" class ="com.springinaction.chapter03.store.ApuKwikEMart" ></ bean >
<!-- 创建通知 -->
< bean id ="welcomeAdvice" class ="com.springinaction.chapter03.store.WelcomeAdvice" ></ bean >
<!-- 创建代理对象 -->
< bean id ="kwikEMart" class ="org.springframework.aop.framework.ProxyFactoryBean" >
<!-- 代理类实现的接口 -->
< property name ="proxyInterfaces" >
< value > com.springinaction.chaper03.store.kwikEMart </ value >
</ property >
<!-- 要织入的通知 -->
< property name ="interceptorNames" >
< list >
< value > welcomeAdvice </ value >
</ list >
</ property >
<!-- 要代理的目标对象 -->
< property name ="target" >
< ref bean ="kwikEMartTarget" />
</ property >
</ bean >
</ beans >
ProxyFactoryBean类是一个在BeanFactory中显示的创建代理对象的中心类。像我们展示的那样,你可以给它一个要实现的接口,一个要代理的目标对象,一个要织入的通知,并且它将创建一个崭新的代理对象。通常配置ProxyFactoryBean,让它实现和目标对象一样的接口。
后置通知:需要实现AfterReturningAdvice
public interface AfterReturningAdvice{
void afterReturning(Object returnValue,Method method,Object[] args,Object target)throws Throwable;
}
环绕通知:需要实现MethodInterceptor,同时实现前置和后置通知
public interface MethodInterceptor extends Interceptor{
Object invoke(MethodInvocation invocation)throws Throwable;
}
MethodInterceptor接口和前面介绍的2种通知不同点:
1、MethodInterceptor能够控制目标方法是否真的被调用。通过调用MethodInvocation.proceed()方法来调用目标方法。这一点不同于前两个,目标方法总是被调用的。
2、MethodInterceptor让你可以控制返回的对象。就是说你可以返回一个与proceed()方法返回对象完全不同的对象。
package
com.wyq.spring.base.aopinstance;
import java.util.HashSet;
import java.util.Set;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.jboss.remoting.samples.transporter.basic.Customer;
/**
* @author 作者
* @version 创建时间:2009-11-5 下午05:19:19
* 类说明
*/
public class OnePerCustomerInterceptor implements MethodInterceptor {
// 定义包含用户的集合
private Set customers = new HashSet();
/*
* 当你在方法调用的前后都需要交叉切面逻辑时,应该使用MethodInterceptor。由于你必须要记得显示调用
* invocation.proceed()方法,所以,在满足要求的情况下,最好还是使用MethodBeforeAdvice或
* AfterReturningAdvice.
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
Customer customer = (Customer)invocation.getArguments()[ 0 ];
if (customers.contains(customer)){
System.out.println( " 抛出异常 " );
}
// 调用目标方法
Object squishee = invocation.proceed();
// 添加用户
customers.add(customer);
// 返回目标方法结果
return squishee;
}
}
import java.util.HashSet;
import java.util.Set;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.jboss.remoting.samples.transporter.basic.Customer;
/**
* @author 作者
* @version 创建时间:2009-11-5 下午05:19:19
* 类说明
*/
public class OnePerCustomerInterceptor implements MethodInterceptor {
// 定义包含用户的集合
private Set customers = new HashSet();
/*
* 当你在方法调用的前后都需要交叉切面逻辑时,应该使用MethodInterceptor。由于你必须要记得显示调用
* invocation.proceed()方法,所以,在满足要求的情况下,最好还是使用MethodBeforeAdvice或
* AfterReturningAdvice.
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
Customer customer = (Customer)invocation.getArguments()[ 0 ];
if (customers.contains(customer)){
System.out.println( " 抛出异常 " );
}
// 调用目标方法
Object squishee = invocation.proceed();
// 添加用户
customers.add(customer);
// 返回目标方法结果
return squishee;
}
}