托模式。但由于基礎(chǔ)類在系統(tǒng)中到處使用,使用繼承會(huì)引起脆弱的繼承關(guān)系。委托模式比較笨拙,依然需要重復(fù)調(diào)用委托對(duì)象
。使用AOP,你也是在一個(gè)地方定義通用功能,只是你可以聲明式定義何時(shí)何地應(yīng)用這些功能,而不一年歐冠在需要新功能的地
方修改代碼。交叉業(yè)務(wù)現(xiàn)在可以被模塊化到特定對(duì)象切面中。這樣做有2個(gè)好處,第一,現(xiàn)在每個(gè)業(yè)務(wù)邏輯放在一個(gè)地方,而不
是分散到代碼的各個(gè)角落。第二,我們的服務(wù)模塊現(xiàn)在更加清晰,因?yàn)樗麄冎话麄兊暮诵墓δ埽o助功能轉(zhuǎn)移到切面中。
切面(Aspect):切面是你要實(shí)現(xiàn)的交叉功能。切面最常用的例子是日志記錄。日志記錄在系統(tǒng)中到處需要用到。
連接點(diǎn)(Joinpoint):連接點(diǎn)是應(yīng)用程序執(zhí)行過程中插入切面的地點(diǎn)。這個(gè)地點(diǎn)可以是方法調(diào)用,異常拋出,或者是要修改
字段。切面代碼在這些地方插入到你的應(yīng)用流程中,添加新的行為。
通知(Advice):通知切面的實(shí)際實(shí)現(xiàn)。它通知應(yīng)用系統(tǒng)新的行為。
切入點(diǎn)(PointCut):切入點(diǎn)定義了通知應(yīng)該應(yīng)用在哪些連接點(diǎn)。通知可以應(yīng)用到AOP框架支持的任何連接點(diǎn)。你并不希望把
所有切面應(yīng)用到所有可能的連接點(diǎn)上。切入點(diǎn)讓你指定通知應(yīng)用到什么地方。
引入(Introduction):引入允許你為已存在類添加新方法和屬性。
目標(biāo)對(duì)象(Target):目標(biāo)對(duì)象是被通知對(duì)象。它既可以是你編寫的類也可以是你要添加定制行為的第三方類。
代理(Proxy):代理是將通知應(yīng)用到目標(biāo)對(duì)象后創(chuàng)建的對(duì)象。對(duì)于客戶對(duì)象來說,目標(biāo)對(duì)象和代理對(duì)象是一樣的。
織入(Weaving):織入是將切面應(yīng)用到目標(biāo)對(duì)象從而創(chuàng)建一個(gè)新的代理對(duì)象的過程。
切入點(diǎn)定義了哪些連接點(diǎn)要被通知。
Spring的AOP實(shí)現(xiàn):
在Spring中所有的通知都以Java類的形式編寫。代理Bean只有在第一次被應(yīng)用系統(tǒng)需要的時(shí)候才被創(chuàng)建。如果你使用的是ApplicationContext,代理對(duì)象在BeanFactory載入所有Bean的時(shí)候被創(chuàng)建。因?yàn)镾pring在運(yùn)行期創(chuàng)建代理,所以使用Spring AOP不需要特殊編譯期。
Spring有2種代理創(chuàng)建方式。如果目標(biāo)對(duì)象實(shí)現(xiàn)了一個(gè)接口暴露的方法,Spring將使用JDK的Java.lang.reflect.Proxy類創(chuàng)建代理。這個(gè)類讓Spring動(dòng)態(tài)產(chǎn)生一個(gè)新的類,它實(shí)現(xiàn)了所需的接口,織入了通知,并且代理對(duì)目標(biāo)對(duì)象的所有請(qǐng)求。
如果目標(biāo)對(duì)象沒有實(shí)現(xiàn)任何接口,Spring使用CGLIB庫生成目標(biāo)對(duì)象的子類。在創(chuàng)建這個(gè)子類的時(shí)候,Spring將通知織入,并且對(duì)目標(biāo)對(duì)象的調(diào)用委托給這個(gè)子類。
通知包含了切面的邏輯。所以當(dāng)你創(chuàng)建一個(gè)通知對(duì)象的時(shí)候,你是在編寫實(shí)現(xiàn)交叉功能的代碼。而且,記住Spring的連接點(diǎn)模型是建立在方法攔截上。這意味著你編寫的Spring通知會(huì)在方法調(diào)用周圍的各個(gè)地方織入系統(tǒng)中。通知的類型有:Around,Before,After,Throws
前置通知:需要擴(kuò)展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>
<!-- 創(chuàng)建代理目標(biāo)對(duì)象 -->
<bean id="kwikEMartTarget" class="com.springinaction.chapter03.store.ApuKwikEMart"></bean>
<!-- 創(chuàng)建通知 -->
<bean id="welcomeAdvice" class="com.springinaction.chapter03.store.WelcomeAdvice"></bean>
<!-- 創(chuàng)建代理對(duì)象 -->
<bean id="kwikEMart" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理類實(shí)現(xiàn)的接口 -->
<property name="proxyInterfaces">
<value>com.springinaction.chaper03.store.kwikEMart</value>
</property>
<!-- 要織入的通知 -->
<property name="interceptorNames">
<list>
<value>welcomeAdvice</value>
</list>
</property>
<!-- 要代理的目標(biāo)對(duì)象 -->
<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>
<!-- 創(chuàng)建代理目標(biāo)對(duì)象 -->
<bean id="kwikEMartTarget" class="com.springinaction.chapter03.store.ApuKwikEMart"></bean>
<!-- 創(chuàng)建通知 -->
<bean id="welcomeAdvice" class="com.springinaction.chapter03.store.WelcomeAdvice"></bean>
<!-- 創(chuàng)建代理對(duì)象 -->
<bean id="kwikEMart" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理類實(shí)現(xiàn)的接口 -->
<property name="proxyInterfaces">
<value>com.springinaction.chaper03.store.kwikEMart</value>
</property>
<!-- 要織入的通知 -->
<property name="interceptorNames">
<list>
<value>welcomeAdvice</value>
</list>
</property>
<!-- 要代理的目標(biāo)對(duì)象 -->
<property name="target">
<ref bean="kwikEMartTarget"/>
</property>
</bean>
</beans>
ProxyFactoryBean類是一個(gè)在BeanFactory中顯示的創(chuàng)建代理對(duì)象的中心類。像我們展示的那樣,你可以給它一個(gè)要實(shí)現(xiàn)的接口,一個(gè)要代理的目標(biāo)對(duì)象,一個(gè)要織入的通知,并且它將創(chuàng)建一個(gè)嶄新的代理對(duì)象。通常配置ProxyFactoryBean,讓它實(shí)現(xiàn)和目標(biāo)對(duì)象一樣的接口。
后置通知:需要實(shí)現(xiàn)AfterReturningAdvice
public interface AfterReturningAdvice{
void afterReturning(Object returnValue,Method method,Object[] args,Object target)throws Throwable;
}
環(huán)繞通知:需要實(shí)現(xiàn)MethodInterceptor,同時(shí)實(shí)現(xiàn)前置和后置通知
public interface MethodInterceptor extends Interceptor{
Object invoke(MethodInvocation invocation)throws Throwable;
}
MethodInterceptor接口和前面介紹的2種通知不同點(diǎn):
1、MethodInterceptor能夠控制目標(biāo)方法是否真的被調(diào)用。通過調(diào)用MethodInvocation.proceed()方法來調(diào)用目標(biāo)方法。這一點(diǎn)不同于前兩個(gè),目標(biāo)方法總是被調(diào)用的。
2、MethodInterceptor讓你可以控制返回的對(duì)象。就是說你可以返回一個(gè)與proceed()方法返回對(duì)象完全不同的對(duì)象。
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 創(chuàng)建時(shí)間:2009-11-5 下午05:19:19
* 類說明
*/
public class OnePerCustomerInterceptor implements MethodInterceptor {
//定義包含用戶的集合
private Set customers = new HashSet();
/*
* 當(dāng)你在方法調(diào)用的前后都需要交叉切面邏輯時(shí),應(yīng)該使用MethodInterceptor。由于你必須要記得顯示調(diào)用
* invocation.proceed()方法,所以,在滿足要求的情況下,最好還是使用MethodBeforeAdvice或
* AfterReturningAdvice.
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
Customer customer = (Customer)invocation.getArguments()[0];
if(customers.contains(customer)){
System.out.println("拋出異常");
}
//調(diào)用目標(biāo)方法
Object squishee = invocation.proceed();
//添加用戶
customers.add(customer);
//返回目標(biāo)方法結(jié)果
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 創(chuàng)建時(shí)間:2009-11-5 下午05:19:19
* 類說明
*/
public class OnePerCustomerInterceptor implements MethodInterceptor {
//定義包含用戶的集合
private Set customers = new HashSet();
/*
* 當(dāng)你在方法調(diào)用的前后都需要交叉切面邏輯時(shí),應(yīng)該使用MethodInterceptor。由于你必須要記得顯示調(diào)用
* invocation.proceed()方法,所以,在滿足要求的情況下,最好還是使用MethodBeforeAdvice或
* AfterReturningAdvice.
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
Customer customer = (Customer)invocation.getArguments()[0];
if(customers.contains(customer)){
System.out.println("拋出異常");
}
//調(diào)用目標(biāo)方法
Object squishee = invocation.proceed();
//添加用戶
customers.add(customer);
//返回目標(biāo)方法結(jié)果
return squishee;
}
}