聊聊AOP

2021-02-14 碼出精彩

AOP是編程中常用的一種方式,以Spring為例我們經常使用spring-aop組件和aspectJ的註解或者配置文件來完成對接口或類中的 某些方法的執行的攔截,本文對用到的一些概念術語進行解讀,方便理解。

」概念Joinpoint「

A joinpoint is a candidate point in the Program Execution of the application where an aspect can be plugged in. This point could be a method being called, an exception being thrown, or even a field being modified. These are the points where your aspect’s code can be inserted into the normal flow of your application to add new behavior.

注意 Joinpoint 並不一定只是方法的執行點,還可以是一個異常的拋出點或者一個屬性的變更點,在這些變動上我們都可以進行攔截。

下面用一個餐館的例子來比喻:

Join points are the options on the menu and pointcuts are the items you select. A joinpoint is an opportunity within code for you to apply an aspect…just an opportunity. Once you take that opportunity and select one or more joinpoints and apply an aspect to them, you』ve got a pointcut.

Joinpoint 就是菜單上的選項,Pointcut 就是你選的菜。Joinpoint 只是你切面中可以切的那些方法,一旦你選擇了要切哪些方法, 那就是 Pointcut 了。

也就是說,所有在你程序中可以被調用的方法都是 JoinPoint 。使用 Pointcut 表達式,那些匹配了的方法,才叫 Pointcut。所以你根本不用關心 JoinPoint 。

再通俗一點,比如你有10個方法,你只想切2個方法,那麼那10個方法就是 Joinpoint , 2個方法就是 PointCut 。

下面是spring-aop中對 Jointpoint 接口的定義:

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();

}

This interface represents a generic runtime joinpoint (in the AOP terminology). A runtime joinpoint is an event that occurs on a static joinpoint (i.e. a location in a the program). For instance, an invocation is the runtime joinpoint on a method (static joinpoint). The static part of a given joinpoint can be generically retrieved using the {@link #getStaticPart()} method.

In the context of an interception framework, a runtime joinpoint is then the reification of an access to an accessible object (a method, a constructor, a field), i.e. the static part of the joinpoint. It is passed to the interceptors that are installed on the static joinpoint.

注釋裡面有一個概念:static joinpoint,比如方法就是一種,可以通過 getStaticPart 方法獲取一個 joinpoint 的 static part 。

對於 getThis 方法來說需要注意的一點,如果它返回的是持有當前 jointpoint 的static part的對象,比如一個觸發的目標對象, 如果是靜態方法則返回null。

這裡提到了 Invocation ,看一下它的接口定義:

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();

}

This interface represents an invocation in the program. An invocation is a joinpoint and can be intercepted by an interceptor.

Invocation 即「調用」,它也是一種 jointpoint ,主要用於 Interceptor 攔截器攔截的時候使用。

它的接口 getArgument 會返回當前被執行的方法的入參,以數組的形式返回。

一種比較常見的子接口是 MethodInvocation :

public interface MethodInvocation extends Invocation {

 /** * Get the method being called. * <p>This method is a friendly implementation of the * {@link Joinpoint#getStaticPart()} method (same result). * @return the method being called */
 Method getMethod();

}

Description of an invocation to a method, given to an interceptor upon method-call. A method invocation is a joinpoint and can be intercepted by a method interceptor.

可以看到一個 method invocation 就是一個 jointpoint,即方法調用類型的 joinpoint ,它會攔截器攔截,同時它的 方法 getMethod 返回的和 Jointpoint 的方法 getStaticPart 的返回結果是一樣的,都是當前被執行的方法。

關於攔截器 Interceptor 下面講 Advice 的時候會再詳細介紹。

Pointcut「

A pointcut defines at what joinpoints, the associated Advice should be applied. Advice can be applied at any joinpoint supported by the AOP framework. Of course, you don’t want to apply all of your aspects at all of the possible joinpoints. Pointcuts allow you to specify where you want your advice to be applied. Often you specify these pointcuts using explicit class and method names or through regular expressions that define matching class and method name patterns. Some AOP frameworks allow you to create dynamic pointcuts that determine whether to apply advice based on runtime decisions, such as the value of method parameters.

