1. AOP 概念
AOP(Aspect Oriented Programming),即面向切面編程。
- 連接點(diǎn)(JoinPoint)
程序執(zhí)行的某個(gè)特定位置:如類開(kāi)始初始化前、類初始化后、類某個(gè)方法調(diào)用前、調(diào)用后、方法拋出異常后。一個(gè)類或一段程序代碼擁有一些具有邊界性質(zhì)的特定點(diǎn),這些點(diǎn)中的特定點(diǎn)就稱為“連接點(diǎn)”。Spring僅支持方法的連接點(diǎn),即僅能在方法調(diào)用前、方法調(diào)用后、方法拋出異常時(shí)以及方法調(diào)用前后這些程序執(zhí)行點(diǎn)織入增強(qiáng)。 - 切點(diǎn)(Pointcut)
每個(gè)類具有多個(gè)連接點(diǎn),如果一個(gè)類擁有15個(gè)方法,那么這些方法都是連接點(diǎn),連接點(diǎn)相當(dāng)于數(shù)據(jù)庫(kù)中的記錄,而切點(diǎn)相當(dāng)于查詢條件。切點(diǎn)和連接點(diǎn)不是一對(duì)一的關(guān)系,一個(gè)切點(diǎn)可以匹配多個(gè)連接點(diǎn)。Spring AOP的規(guī)則解析引擎負(fù)責(zé)切點(diǎn)所設(shè)定的查詢條件,找到對(duì)應(yīng)的連接點(diǎn)。其實(shí)確切地說(shuō),不能稱之為查詢連接點(diǎn),因?yàn)檫B接點(diǎn)是方法執(zhí)行前、執(zhí)行后等包括方位信息的具體程序執(zhí)行點(diǎn),而切點(diǎn)只定位到某個(gè)方法上,所以如果希望定位到具體連接點(diǎn)上,還需要提供方位信息。 - 增強(qiáng)(Advice)
增強(qiáng)是織入到目標(biāo)類連接點(diǎn)上的一段程序代碼,在Spring中,增強(qiáng)除用于描述一段程序代碼外,還擁有另一個(gè)和連接點(diǎn)相關(guān)的信息,這便是執(zhí)行點(diǎn)的方位。結(jié)合執(zhí)行點(diǎn)方位信息和切點(diǎn)信息,我們就可以找到特定的連接點(diǎn)。 - 通知器(Advisor)
當(dāng)我們完成切面增強(qiáng)設(shè)計(jì)(Advice)和切入點(diǎn)的設(shè)計(jì)(Pointcut),需要一個(gè)對(duì)象把他們結(jié)合起來(lái),Advisor 就是起到這個(gè)作用,通過(guò)Advisor ,可以確定在哪個(gè)Pointcut 使用哪個(gè)Advice。所以一個(gè)Advisor包含一個(gè)Advice 和 一個(gè)Pointcut 信息。
2. 一些疑問(wèn)
- Spring AOP 增強(qiáng)的代理類 在什么時(shí)候創(chuàng)建 ?
- Spring AOP 怎樣為一個(gè)類 和 方法 匹配 增強(qiáng)的 ?
- Spring AOP 是如何 協(xié)調(diào) 前置通知 后置通知 異常通知 返回通知的?
- 切面類 可以被 AOP增強(qiáng)么?
3. 注冊(cè) AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator是用來(lái)處理 當(dāng)前項(xiàng)目當(dāng)中 所有 AspectJ 注解的切面類。以及所有的 Spring Advisor ,同時(shí) 也是一個(gè) BeanPostProcessor 。所以 想了解Spring AOP 如何實(shí)現(xiàn),就要先分析 AnnotationAwareAspectJAutoProxyCreator,那么他從何而來(lái),是如何注冊(cè)到容器內(nèi)的。
注冊(cè)AnnotationAwareAspectJAutoProxyCreator方式:
- 使用
EnableAspectJAutoProxy注解,改注解 又被@Import注解注釋,向容器中注冊(cè)了AspectJAutoProxyRegistrar,最后由 AspectJAutoProxyRegistrar 向容器中注冊(cè) AnnotationAwareAspectJAutoProxyCreator。
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//向容器注冊(cè) AnnotationAwareAspectJAutoProxyCreator
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
//對(duì)proxyTargetClass 屬性的支持,切換JDK代理 和 CGLIB 代理
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
//是否對(duì) AopContext.currentPoxy支持。
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
- 使用
@EnableAutoConfiguration注解。改注解會(huì)向容器注冊(cè)Spring.factories文件中聲明的類,其中AopAutoConfiguration是對(duì)Spring AOP自動(dòng)配置的支持。
@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration
最終還是通過(guò) EnableAspectJAutoProxy 注冊(cè)AnnotationAwareAspectJAutoProxyCreator
@EnableAspectJAutoProxy(proxyTargetClass = false)
判斷條件 ,可以在 application.properties 中 配置。
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = true)
public static class JdkDynamicAutoProxyConfiguration {
}
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = false)
public static class CglibAutoProxyConfiguration {
}
}
- xml 標(biāo)簽
Spring 采用 自定義標(biāo)簽 <aop:aspectj-autoproxy /> 注冊(cè)AnnotationAwareAspectJAutoProxyCreator
4. AnnotationAwareAspectJAutoProxyCreator的初始化
AnnotationAwareAspectJAutoProxyCreator初始化 主要初始一些 組件對(duì)象。
組件包括:
- AspectJAdvisorFactory
根據(jù)AspectJ 注釋的切面類 創(chuàng)建 Spring AOP Advisors 的工廠接口。Advisor增強(qiáng)器的創(chuàng)建 都是由AspectJAdvisorFactory完成。 - BeanFactoryAspectJAdvisorsBuilder
從項(xiàng)目中 查找所有的切面類,然后使用 AspecJAdvisorFactory 創(chuàng)建 Advisors。 - advisorRetrievalHelper
對(duì)Xml 配置Advisor 的支持。從容器中檢索 所有的 Advisor 類型。
5 . 創(chuàng)建代理類過(guò)程
由于 AnnotationAwareAspectJAutoProxyCreator 是一個(gè) BeanPostProcessor 實(shí)現(xiàn)類,Spring 會(huì)在對(duì)一個(gè)對(duì)象的初始化前后執(zhí)行 BeanPostProcssor 的接口方法。Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;就是 創(chuàng)建增強(qiáng)代理對(duì)象的方法。該方法被AnnotationAwareAspectJAutoProxyCreator的父類AbstractAutoProxyCreator實(shí)現(xiàn)。
- postProcessAfterInitialization
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//bean 就是需要 被增強(qiáng)的類。
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//防止 bean 多次被增強(qiáng)。
if (!this.earlyProxyReferences.contains(cacheKey)) {
//如果有必要 就進(jìn)行封裝,有沒(méi)有必要主要取決于 bean 是否需要被增強(qiáng)。
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
- wrapIfNecessary
對(duì)給定bean 就行封裝。
/**
*如果有必要 封裝 bean
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//如果已經(jīng)處理過(guò)
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
//如果不需要增強(qiáng) 就直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//如果是一個(gè) 組件類,組件類包括Advice,Pointcut,Advisor,AopInfrastructureBean實(shí)現(xiàn)類,和 Aspect注解注釋的類。所以 切面類 自己不會(huì)被AOP 增強(qiáng)。
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 為bean 獲取和 匹配合適的 增強(qiáng)器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
//如果 沒(méi)有 匹配到增強(qiáng)器 那么就不用創(chuàng)建增強(qiáng)代理類。
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//創(chuàng)建 增強(qiáng)代理類
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
5.1 獲取增強(qiáng)器
-
getAdvicesAndAdvisorsForBean(為bean獲取和匹配合適的增強(qiáng)器)
getAdvicesAndAdvisorsForBean ---> findEligibleAdvisors:為bean獲取和匹配增強(qiáng)器分為兩步:
5.1.1 獲取到所有的增強(qiáng)器
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//獲取所有的增強(qiáng)器
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//匹配合適的增強(qiáng)器
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
- findCandidateAdvisors 獲取到所有的增強(qiáng)器
調(diào)用AnnotationAwareAspectjAutoProxyCreator 的 findCandidateAdvisors@Override protected List<Advisor> findCandidateAdvisors() { // 對(duì)xml 配置 Advisor 的支持. List<Advisor> advisors = super.findCandidateAdvisors(); // 搜索容器中所有 @Aspecj 注解申明的 切面類,構(gòu)建增強(qiáng)器 advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); return advisors; } - aspectJAdvisorsBuilder.buildAspectJAdvisors()
代碼比較多,就不粘出來(lái)了,大致邏輯就是
- 獲取容器中已經(jīng)注冊(cè)的BeanNames
- 遍歷 所有已經(jīng)注冊(cè)的Bean,查找 @AspectJ
- 通過(guò)AspectJAdvisorFactory獲取Advisor。怎么獲取的 就不解釋了。
5.1.2 匹配合適的增強(qiáng)器
匹配合適的增強(qiáng)器,Spring 使用了 AopUtils.canApply。
匹配邏輯就是:
遍歷 所有增強(qiáng)器,采用增強(qiáng)器的 Poincut 對(duì)一個(gè)對(duì)象的所有方法進(jìn)行匹配,一旦有方法匹配到,就返回true.
對(duì)方法的匹配需要遍歷 所有方法 一一就行匹配。
5.2 創(chuàng)建 代理類
通過(guò) ProxyFactory 創(chuàng)建動(dòng)態(tài)代理類,創(chuàng)建之前 先配置ProxyFactory,設(shè)置目標(biāo)對(duì)象,設(shè)置是否對(duì)目標(biāo)對(duì)象代理(會(huì)影響采用JDK代理或者Cglib代理),設(shè)置增強(qiáng)器集合,等。
配置ProxyFactory完成之后就是獲取代理類了,
調(diào)用ProxyFactory.getProxy:
public Object getProxy(ClassLoader classLoader) {
//先獲取 AopProxy,AopProxy有JDK實(shí)現(xiàn)和 Cglib實(shí)現(xiàn)兩種。
return createAopProxy().getProxy(classLoader);
}
判斷采取JDK代理 或Cglib代理。
判斷條件:
- Optimize: 是否采用了 激進(jìn)的優(yōu)化策略,該優(yōu)化僅支持 Cglib代理
- ProxyTargetClass: 代理目標(biāo)類,代理目標(biāo)類 就是采用 子類繼承的方式創(chuàng)建代理,所以也是Cglib代理,可以通過(guò)。
- hasNoUserSuppliedProxyInterfaces:判斷是否是實(shí)現(xiàn)了接口,如果沒(méi)有必須采用Cglib代理。
所以如果我們想在項(xiàng)目中 采用Cglib代理的話 application.properties中配置:
spring.aop.proxy-target-class=false,或者使用注解配置proxyTargetClass = true .
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
//判斷采用什么代理類型。
//Optimize 是否采用了 激進(jìn)的優(yōu)化策略,該優(yōu)化僅支持 Cglib代理
//ProxyTargetClass 代理目標(biāo)類,代理目標(biāo)類 就是采用 子類繼承的方式創(chuàng)建代理,所以也是Cglib代理,可以通過(guò)
// 判斷是否是實(shí)現(xiàn)了接口,如果沒(méi)有必須采用Cglib代理。
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
如果目標(biāo)對(duì)象是接口 采用 JDK代理。
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
- 拿JdkDynamicAopProxy來(lái)說(shuō)。
獲取代理類:
JDK動(dòng)態(tài)代理的關(guān)鍵是 InvocationHandler,JdkDynamicAopProxy實(shí)現(xiàn)了InvocationHandler接口。
@Override
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
//獲取代理類,其中InvocationHanler 是 this,就是JdkDynamicAopProxy。
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
6.增強(qiáng)方法的執(zhí)行
增強(qiáng)方法的執(zhí)行 是AOP的核心所在,理解Spring Aop 是如何 協(xié)調(diào) 各種通知 的執(zhí)行,是理解的關(guān)鍵。
通知 分為前置 后置 異常 返回 環(huán)繞通知,在一個(gè)方法中每種通知的執(zhí)行時(shí)機(jī)不同,協(xié)調(diào)他們之間執(zhí)行順序很重要。
但是Spring AOP 采用了很聰明的方法讓各種各樣的通知準(zhǔn)確有序的工作。
- JdkDynamicAopProxy.invoker
由于invoker 方法很大,我刪除了大部分的代碼,保留幾處關(guān)鍵代碼:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//省略了 對(duì) hashCode equals 等方法的處理....
//exposeProxy屬性的支持。
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 獲取目標(biāo)對(duì)象類
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
// 獲取攔截器鏈 這里this.advised 就是AnnotationAwareAspectJAutoProxyCreator
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
//如果 沒(méi)有攔截器鏈 則直接執(zhí)行。
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
//開(kāi)始執(zhí)行攔截器鏈
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
//返回執(zhí)行結(jié)果。
return retVal;
}
6.1 創(chuàng)建攔截器鏈
攔截器鏈InterceptorAndDynamicMethodMatcher 類 和 MethodInterceptor 類型的集合。
InterceptorAndDynamicMethodMatcher 封裝了 MethodInterceptor 和 MethodMather ,作用就是在執(zhí)行時(shí)進(jìn)行再次匹配。
創(chuàng)建攔截器的過(guò)程就是 對(duì)所有 適合 目標(biāo)類的 Advisor進(jìn)行再一次篩選。對(duì)匹配的Advisor封裝成 MethodInterceptor。通過(guò)MethodInterceptor 統(tǒng)一 增強(qiáng)方法的調(diào)用。
6.2 執(zhí)行攔截器鏈
使用ReflectiveMethodInvocation 對(duì)連接器鏈進(jìn)行封裝。通過(guò)process 方法觸發(fā)攔截器開(kāi)始執(zhí)行。
public Object proceed() throws Throwable {
// 判斷攔截器鏈 是否執(zhí)行結(jié)束,如果執(zhí)行結(jié)束執(zhí)行目標(biāo)方法。
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
//獲取下一個(gè) 需要執(zhí)行的 攔截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
//如果需要執(zhí)行時(shí) 進(jìn)行匹配
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
//如果匹配,并不清楚 為什么還要在這里 進(jìn)行再次匹配。
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments))
//注意這里 將ReflectiveMethodInvocation 自己當(dāng)參數(shù) ,傳入調(diào)用。
return dm.interceptor.invoke(this);
}
else {
//遞歸的調(diào)用
return proceed();
}
}
else {
//如果 MethodInterceptor
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
查看代碼并沒(méi)有發(fā)現(xiàn)任何的關(guān)于協(xié)調(diào)前置,后置各種通知的代碼。其實(shí)所有的協(xié)調(diào)工作都是由MethodInterceptor 自己維護(hù)。
- 前置MethodInterceptor(MethodBeforeAdviceInterceptor)的invoke
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
//激活 前置增強(qiáng)方法
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
//繼續(xù)調(diào)用下一個(gè) 攔截器。
return mi.proceed();
}
-
后置MethodInterceptor(AspectJAfterAdvice)
@Override public Object invoke(MethodInvocation mi) throws Throwable { try { //繼續(xù)調(diào)用一下攔截器。 return mi.proceed(); } finally { //在finally 里面激活 后置增強(qiáng)方法 invokeAdviceMethod(getJoinPointMatch(), null, null); } }通過(guò)對(duì)比前置 和 后置 攔截器,可以發(fā)現(xiàn) 具體協(xié)調(diào)各種通知 的 邏輯 實(shí)際是利用了 遞歸的思想。
后置攔截器為例: 先執(zhí)行 遞歸方法 proceed()。最后從遞歸方法返回的時(shí)候 最后調(diào)用 后置方法。
7. 模擬攔截器調(diào)用
為了更好理解 攔截器調(diào)用,自己實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的攔截器鏈調(diào)用過(guò)程。
7.1 準(zhǔn)備工作
-
接口
//方法調(diào)用 interface MethodInvocation{ Object process() throws Throwable; } //方法攔截器 interface MethodInterceptor{ Object invoke(MethodInvocation mi) throws Throwable; } -
實(shí)現(xiàn)
//后置增強(qiáng)方法的 攔截器 class AfterMethodInterceptor implements MethodInterceptor{ String identification; public AfterMethodInterceptor(String identification){ this.identification = identification; } @Override public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.process(); }finally { System.out.println("執(zhí)行后置通知"+identification); } } } //前置 的 方法攔截器 class BeforMethodInterceptor implements MethodInterceptor{ String identification; public BeforMethodInterceptor(String identification){ this.identification = identification; } @Override public Object invoke(MethodInvocation mi) throws Throwable { System.out.println("執(zhí)行前置通知"+identification); return mi.process(); } } // 默認(rèn)的 方法調(diào)用實(shí)現(xiàn) class DefaultMethodInvacation implements MethodInvocation { List<MethodInterceptor> chian; Object target; //目標(biāo)對(duì)象 Method method; //目標(biāo)方法 Object[] args; //方法參數(shù) int currentChianIndex; //記錄攔截器鏈當(dāng)前執(zhí)行位置 public DefaultMethodInvacation(List<MethodInterceptor> chian,Object target,Method method,Object[] args){ this.chian = chian; this.method = method; this.target = target; this.args = args; } @Override public Object process() throws Throwable{ Object value ; //如果 攔截器 執(zhí)行完畢 執(zhí)行 目標(biāo)方法 if(currentChianIndex == chian.size()){ value = method.invoke(target, args); return value; } //獲取下一個(gè) 攔截器 MethodInterceptor methodInterceptor = chian.get(currentChianIndex++); return methodInterceptor.invoke(this); } } //目標(biāo)對(duì)象 class TargetObj{ //目標(biāo)方法 public void targetMethod(){ System.out.println("-----目標(biāo)方法執(zhí)行----"); } }
7.2 測(cè)試代碼
@Test
public void aopchain() throws Throwable {
List<MethodInterceptor> chain = Lists.newArrayList();
//攔截器鏈,穿插這 放入 前置 和 后置 攔截器 。
chain.add(new AfterMethodInterceptor("1"));
chain.add(new BeforMethodInterceptor("1"));
chain.add(new AfterMethodInterceptor("2"));
chain.add(new BeforMethodInterceptor("2"));
chain.add(new AfterMethodInterceptor("3"));
chain.add(new BeforMethodInterceptor("3"));
//目標(biāo)對(duì)象
TargetObj targetObj = new TargetObj();
//目標(biāo)方法
Method method = TargetObj.class.getMethod("targetMethod");
DefaultMethodInvacation defaultMethodInvacation = new DefaultMethodInvacation(chain, targetObj, method, null);
//執(zhí)行攔截器鏈
defaultMethodInvacation.process();
}
7.3 執(zhí)行結(jié)果
雖然 前置 和 后置 都是 無(wú)序的 存放在 攔截器鏈中,但是 前置 和 后置 都在各自的位置執(zhí)行。
執(zhí)行前置通知1
執(zhí)行前置通知2
執(zhí)行前置通知3
-----目標(biāo)方法執(zhí)行----
執(zhí)行后置通知3
執(zhí)行后置通知2
執(zhí)行后置通知1