(1):切點接口定義正則表達式
org.springframework.aop.Pointcut
接口是中心接口。用來將Advice(通知)定位到特定的類和方法
。spring
public interface Pointcut { ClassFilter getClassFilter(); MethodMatcher getMethodMatcher(); }
ClassFilter
接口用來決定切點做用的類。若是matches()
方法老是返回true
,全部的目標均匹配。express
public interface ClassFilter { boolean matches(Class clazz); }
MethodMatcher
接口一般更重要。數組
public interface MethodMatcher { boolean matches(Method m, Class targetClass); boolean isRuntime(); boolean matches(Method m, Class targetClass, Object[] args); }
大多數MethodMatcher
實現是靜態的,意味着它們的isRuntime()
返回false
。因爲這個緣由,三參數的matches
方法永遠不會被調用。緩存
`注意:springboot
若是可能的話,讓切點爲靜態,容許AOP框架緩存切點計算結果當AOP代理被建立的時候。框架
(2):切點操做性能
Spring支持切點操做有聯合(union)和交集(intersection)。聯合意味着方法任意一個匹配便可,交集優化
意味着全部切點都匹配。然而,使用AspectJ point expression
一般更簡便。this
AspectJExpressionPointcut
:AspectJ point expression
。
(3):便利切點實現
靜態切點(Static Pointcuts)
靜態切入點基於方法和目標類
,不能考慮方法參數。對於大多數用途,靜態切入點足夠而且最好。首次調用方法,Spring只評估一次靜態切入點。以後,無需再次使用每一個方法調用來評估切入點。
正則表達式切入點(Regular Expression Pointcuts)
JdkRegexpMethodPointcut
:JDK支持的正則表達式。你能夠設置正則表達式的列表,任何一個知足,即切點評估爲true
。
動態切點(Dynamic Pointcuts)
動態切點,評估成本比靜態成本高。考慮了方法參數和靜態信息。這意味着必須使用每一個方法調用來評估它們,而且不能緩存結果,由於參數會有所不一樣。
控制流切點(Control Flow Pointcuts)
和AspectJ的cflow
相似。
(4):切點父類(Pointcut Superclasses)
因爲static pointcuts
是最有用的,StaticMethodMatcherPointcut
。使用示例以下:
class TestStaticPointcut extends StaticMethodMatcherPointcut { public boolean matches(Method m, Class targetClass) { // return true if custom criteria match } }
Advice API in Spring
(1):通知生命週期
每個通知都是一個Spring Bean
。通知示例能夠在全部通知對象之間共享
。
(2):Spring通知類型
Around Advice(環繞通知)
Spring最基本的通知是環繞通知。使用了method interception
。類實現MethodInterceptor
。
public interface MethodInterceptor extends Interceptor { /** * Implement this method to perform extra treatments before and * after the invocation. Polite implementations would certainly * like to invoke {@link Joinpoint#proceed()}. * @param invocation the method invocation joinpoint * @return the result of the call to {@link Joinpoint#proceed()}; * might be intercepted by the interceptor * @throws Throwable if the interceptors or the target object * throws an exception */ Object invoke(MethodInvocation invocation) throws Throwable; } /** * Description of an invocation to a method, given to an interceptor * upon method-call. * * <p>A method invocation is a joinpoint and can be intercepted by a * method interceptor. */ public interface MethodInvocation extends Invocation { /** * Get the method being called. * <p>This method is a frienly implementation of the * {@link Joinpoint#getStaticPart()} method (same result). * @return the method being called */ Method getMethod(); } public interface Invocation extends Joinpoint { /** * Get the arguments as an array object. * It is possible to change element values within this * array to change the arguments. * @return the argument of the invocation */ Object[] getArguments(); } public interface Joinpoint { /** * Proceed to the next interceptor in the chain. * <p>The implementation and the semantics of this method depends * on the actual joinpoint type (see the children interfaces). * @return see the children interfaces' proceed definition * @throws Throwable if the joinpoint throws an exception */ Object proceed() throws Throwable; /** * Return the object that holds the current joinpoint's static part. * <p>For instance, the target object for an invocation. * @return the object (can be null if the accessible object is static) */ Object getThis(); /** * Return the static part of this joinpoint. * <p>The static part is an accessible object on which a chain of * interceptors are installed. */ AccessibleObject getStaticPart(); }
invoke()方法的MethodInvocation
參數公開了被調用的方法,目標鏈接點,AOP代理和方法的參數。 invoke()方法應該返回調用的結果:鏈接點的返回值
。
Before Advice(前置通知)
簡單的通知類型是前置通知。不須要MethodInvocation
對象。它是在進入方法以前調用
。
前置通知主要優勢不須要調用proceed()方法
,所以不會無心中沒法繼續攔截鏈。
public interface MethodBeforeAdvice extends BeforeAdvice { void before(Method m, Object[] args, Object target) throws Throwable; }
Spring API設計容許在通知以前提供字段,儘管一般的對象適用於字段攔截,可是Spring不太可能實現它。請注意,返回類型爲void
。在通知能夠在鏈接點執行以前插入自定義行爲可是不能更改返回值以前。若是before advice拋出異常,則會停止攔截器鏈進一步執行。異常傳播回攔截器鏈。
返回全部方法的調用次數
public class CountingBeforeAdvice implements MethodBeforeAdvice { private int count; public void before(Method m, Object[] args, Object target) throws Throwable { ++count; } public int getCount() { return count; } }
Throws Advice(異常通知)
異常通知在返回鏈接點後,若是鏈接點跑出異常,則該通知被調用。ThrowsAdvice
,該接口是一個標記接口,繼承了AfterAdvice
。示例以下:異常參數必須存在,其餘參數像method,arguments是否存在,取決你是否須要。
public void afterThrowing(Exception ex){ } public void afterThrowing(RemoteException){ } public void afterThrowing(Method method, Object[] args, Object target, Exception ex){ } public void afterThrowing(Method method, Object[] args, Object target, ServletException ex){ }
注意
不要拋出與目標籤名方法不兼容的未聲明的已檢查異常
。
後置返回通知(After Returning Advice)
後置返回通知須要實現`AfterReturningAdvice
`。
public interface AfterReturningAdvice extends Advice { void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable; }
後置返回通知能夠訪問返回值(可是它不能修改),被調用的方法,方法參數,目標對象
.若是它拋出異常,
則拋出攔截器鏈而不是返回值。
統計全部成功調用可是沒有拋出異常的方法次數
public class CountingAfterReturningAdvice implements AfterReturningAdvice { private int count; public void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable { ++count; } public int getCount() { return count; } }
引入通知(Introduction Advice)
引入通知須要一個IntroductionAdvisor
和IntroductionInterceptor
,以下實現
public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice { } public interface DynamicIntroductionAdvice extends Advice { /** * Does this introduction advice implement the given interface? * @param intf the interface to check * @return whether the advice implements the specified interface */ boolean implementsInterface(Class<?> intf); }
invoke()
方法繼承了AOP aopalliance MethodInterceptor
接口。若是調用的方法在引入的接口上,
則引入攔截器負責處理調用,它不能調用proceed()
。
引入通知不能適用於任意切點。它僅適用於類,而不是方法級別的。
只能在IntroductionAdvisor
中使用引入通知。
public interface IntroductionAdvisor extends Advisor, IntroductionInfo { ClassFilter getClassFilter(); void validateInterfaces() throws IllegalArgumentException; } public interface IntroductionInfo { Class[] getInterfaces(); }
僅僅含有class過濾邏輯。getInterfaces()
返回切面引入的接口。validateInterfaces()
方法被用於判斷引入的接口是否能被IntroductionInterceptor
配置。
類DelegatingIntroductionInterceptor
被設計成代理一個引入給實際實現引入的接口。
在Spring中,Advisor
是一個僅含有一個通知對象和一個切點表達式對象關聯
的切面
。
`DefaultPointcutAdvisor
`是最通用advisor。
(2):使用ProxyFactoryBean
建立AOP代理
在Spring中建立AOP代理的基本方法是使用ProxyFactoryBean
。他能夠徹底控制切入點,任何適用通知以及它們順序。
JavaBean屬性
一些key屬性繼承自ProxyConfig
。
proxyTargetClass
:若是代理的是目標類,而不是接口,該值爲true。
默認是false
。
optimize
:控制是否將積極優化應用與經過CGLIB建立的代理。除非您徹底瞭解相關AOP的優化,不然不該該輕易使用此設置。
frozen
:若是代理配置被凍結,則再也不容許更改配置。默認爲false
。
exposeProxy
:決定是否將當前代理對象暴露到ThreadLocal中,以即可以被目標對象訪問
。能夠經過
AopContext.currentProxy()
獲取。
其餘屬性來自於ProxyFactoryBean
。
proxyInterfaces
:代理的接口數組。若是沒有被支持,則CGLIB代理被使用.
interceptorNames
:攔截器數組(Advisor
)。這些名字存在當前bean工廠中,包含祖先工廠。
singleton
:決定工廠是否返回單例對象,默認爲true
。
(3):JDK和CGLIB代理
若是一個目標對象沒有實現任何接口,則使用CGLIB代理。
若是一個目標對象實現了任何一個接口,默認使用JDK代理
若是一個目標對象實現了任何一個接口,可是proxyTargetClass
屬性爲true
,使用GGLIB代理。
(4):代理接口
interceptorNames
屬性持有一個列表,這個列表是攔截器(MethoInterceptor)
或者(Advisor
)
在當前工廠的bean names
。
注意:
您可能想知道爲何列表不包含bean引用。 緣由是,若是ProxyFactoryBean的singleton屬性設置爲false,則它必須可以返回獨立的代理實例. 若是任何顧問自己就是原型,則須要返回一個獨立的實例,所以必須可以從工廠得到原型的實例. 持有引用是不夠的.
(5):代理類
CGLIB代理的侷限性
final方法不能被通知。因爲它們不能被重寫
Spring3.2,CGOLIB被從新打包到spring-core jar中。
(6):使用全局(Advisors)
經過在攔截器後附加星號
,將全部與星號前面的部分匹配的bean名稱的advisor程序添加到攔截器鏈中。以下所示:
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="service"/> <property name="interceptorNames"> <list> <value>global*</value> </list> </property> </bean> <bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor"/> <bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor"/>
(7):使用ProxyFactory
建立AOP代理
一個目標對象,一個通知,一個顧問。
@Bean public AspectJProxyFactory aspectJProxyFactory() { AspectJProxyFactory proxyFactoryBean = new AspectJProxyFactory(new UserService()); String expression = "execution(* com.ley.springboot.UserService.*(..))"; AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression(expression); Advice advice = new UserServiceMethodBeforeAdvice(); DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice); proxyFactoryBean.addAdvisor(advisor); return proxyFactoryBean; } @Bean(name = "proxyFactoryBean") public ProxyFactoryBean proxyFactoryBean() { ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean(); proxyFactoryBean.setTarget(new UserService()); String expression = "execution(* com.ley.springboot.UserService.*(..))"; AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression(expression); Advice advice = new UserServiceMethodBeforeAdvice(); DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice); proxyFactoryBean.addAdvisors(advisor); return proxyFactoryBean; }
ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl); factory.addAdvice(myMethodInterceptor); factory.addAdvisor(myAdvisor); MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();
(8):操控通知對象
Advised
接口是用來操控通知對象的。
Advisor[] getAdvisors(); void addAdvice(Advice advice) throws AopConfigException; void addAdvice(int pos, Advice advice) throws AopConfigException; void addAdvisor(Advisor advisor) throws AopConfigException; void addAdvisor(int pos, Advisor advisor) throws AopConfigException; int indexOf(Advisor advisor); boolean removeAdvisor(Advisor advisor) throws AopConfigException; void removeAdvisor(int index) throws AopConfigException; boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException; boolean isFrozen();
能夠添加DefaultPointcutAdvisor,它持有一個pointcut和advised
。而且能夠被用於添加任意一個
Advisor
。
(9):使用auto-proxy
策略
BeanNameAutoProxyCreator
:該類是一個BeanPostProcessor
,根據bean的名稱自動建立AOP
代理。
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames" value="jdk*,onlyJdk"/> <property name="interceptorNames"> <list> <value>myInterceptor</value> </list> </property> </bean>
DefaultAdvisorAutoProxyCreator
通用且功能更強大的自動代理建立器。會在當前上下文中自動應用符合添加的advisor程序,而無需在auto-proxy advisor的bean定義中包含特定的bean名稱。該類在將相同建議一致的應用於許多業務對象頗有用。例如跟蹤或者性能監視方面。
(10):定義新的通知類型
org.springframework.aop.framework.adapter
包是一個SPI
包,擴展新的通知類型。自定義新的通知類型惟一約束是它必須實現org.aopalliance.aop.Advice
標記接口。