區別如下:

Joinpoint - Potential places to apply/run the advice code.

Pointcut - actual chosen joinpoints for executing the advice.

spring-aop中的 Pointcut 接口定義如下:

public interface Pointcut {

 /** * Return the ClassFilter for this pointcut. * @return the ClassFilter (never {@code null}) */
 ClassFilter getClassFilter();

 /** * Return the MethodMatcher for this pointcut. * @return the MethodMatcher (never {@code null}) */
 MethodMatcher getMethodMatcher();


 /** * Canonical Pointcut instance that always matches. */
 Pointcut TRUE = TruePointcut.INSTANCE;

}

它需要提供 MethodMatcher 實現類和 ClassFilter 實現類分別用於匹配方法和類,二者要同時滿足才會被增強,如我想攔截 所有Grpc服務類的非 Object 方法,或者帶有某個類註解的所有方法等。

Advice「

This is an object which includes API invocations to the system wide concerns representing the action to perform at a joinpoint specified by a point.

中文翻譯為「聲明」(也有叫「通知」的,但註解裡面也說到了是一個action to perform,所以個人認為叫增強更好), advice 就是你作用到 pointcut 上的方式和行為,如可以使用Before, After或者Around等方式,以及對應的相應的代碼邏輯。

spring-aop中的定義如下:

/** * Tag interface for Advice. Implementations can be any type * of advice, such as Interceptors. */
public interface Advice {

}

是的,一個空的接口定義,只是一個 Tag interface ,實現類可以是任何的 advice ,比如攔截器 Interceptor 也是一種。

基於該接口衍生出了很多子接口如:MethodBeforeAdvice 、 AfterReturningAdvice 、 ThrowsAdvice 等。

Interceptor

我們來看一下一種常見的 Advice 攔截器 Interceptor 的定義:

public interface Interceptor extends Advice {

}

這是一個通用的接口,一般不會直接使用而是使用它的一些子接口(如MethodInterceptor),文檔中對它的解釋重點如下:

A generic interceptor can intercept runtime events that occur within a base program. Those events are materialized by (reified in) joinpoints. Runtime joinpoints can be invocations, field access, exceptions

可以看到它是用來攔截運行時程序中的事件event的,這些事件以 jointpoint 的「物化形式」存在,比如調用(Invocations)、 屬性訪問(Field Access)和異常(Exceptions)。

看一下我們常用的方法攔截器 MethodInterceptor 接口的定義:

@FunctionalInterface
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;

}

可以看到它本質上是抽象了對攔截到的「方法調用」的處理接口,入參是 MethodInvocation ,一般在執行 Jointpoint#proceed() 之前和之後進行處理,返回結果可以是方法的返回值或hack後的返回值。

Advisor

advisor 就是作用在具體對象上的 pointcut 和 advice ,把 pointcut 和 advice 合起來就是 advisor 。比如切到哪個具體方法上面,是使用的Before、After還是Around,這個就叫 advisor 。

看一下spring-aop中的 Advisor 接口的定義:

public interface Advisor {

 Advice EMPTY_ADVICE = new Advice() {};


 /** * Return the advice part of this aspect. An advice may be an * interceptor, a before advice, a throws advice, etc. * @return the advice that should apply if the pointcut matches */
 Advice getAdvice();

 boolean isPerInstance();

}

Base interface holding AOP advice (action to take at a joinpoint) and a filter determining the applicability of the advice (such as a pointcut). This interface is not for use by Spring users, but to allow for commonality in support for different types of advice.

Spring AOP is based around around advice delivered via method interception, compliant with the AOP Alliance interception API.

乍一看Advice和Advisor接口沒有什麼大的區別,後者除了包括前者的一個實現類接口外還有一個是否是每個目標對象都會創建一個代理 的方法。

其實不是一個東西,看一下更通用的接口 PointcutAdvisor 就知道了:

public interface PointcutAdvisor extends Advisor {

 /** * Get the Pointcut that drives this advisor. */
 Pointcut getPointcut();

}

