Spring只支持方法的JoinPoints,也就是Adivices將在方法執(zhí)行的前后被引用,Spring不支持Field成員的Joinpoints,這是因為Spring的設計哲學中認為,支持Field成員的Joinpoints會破壞對象的封裝性。
Advices
Advices實現(xiàn)了Aspectj的真正邏輯,根據(jù)蔣這些通知織入Targets的時機不同,Spring提供了幾種不同的通知,比如,Before、After、Around、Throw。
1.Before
/**
* 前置通知,在目標方法調(diào)用之前執(zhí)行
* @author Administrator
*
*/
public class BeforeAdvicesTest implements MethodBeforeAdvice{
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("before execute");
}
}
<bean id="before" class="com.bhsc.AOP.BeforeAdvicesTest"></bean>
<bean id="target"
class="com.bhsc.AOP.target.TargetImple"></bean>
<bean id="helloProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" 、
value="com.bhsc.AOP.target.TargetInterface"/>
<property name="target" ref="target"/>
<property name="interceptorNames">
<list>
<value>before</value>
</list>
</property>
</bean>
通知實現(xiàn)了MethodBeforeAdvice,在配置中建立了Advice,Target,還使用了ProxyFactoryBean,這個類會被BeanFactory,ApplicationContext,用來建立代理對象,并配置了實現(xiàn)的接口,在不指定目標方法時,Before Advice會被織入到接口上的所有方法。
Advice的Target并不是動態(tài)代理中的代理對象,而是真實的對象,也就是動態(tài)代理中傳入的真正的類。
拿動態(tài)代理來對比下:
Object proxy = Proxy.newProxyInstance(Monkey.class.getClassLoader(),
Monkey.class.getInterfaces(),
new MyInvocationHandler( new Monkey()));
可以看到動態(tài)代理中,真正的類是new時候傳入的,而Spring則配置在文件中注入了。
2.After 和Before類似
3.Around
/**
* Around通知,在真正執(zhí)行某個方法前,會先插入Interceptor,如果有多個,則會一個個執(zhí)行,然后執(zhí)行method的方法,將執(zhí)行流程轉(zhuǎn)給下一個Interceptor,最后一層層返回堆棧,最后離開堆棧返回應用程序本身的流程。
* @author Administrator
*
*/
public class MethodInterceptorTest implements MethodInterceptor{
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("execute method:"+methodInvocation.getMethod());
Object result=null;
result=methodInvocation.proceed();
return result;
}
}
<!-- around -->
<bean id="around"
class="com.bhsc.AOP.MethodInterceptorTest"></bean>
<bean id="helloProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"
value="com.bhsc.AOP.target.TargetInterface"/>
<property name="target" ref="target"/>
<property name="interceptorNames">
<list>
<value>around</value>
</list>
</property>
</bean>
1.Throw 如果拋出的異常類型和發(fā)生的類型相同,那么就會執(zhí)行通知方法。
前面的Advice,一定程度上也包括切入點,也就是一個類的所有方法,不過這些都是粗粒度的,事實上,我們可以定義更細粒度的,定義需要的方法,那就是PointCut。
PointCut定義了Joinpoint的集合,表示Advice應用的時機(方法)。在Spring中,用PointcutAdvisor表示PointCut和Advisor的結(jié)合,就是aspect。PointcutAdvisor是Advisor的子接口:
public interface Advisor {
/**
* @return whether this advice is associated with a particular target instance
*/
boolean isPerInstance();
public interface PointcutAdvisor extends Advisor {
/**
* Get the Pointcut that drives this advisor.
*/
Pointcut getPointcut();
}
分別有NameMatchMethodPointcutAdvisor、RegexpMethodPointcutAdvisor、ControlFlowPointcut
<!-- NameMatchMethodPointcutAdvisor,以llo結(jié)尾的方法,使用前面定義的before通知,mappedName可以使用集合注入,那就是nammedNames -->
<bean id="helloAdvisor" class="org.springframework.aop.
support.NameMatchMethodPointcutAdvisor">
<property name="mappedName" value="*llo"/>
<property name="advice" ref="before"/>
<bean id="helloAdvisorProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"
value="com.bhsc.AOP.target.TargetInterface"/>
<property name="target" ref="target"/>
<property name="interceptorNames">
<list>
<value>helloAdvisor</value>
</list>
</property>
在target中加入nihao方法:
public void nihao(String name){
System.out.println("你好 :"+name);
}
TargetInterface ti=(TargetInterface)ctx.getBean("helloProxy");
ti=(TargetInterface)ctx.getBean("helloAdvisorProxy");
ti.hello("lisi");
ti.nihao("lisi");
before execute
hello :lisi
你好 :lisi
3.ControlFlowPointcut
用來判斷某個方法中,是否要求目標對象執(zhí)行某個方法,在執(zhí)行時期動態(tài)決定是否介入Advice來提供服務,所以這是動態(tài)的PointCut功能。
public class Some implements ApplicationContextAware{
private TargetInterface target;
applicationContext)throws BeansException {
target=(TargetInterface)applicationContext.getBean("");
}
public void helloEverybody(){
target.hello("wangwu");
target.nihao("qw");
}
public void hello(){
System.out.println("hello!");
}
boolean flag=true;
if(flag){
some.helloEverybody();
}else{
some.hello();
}
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
Pointcut TRUE = TruePointcut.INSTANCE;
}
TruePointcut是PointCut的簡單實現(xiàn),傳回的ClassFilter是ClassFilter.TRUE,傳回的MethodMatcher是MethodMatcher.TRUE;
boolean matches(Class clazz);
ClassFilter TRUE = TrueClassFilter.INSTANCE;
}
MethodMatcher決定某個方法是否要應用Advice:
boolean matches(Method method, Class targetClass);
boolean isRuntime();
boolean matches(Method method, Class targetClass, Object[] args);
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
如果是靜態(tài)的PointCut,比如前面的NameMatchMethodPointcutAdvisor ,
RegexpMethodPointcutAdvisor,則執(zhí)行第一個方法,isRuntime返回FALSE,否則執(zhí)行第二個方法,isRuntime返回true。
Introduction
是一種特殊的Advice,從行為上來看,它不像前面的Before,After等,在方法前后介入服務,而是直接介入整個對象的行為,就好像憑空多了一些可操作的行為,為對象動態(tài)加入(Mixin)原先所沒有的職責。
<!-- Mixin -->
<!-- 原來的 -->
<bean id="someService" class="onlyfun.caterpillar.Some" />
<!-- 新加的 -->
<bean id="otherIntroduction"
class="onlyfun.caterpillar.OtherIntroduction" />
"org.springframework.aop.support.DefaultIntroductionAdvisor">
<constructor-arg ref="otherIntroduction" />
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"
value="onlyfun.caterpillar.ISome" />
<property name="target" ref="someService" />
<property name="interceptorNames">
<list>
<value>introductionAdvisor</value>
</list>
</property>
</bean>
ApplicationContext ctx = new
ClassPathXmlApplicationContext("Aop.xml");
ISome some=(ISome)ctx.getBean("proxyFactoryBean");
some.doSome();
//看起來像是增加了職責
原來對象的職責。。。
Autoproxing
自動代理可以不用為每一個目標對象手動代理對象,使用自動代理,可以通過Bean名稱或是Pointcut對比,自動為符合對比條件的目標對象建立代理對象。
1.BeanNameAutoProxyCreator
看一下前面的配置:
<bean id="before" class="com.bhsc.AOP.BeforeAdvicesTest"></bean>
<bean id="target"
class="com.bhsc.AOP.target.TargetImple"></bean>
<bean id="helloProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" 、
value="com.bhsc.AOP.target.TargetInterface"/>
<property name="target" ref="target"/>
<property name="interceptorNames">
<list>
<value>before</value>
</list>
</property>
</bean>
不僅要定于Advice,target,還要定義一個代理對象,配置接口,target等,如果應用比較大,那么一個個配置是很麻煩的,PointcutAdvisor的定義只是對切入點更準確的定義,并不能解決配置繁瑣的問題(NameMatchMethodPointcutAdvisor),不過Spring提供了自動代理,可以更加簡單的配置。
比如,我們?yōu)?font face="Times New Roman">Bean取好名稱,xxxService,這樣,就可以使用BeanNameAutoProxyCreator配置:
<!-- 自動代理 -->
<bean id="introductionProxyCreator"
class="org.springframework.
aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames" value="introductionAdvisor" />
<property name="beanNames">
<list>
<value>*Service</value>
</list>
</property>
</bean>
ISome someService=(ISome)ctx.getBean("someService");
someService.doSome();
((IOther)someService).doOther();
2.DefaultAdvisorAutoProxyCreator:自動搜尋