Spring
中的
AOP
創(chuàng)建時(shí)間:
2007年1月27日
星期六
????????????????? ?零雨其蒙原創(chuàng),轉(zhuǎn)載請(qǐng)注明
一、概述
(一)基本概念
1
、什么是AOP?
???
面向方面編程。所謂方面即是指日志、權(quán)限、異常處理、事務(wù)處理等。
2
、AOP的3個(gè)關(guān)鍵概念
??
(
1
)切入點(diǎn)(
Pointcut
):
Pointcut
是
Join Point
的集合,
Join Point
就是需要注入
Adivce
的位置,也就是需要插入日志輸出代碼、事務(wù)處理代碼等“方面”(
Aspect
,也就是
AOP
中的
A
)代碼的地方。
???
比如我現(xiàn)在要寫一個(gè)存錢的方法:
saving
()
???
通常情況下我就得在這個(gè)
saving
()方法前后寫些事務(wù)代碼
???
如:
?
????????? logger.log(Level.INFO,”start”);
Saving();
???????????????????????? logger.log(Level.INFO,”end”);
??????
????????
對(duì)于事務(wù)代碼而言,
saving
()方法的前后就都是
Join Point
了。在
Spring
中它對(duì)應(yīng)
config.xml
中設(shè)定的方法,這個(gè)方法就是類(
class
)中需要進(jìn)行某方面處理的方法(
method
)。
??????
?
(
2
)通知(
Advice
):
就是指
Join Point
對(duì)應(yīng)的代碼(方法)。比如日志輸出這個(gè)方面,指的就是日志輸出的代碼或方法了。在
Spring
中,它對(duì)應(yīng)類(
class
)。
???????
(
3
)
Advisor
:
是
Poincut
和
Advice
的配置器,它包括
Pointcut
和
Advice
,是將
Advice
注入程序中
Pointcut
位置的代碼。在
Sping
中,它對(duì)應(yīng)
config.xml
中的配置段
<bean id=logAdvisor class=”org.springframework.aop.support.RegexpMethodPointcutAdvisor”>
。
?
<bean id=”logAdvisor” class=”org.springframework.aop.support.RegexpMetho
dPointcutAdvisor”>
???
//advice
屬性對(duì)應(yīng)的當(dāng)然就是輸出日志的類,也就是
①
對(duì)應(yīng)的那個(gè)bean
??? <property name=”advice”>
???????? <ref bean=”log”/>
??? </property>
??
//patterns
屬性指出指定要代理的方法,使用的是正則表達(dá)式
??? <property name=”patterns”>
???????? <value>.*doAuditing.*</value>
??? </property>
</bean>
?
(二)框架圖
?
創(chuàng)建代理的兩種方法
|
ProxyFactoryBean
|
動(dòng)態(tài)代理
|
?
|
Spring4
種
Advice
|
Interception Around
|
Before
|
After Returning
|
Throw
|
?
|
兩種代理方式
|
Java
動(dòng)態(tài)代理
|
CGLIB
代理
|
?
|
(三)何時(shí)使用什么
? 1
、創(chuàng)建代理的兩種方法中首選動(dòng)態(tài)代理。
第
1
種:針對(duì)某個(gè)類進(jìn)行配置。可以指定某個(gè)類中所有方法都調(diào)用方面,也可以指定某個(gè)類中的某個(gè)方法,此時(shí)由于用到正則表達(dá)式,于是需要引入
jakarta-oro-2.0.8.jar
包。
第
2
種:針對(duì)某個(gè)方法進(jìn)行配置。
? 2
、Spring4種Advice:
?????
第
1
種:在需要調(diào)用方面的方法前后都調(diào)用處理方面的代碼
?????
第
2
種:在需要調(diào)用方面的方法之前調(diào)用處理方面的代碼
第
3
種:在需要調(diào)用方面的方法之后都調(diào)用處理方面的代碼
第
4
種:在需要調(diào)用方面的方法發(fā)生異常時(shí)調(diào)用處理方面的代碼
3
、兩種代理方式首選第1種。
?????????
第
1
種:面向接口,必須先定義接口,這是好的習(xí)慣,應(yīng)該提倡
?????????
第
2
種:當(dāng)沒有接口的時(shí)候,可以使用這種方法。需引入
cglib-nodep-2.1_3,jar
包。
二、詳細(xì)
(一)、創(chuàng)建AOP代理的兩種方法:
1
、用ProxyFactoryBean創(chuàng)建AOP代理
??
(需要指明代理目標(biāo)類)
(
1
)代理目標(biāo)類的所有方法
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
?"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
//
下面
interceptorNames
屬性(property)中的value值就是這個(gè)bean的id,其主要對(duì)應(yīng)的是寫入日志的那個(gè)類,也就是Spring AOP概念中的Advice(通知)。
①
<bean id="log" class="com.gc.action.LogAround"/>
//
要輸出日志的那個(gè)類(因?yàn)檫@種方法必須要指明代理目標(biāo)類)
②
<bean id="timeBook" class="com.gc.action. timeBook "/>
//
下面開始定義代理類,也就是ProxyFactoryBean,這是Spring自帶的類,這也是Spring AOP中的Advisor
<bean id=”logProxy” class=”org.springframework.aop.framework.ProxyFactor
yBean
”>
?//
第一個(gè)屬性,是指明要代理的類的接口,因?yàn)檫@個(gè)例子中使用的是Java動(dòng)態(tài)代理機(jī)制來實(shí)現(xiàn)AOP的,因此必須指明接口
??? <property name=”proxyInterfaces”>
??????? <value>com.gc.impl.TimeBookInterface</value>
??? </property>
//
這個(gè)屬性是指明代理目標(biāo)(target)類,也就是
②
定義的那個(gè)類
??
?<property name=”target”>
??????? <ref bean=”timeBook”/>
??? </property>
//
這個(gè)屬性是用來指明插入哪個(gè)Advice,此處使用list,應(yīng)該表示這個(gè)類不只是可以調(diào)用這一個(gè)log類
??? <property name=”interceptorNames”>
???????? <list>
????
?????????<value>log</value>
//
這個(gè)值(log)對(duì)應(yīng)
①
中定義的那個(gè)id為log的bean
???????? </list>
??? </property>
</bean>
</beans>
(
2
)代理目標(biāo)類的指定方法
<?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="log" class="com.gc.action.LogAround"/>
<bean id="timeBook" class="com.gc.action. timeBook "/>
//
在上一段配置文件中添加了下面這個(gè)bean,用來指明要輸出日志的指定方法(上一個(gè)例子是所有方法都輸出日志)
<bean id=”logAdvisor” class=”org.springframework.aop.support.RegexpMetho
dPointcutAdvisor”>
???
//advice
屬性對(duì)應(yīng)的當(dāng)然就是輸出日志的類,也就是
①
對(duì)應(yīng)的那個(gè)bean
??? <property name=”advice”>
???????? <ref bean=”log”/>
??? </property>
??
//patterns
屬性指出指定要代理的方法,使用的是正則表達(dá)式
??? <property name=”patterns”>
???
?????<value>.*doAuditing.*</value>
??? </property>
</bean>
<bean id=”logProxy” class=”org.springframework.aop.framework.ProxyFactor
yBean
”>
??? <property name=”proxyInterfaces”>
??????? <value>com.gc.impl.TimeBookInterface</value>
??? </property>
??? <property name=”target”>
??????? <ref bean=”timeBook”/>
??? </property>
??? <property name=”interceptorNames”>
???????? <list>
????????????? <value>log</value>
???????? </list>
??? </property>
</bean>
</beans>
?
2
、用DefaultAdvisorAutoProxyCreator創(chuàng)建自動(dòng)代理
??
(好處:不用指明代理目標(biāo)類,如果一個(gè)大項(xiàng)目中有很多類也不必一個(gè)一個(gè)設(shè)置
AOP
代理)
<?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="log" class="com.gc.action.Log
Aop
"/>
<bean id="timeBook" class="com.gc.action. timeBook "/>
//
使用DefaultAdvisorAutoProxyCreator(紅色代碼)替代ProxyFactoryBean(綠色代碼),因?yàn)榫G色代碼的作用是為具體的類(即所謂代理目標(biāo)類)設(shè)置advice。
<bean id=”autoProxyCreator” class=”org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator”>
<bean id=”logAdvisor
” class=”org.springframework.aop.support.RegexpMetho
dPointcutAdvisor”>
?
?? <property name=”advice”>
???????? <ref bean=”log”/>
??? </property>
?
?? <property name=”patterns”>
??????
??<value>.*doAuditing.*</value>
??? </property>
</bean>
/*<bean id=”logProxy” class=”org.springframework.aop.framework.ProxyFactor
yBean”>
??? <property name=”proxyInterfaces”>
??????? <value>com.gc.impl.TimeBookInterface</value>
??? </property>
??? <property name=”target”>
??????? <ref bean=”timeBook”/>
??? </property>
??? <property name=”interceptorNames”>
???????? <list>
????????????? <value>log</value>
???????? </list>
??? </property>
</bean>*/
</beans>
?
3
、總結(jié)
實(shí)際上,
DefaultAdvisorAutoProxyCreator
和
ProxyFactoryBean
就是兩種代理類,前者是自動(dòng)的將
Advisor
和目標(biāo)類聯(lián)系起來,后者是通過指定的方式,將目標(biāo)類和
Advisor
組合起來。
而
advisor
,對(duì)應(yīng)的就是
org.springframework.aop.support.RegexpMethodPointcutAdvisor
,通過正則表達(dá)式來匹配類中的方法(設(shè)定
Pointcut
)。
(二)Spring四種通知(Advice)形式
1
、Interception Around通知
??????
(
1
)負(fù)責(zé)輸出日志的類
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
?
public class LogAround implements MethodInterceptor{
????? private Logger logger = Logger.getLogger(this.getClass().getName());
???
??? public Objectinvoke(MethodInvocation methodInvocation) throws Throwable {
?????????? logger.log(Level.INFO, methodInvocation.getArguments()[0] + "
開始審核數(shù)據(jù)....");?
??????? try {
????????? Object result = methodInvocation.proceed();
????????? return result;
??????? }
??????? finally {
???????????????? logger.log(Level.INFO, methodInvocation.getArguments()[0] + "
審核數(shù)據(jù)結(jié)束....");
??????? }???????
?? }
}?????
?
(
2
)配置文件
<?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="log" class="com.gc.action.Log
Aop
"/>
<bean id="timeBook" class="com.gc.action. timeBook "/>
<bean id=”logProxy” class=”org.springframework.aop.framework.ProxyFactor
yBean”>
??? <property name=”proxyInterfaces”>
??????? <value>com.gc.impl.TimeBookInterface</value>
??? </property>
??? <property name=”target”>
??????? <ref bean=”timeBook”/>
??? </property>
??? <property name=”interceptorNames”>
???????? <list>
????????????? <value>log</value>
???????? </list>
??? </property>
</bean>
</beans>
?
?
2
、Before通知
(
1
)負(fù)責(zé)輸出日志的類
import java.lang.reflect.Method;
?
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.springframework.aop.MethodBeforeAdvice;
?
public class LogBefore implements MethodBeforeAdvice {
?????? private Logger logger = Logger.getLogger(this.getClass().getName());
???
??? public void before(Method method, Object[] args, Object target) throws Throwable {
????????????? logger.log(Level.INFO, args[0] + "
開始審核數(shù)據(jù)
....");
?? }
}
(
2
)配置文件
????
與第
1
種方法相同。
3
、After Returning通知
(
1
)負(fù)責(zé)輸出日志的類
import java.lang.reflect.Method;
?
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.springframework.aop.AfterReturningAdvice;
?
public class LogAfterReturning implements AfterReturningAdvice {
?????? private Logger logger = Logger.getLogger(this.getClass().getName());
???
??? public void afterReturning(Method method, Object[] args, Object target) throws Throwable {
????????????? logger.log(Level.INFO, args[0] + "
開始審核數(shù)據(jù)
....");
?? }
}
(
2
)配置文件
????
與第
1
種方法相同。
4
、Throw通知
(
1
)負(fù)責(zé)輸出日志的類
import java.lang.reflect.Method;
?
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.springframework.aop.ThrowsAdvice;
?
public class LogThrow implements ThrowsAdvice {
?????? private Logger logger = Logger.getLogger(this.getClass().getName());
???
??? public void afterThrowing(Method method, Object[] args, Object target,Throwable subclass) throws Throwable {
????????????? logger.log(Level.INFO, args[0] + "
開始審核數(shù)據(jù)
....");
?? }
}
(
2
)配置文件
????
與第
1
種方法相同。
5
、測(cè)試程序
??
(
1
)使用自動(dòng)代理
public class TestHelloWorld {
?????? public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
????????????? ApplicationContext actx=new FileSystemXmlApplicationContext("config.xml");
????????????? TimeBookInterface timeBookProxy = (TimeBookInterface)actx.getBean("timeBook");
????????????? timeBookProxy.doAuditing("
張三
");
??
(
2
)不使用自動(dòng)代理
public class TestHelloWorld {
?????? public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
????????????? ApplicationContext actx=new FileSystemXmlApplicationContext("config.xml");
????????????? TimeBookInterface timeBookProxy = (TimeBookInterface)actx.getBean("logProxy");
????????????? timeBookProxy.doAuditing("
張三
");
使用自動(dòng)代理,則直接調(diào)用該類的名字(
timeBook
),否則調(diào)用相應(yīng)的代理
bean
(
logProxy
)。因?yàn)榈?/span>
1
種方法中根本就沒有對(duì)應(yīng)的代理
bean
,只有一個(gè)
Spring
的自動(dòng)代理類
?
(三)Spring兩種代理方式
1
、Java動(dòng)態(tài)代理
(
1
)配制文件(不使用自動(dòng)代理)
<?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="log" class="com.gc.action.Log
Aop
"/>
<bean id="timeBook" class="com.gc.action. timeBook "/>
<bean id=”logProxy” class=”org.springframework.aop.framework.ProxyFactor
yBean”>
??? <property name=”proxyInterfaces”>
??????? <value>com.gc.impl.TimeBookInterface</value>
??? </property>
??? <property name=”target”>
??????? <ref bean=”timeBook”/>
??? </property>
??? <property name=”interceptorNames”>
???????? <list>
????????????? <value>log</value>
???????? </list>
??? </property>
</bean>
</beans>
?
(
2
)測(cè)試程序
public class TestHelloWorld {
?????? public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
????????????? ApplicationContext actx=new FileSystemXmlApplicationContext("config.xml");
????????????? TimeBookInterface timeBookProxy = (TimeBookInterface)actx.getBean("logProxy");
????????????? timeBookProxy.doAuditing("
張三
");
?
2
、CGLIB代理
(
1
)配制文件(不使用自動(dòng)代理)
<?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="log" class="com.gc.action.Log
Aop
"/>
<bean id="timeBook" class="com.gc.action. timeBook "/>
<bean id=”logProxy” class=”org.springframework.aop.framework.ProxyFactor
yBean”>
//
增加如下屬性,就表示使用的是CGLIB代理(對(duì)目標(biāo)類直接代理)
??? <property name=”proxyTargetClass”>
??????? <value>true</value>
??? </property>
?? /*
然后去掉下面的屬性,也就是說此種方法不需要面向接口,或不需要指出接口
<property name=”proxyInterfaces”>
??????? <value>com.gc.impl.TimeBookInterface</value>
??? </property>*/
??? <property name=”target”>
??????? <ref bean=”timeBook”/>
??? </property>
??? <property name=”interceptorNames”>
???????? <list>
???????????
??<value>log</value>
???????? </list>
??? </property>
</bean>
</beans>
?
(
2
)測(cè)試程序
public class TestHelloWorld {
?????? public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
????????????? ApplicationContext actx=new FileSystemXmlApplicationContext("config.xml");
????????????? TimeBookInterface timeBookProxy = (TimeBookInterface)actx.getBean("logProxy");
????????????? timeBookProxy.doAuditing("
張三
");
?
?