Superinterface for all Advisors that are driven by a pointcut. This covers nearly all advisors except introduction advisors, for which method-level matching doesn’t apply.

這個接口才是我們要用到的那個接口,它提供一個接口要返回一個 Pointcut 對象,這個接口是通過 pointcut 來驅動 的。比如,我們可以通過實現 getPointcut 方法返回一個只 PointCut 的實現類,讓它的 ClassFilter 和 MethodMatcher 實現的功能是:對Grpc服務類的自有接口進行攔截的。然後通過實現 getAdvice 方法返回一個 MethodInteceptor 的實現類,默認對執行的方法通過Around的方式進行增強。

所以,Spring AOP中的 advisor 都是基於 pointcut 來驅動,並需要 advice 來完成具體的邏輯的。

Aspect

這個概念和Advisor其實差不多,它更多的出現在說明的文檔中,以「切面」的名字示眾。為什麼說它也是Advisor呢,看一下Spring的 基於配置文件的注釋就知道了:

<bean id="sysAspect" class="com.example.aop.SysAspect"/>
<!-- 配置AOP -->
<aop:config>
    <!-- 配置切點表達式 -->
    <aop:pointcut id="pointcut" expression="execution(public * com.example.controller.*Controller.*(..))"/>
    <!-- 配置切面及配置 -->
    <aop:aspect order="3" ref="sysAspect">
        <!-- 前置通知 -->
        <aop:before method="beforMethod"  pointcut-ref="pointcut" />
        <!-- 後置通知 -->
        <aop:after method="afterMethod"  pointcut-ref="pointcut"/>
        <!-- 返回通知 -->
        <aop:after-returning method="afterReturnMethod" pointcut-ref="pointcut" returning="result"/>
        <!-- 異常通知 -->
        <aop:after-throwing method="afterThrowingMethod" pointcut-ref="pointcut" throwing="ex"/>
        <aop:around method="aroundMethod" pointcut-ref="pointcut"/>
    </aop:aspect>
</aop:config>

該配置通過 <aop:config> 、 <aop:pointcut> 、 <aop:aspect>以及它的子標籤 <aop:before> 、 <aop:after> 等將一個類中的各個方法定義為具體的增強的邏輯(當然也可以直接在對應的類中使用各種註解來實現,這裡只是用配置的方式做一個講解)。

Target

目標對象就是織入 advice 的對象,也叫做 advised object ,由於Spring是通過運行時代理的方式來實現 aspect 的, 所以 advised object 總是一個代理對象( proxied object )。

Proxy

一個類被AOP織入 advice ,就會產生一個結果類,它是融合了原類和增強邏輯的代理類。在Spring AOP中,一個AOP代理類是一個 JDK動態代理對象或者一個CGLIB代理對象。

Spring為什麼建議基於接口編程?因為它默認使用JDK的動態代理來完成AOP功能,如果是一個普通的類的方法,則只能使用CGLIB來實現。但需要引入額外的asm的包。

如果需要強制使用CGLIB,需要顯示通過配置文件的方式設置 <aop:config> 標籤對應的 proxy-target-class 為true:

<aop:config proxy-target-class="true">
    <!-- other beans defined here... -->
</aop:config>

如果是使用@AspectJ註解則需要設置 <aop:aspectj-autoproxy> 標籤對應的屬性為true:

<aop:aspectj-autoproxy proxy-target-class="true"/>

Spring AOP和AspectJ

Spring AOP的目的是提供一個能個Spring IOC緊密集成的代理方式,解決常見的企業及開發應用中的問題,並不像AspectJ那樣強大, 比如細粒度的對象的增強Spring AOP就做不了。

Spring seamlessly integrates Spring AOP and IoC with AspectJ, to enable all uses of AOP within a consistent Spring-based application architecture. This integration does not affect the Spring AOP API or the AOP Alliance API. Spring AOP remains backward-compatible.

Spring AOP用起來比較簡單,雖然它也需要依賴aspectweaver.jar,但是也只是借用了其中的註解和語法。它不需要引入AspectJ 的compiler/weaver到開發和構建的環節中。如果僅需要對Spring Bean進行切面,直接用Spring AOP即可,如果不是Spring容器 管理的對象,比如領域對象,則可以考慮使用AspectJ,對於簡單方法執行的切面Spring AOP可以搞定,但是對於諸如屬性獲取和設置 則需要使用AspectJ來完成。

@AspectJ支持

Spring有兩種方式使用@AspectJ註解來實現AOP,該註解是在AspectJ 5中引入的,需要保證aspectjweaver.jar在classpath中。

使用Java Configuration的方式:

@Configuration
@EnableAspectJAutoProxy
public class AppConfig{
}

使用XML方式:

<aop:aspectj-autoproxy/>

需要注意的是@AspectJ註解本身並不會被Spring包掃描自動發現,需要使用如@Component註解來使Spring發現。

」例子

除了通過配置文件聲明aspects,如使用 <aop:config> 或者 <aop:aspectj-autoproxy> 之外,還可以通過編程的方式 創建proxies來完成對目標對象的advised。

一般用AspectJ風格的pointcut表達式我們經常使用,如@Aspect、@Before等這裡不再深入,如果用編程的方式實現可以利用 ProxyFactory 類來實現編程方式的AOP功能。比如下面的例子:

import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;

public class MethodBeforeAdviceBarImpl implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("Bar!");
    }
}

使用Spring AOP自帶的Advisor實例,只需要引入spring-context.jar即可:

import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;

public class App {

    public static void main(String[] args) {
        final MethodBeforeAdvice advice = new MethodBeforeAdviceBarImpl();

        final NameMatchMethodPointcutAdvisor nameMatchMethodPointcutAdvisor = new NameMatchMethodPointcutAdvisor();
        nameMatchMethodPointcutAdvisor.setMappedName("foo");
        nameMatchMethodPointcutAdvisor.setAdvice(advice);

        final ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.addAdvisor(nameMatchMethodPointcutAdvisor);

        final Foo foo = new FooImpl();
        proxyFactory.setTarget(foo);

        final Foo fooProxy = (Foo) proxyFactory.getProxy();
        fooProxy.foo();
    }
}

但是如果要使用AspectJ表達式形式的Advisor還需要引入aspectjweaver.jar(否則在運行時會報錯),比如下面的例子:

import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import org.springframework.aop.framework.ProxyFactory;

public class App {

    public static void main(String[] args) {
        final MethodBeforeAdvice advice = new MethodBeforeAdviceBarImpl();

        final AspectJExpressionPointcutAdvisor aspectJExpressionPointcutAdvisor = new AspectJExpressionPointcutAdvisor();
        aspectJExpressionPointcutAdvisor.setAdvice(advice);
        aspectJExpressionPointcutAdvisor.setExpression("execution(void biz.tugay.spashe.Foo.foo())");

        final ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.addAdvisor(aspectJExpressionPointcutAdvisor);

        final Foo foo = new FooImpl();
        proxyFactory.setTarget(foo);

        final Foo fooProxy = (Foo) proxyFactory.getProxy();
        fooProxy.foo();
    }
}

AspectJExpressionPointcutAdvisor 內部通過 AspectJExpressionPointcut 來實現,它引入了很多AspectJ的包:

import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.weaver.patterns.NamePattern;
import org.aspectj.weaver.reflect.ReflectionWorld.ReflectionWorldException;
import org.aspectj.weaver.reflect.ShadowMatchImpl;
import org.aspectj.weaver.tools.ContextBasedMatcher;
import org.aspectj.weaver.tools.FuzzyBoolean;
import org.aspectj.weaver.tools.JoinPointMatch;
import org.aspectj.weaver.tools.MatchingContext;
import org.aspectj.weaver.tools.PointcutDesignatorHandler;
import org.aspectj.weaver.tools.PointcutExpression;
import org.aspectj.weaver.tools.PointcutParameter;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.PointcutPrimitive;
import org.aspectj.weaver.tools.ShadowMatch;

exposeProxy屬性

在 ProxyConfig 類中有一個 exposeProxy 屬性, 默認為false,它的含義如下:

Set whether the proxy should be exposed by the AOP framework as a ThreadLocal for retrieval via the AopContext class. This is useful if an advised object needs to call another advised method on itself. (If it uses {@code this}, the invocation will not be advised). Default is 「false」, in order to avoid unnecessary extra interception. This means that no guarantees are provided that AopContext access will work consistently within any method of the advised object.

如果不設置為true,想通過 AopContext 類來獲取當前代理對象時會報錯:

java.lang.IllegalStateException: Cannot find current proxy: Set 『exposeProxy』 property on Advised to 『true』 to make it available.

設置的方法根據版本不同而不同

<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>

@EnableAspectJAutoProxy(exposeProxy=true, proxyTargetClass=true)

這引出了一個很經典的問題,即用Spring AOP進行advised過的代理對象在調用目標對象的方法時是可以被增強的,但其內部如果再調用本身對象的方法,則該方法 是不會被增強的。

AopContext 的使用方式如下(來自官網),它可以獲得當前線程的上下文中的代理對象:

public class SimplePojo implements Pojo {

    public void foo() {
        // this works, but... gah!
        ((Pojo) AopContext.currentProxy()).bar();
    }

    public void bar() {
        // some logic...
    }
}

但是這種方式會強制耦合Spring AOP,入侵性強不說,還讓上下文顯示的知道當前代碼要被用在AOP的上下文中,並且需要設置 exposeProxy 屬性為true,否則 AopContext 拿不到當前執行的代理對象,也就無法觸發植入的邏輯。

Spring AOP通過如下代碼將代理對象放入到Aop具體代碼參考:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    ... ...

    Object retVal;

    if (this.advised.exposeProxy) {
        // Make invocation available if necessary.
        oldProxy = AopContext.setCurrentProxy(proxy); // 內部是一個ThreadLocal對象來維護
        setProxyContext = true;
    }

    ... ...

除了使用上面的方法外,還有一種比較簡潔但是很奇葩的做法就是在當前的Bean中注入自己,然後在相關方法調用自身方法的地方使用 這個注入的實例來調用相應的方法,即不使用this(隱式),因為this其實是被代理對象本身,需要使用proxy對象進行調用實現增強, 如果拿不到當前對象的代理,就自己注入一個,其實和上面的 AopContext.currentProxy() 是一個原理。

需要注意AspectJ就沒有這樣的問題,因為它並不是一個基於代理的AOP框架,而是編譯和加載時就已經植入了代碼到源碼中。

其他比較經典的關於AOP失效的例子包括 @Async 失效、 @Transactional 失效等,都是通過對當前類對象來調用具體的被聲明為增強的方法 但是無法成功。

多線程和事務管理

Spring的事務處理為了與數據訪問解耦,它提供了一套處理數據資源的機制,而這個機制與上文中的原理相差無幾,也是採用的ThreadLocal的方式。

在編程中,Service實例都是單例的無狀態的,事務管理則需要加入事務控制的相關狀態變量,使得Service實例不再是無狀態線程安全的,解決這個問題的方式就是使用ThreadLocal。

通過使用ThreadLocal將數據源綁定在當前線程上,在當前線程的事務中,從設定的地方去取連接就會是同一個資料庫連接,這樣操作事務就會在同一個連接上進行。

但是,ThreadLocal的特性是,綁定在當前線程中的變量不會自動傳遞到其它線程中(當然,InheritableThreadLocal可以在父子線程中間傳遞變量值,但是這需要特殊的使用場景),所以當開啟子線程時,子線程並沒有父線程的資料庫連接資源。

Referenceshttps://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-introduction-defnhttps://stackoverflow.com/questions/15447397/spring-aop-whats-the-difference-between-joinpoint-and-pointcuthttps://stackoverflow.com/questions/11446893/spring-aop-why-do-i-need-aspectjweaverhttps://www.cnblogs.com/duanxz/p/4367362.htmlhttps://stackoverflow.com/questions/6222600/transactional-method-calling-another-method-without-transactional-anotation

相關焦點

  • 如何理解 Spring AOP 以及使用 AspectJ?
    :config>元素中,在<beans>元素中可以包含多個<aop:config>元素,一個 <aop:config> 中又可以包含子元素和屬性,其子元素包含<aop:pointcut、<aop:advisor、<aop:aspect>,在配置時,需要嚴格按照順序來定義,在<aop:aspect>下,同樣包含屬性和多個子元素
  • 求求你,下次面試別再問我什麼是AOP了!
    .test1.Service1.m1()我是 m1 方法public void com.javacode2018.aop.demo8.test1.Service1.m1(),耗時(納秒):8680400--準備調用:public void com.javacode2018.aop.demo8.test1.Service1.m2()我是 m2 方法public
  • Spring框架IOC和AOP簡介
    /schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.0.xsd"
  • Spring5.0源碼學習系列之Spring AOP簡述
    com.example.spring.aop.service.UserService.addUser(com.example.spring.aop.bean.User); target is of class [com.example.spring.aop.service.impl.UserServiceImpl]方法調用後(after method invoke) :User
  • Spring AOP是什麼?你都拿它做什麼?
    /www.springframework.org/schema/aop/spring-aop.xsd">        <bean id="userDao" class="test.spring_aop_anno.UserDao"></bean>        <bean id="orderDao" class="test.spring_aop_anno.OrderDao
  • spring AOP是什麼?你都拿它做什麼?
    /schema/aop/spring-aop.xsd">     <!-- 實例化切面類 -->     <bean id="transactionAop" class="test.spring_aop_anno.TransactionAop"></bean>          <!-- Aop相關配置 -->     <aop:config>         <!
  • 詳解Spring框架的AOP機制
    下面列出的是spring-aop-5.0版本,其它版本也可以。● spring-aop-5.0.8.RELEASE● spring-aspects-5.0.8.RELEASE另外還需要引入下面的Jar包:● aspectjrt● aspectjweaver在課程案例SpringProgram項目中,相關的Teacher實體類、EmailNotice業務類、Spring配置文件已經存在
  • Java 第一大框架:Spring 的 IoC 跟 AOP 雛形如何實現?
    ="http://www.springframework.org/schema/aop"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop
  • 漫畫| Spring AOP的底層原理是什麼?
    >4、談談Spring如何配置聲明式事務控制聲明式事務管理有兩種常用的方式:基於tx和aop
  • 我們已經不用AOP做操作日誌了!
    優化發送業務消息的實現,使用切面攔截減少對業務代碼的侵入;目前暫時不支持對多表關聯操作日誌記錄,需要拓展;總結本文以操作日誌為題材討論了操作日誌的實現方案和可行性,並且都已經在功能上進行實現,其中使用aop
  • 安卓架構師必備之Android AOP面向切面編程詳解,超實用!
    message.thrown                                            break;            }        }    }}android {    compileSdkVersion 25    buildToolsVersion "25.0.2"    defaultConfig {        applicationId "com.zx.aopdemo
  • 你知道Spring是怎麼將AOP應用到Bean的生命周期中的嗎?
    關於創建代理的具體源碼分析,在Spring中AOP相關的API及源碼解析,原來AOP是這樣子的一文中已經做了詳細介紹,所以本文不再贅述,現在我們的重點將放在Spring是如何解析出來通知的,對應方法就是getAdvicesAndAdvisorsForBean,其源碼如下:第一步:調用org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator
  • Spring AOP看這篇就夠了
    使用 Java Configuration 方式使能@AspectJ@Configuration@EnableAspectJAutoProxypublic class AppConfig {}使用 XML 方式使能@AspectJ<aop
  • 一個Spring AOP的坑!很多人都犯過!
    如果是配置文件修改: <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>如果是SpringBoot,則修改應用啟動入口類的註解:@EnableAspectJAutoProxy(exposeProxy = true)public class
  • 用英語聊聊壓力(三)
    相關閱讀用英語聊聊壓力(二)用英語聊聊壓力(一)用英語聊聊各種夏日皮膚煩惱鞋子「不合腳」英文怎麼說(來源:VOA英語教學 編輯:丹妮)