Spring拾遺(二)——AOP的使用拾遺

前言

上一篇總結了一下spring ioc中一些之前不太熟悉的東西,這一篇博客繼續總結AOP,針對AOP,其實大部分主要是一些概念,這些概念能熟悉了其實AOP就算弄懂了30%,這篇博客依舊按照spring官方參考文檔來進行總結(主要是spring文檔中的第五章)

簡介

官網中有這樣一段簡介

Aspect-oriented Programming (AOP) complements Object-oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns (such as transaction management) that cut across multiple types and objects. (Such concerns are often termed “crosscutting” concerns in AOP literature.)

翻譯:AOP通過提供另一種程序結構的考量補足了OOP面向對象的思想。在OOP中每一個組件是類,但是在AOP中卻是切面。切面使得跨約多個類型和對象的考慮成爲可能。

AOP的一些概念

官網中的概念介紹:spring aop doc,這裏爲了理解簡單,不會按照官網中的介紹順序來介紹AOP的概念。

Join point

連接點

A point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.

方法執行過程中的一個點,例如方法正常的執行,或者異常處理。在spring aop中,一個連接點通常表示的就是一個方法的執行。

簡單點來理解就是——一個方法,這個方法就是一個連接點。

pointcut

A predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name). The concept of join points as matched by pointcut expressions is central to AOP, and Spring uses the AspectJ pointcut expression language by default.

切點,我個人理解其實一定程度上所謂的切點就是連接點的集合。(可以理解爲pointcut是一張表,join point是這個表中的一條具體的記錄)

翻譯:匹配一個連接點結合的表達式。point cut連接了連接點和advice,pointcut表達式與連接點之間匹配的概念是AOP的核心,spring中默認採用了AspectJ切入點表達式

target object

目標對象

An object being advised by one or more aspects. Also referred to as the “advised object”. Since Spring AOP is implemented by using runtime proxies, this object is always a proxied object.

這個就是針對含有join point的對象,spring aop中被代理的對象。

Aop proxy

aop 代理

AOP proxy: An object created by the AOP framework in order to implement the aspect contracts (advise method executions and so on). In the Spring Framework, an AOP proxy is a JDK dynamic proxy or a CGLIB proxy.

AOP爲了實現切面而生成的一個對象。在spring中,生成AOP代理對象的方式是JDK的動態代理或者CGLIB

weaving

織入

linking aspects with other application types or objects to create an advised object. This can be done at compile time (using the AspectJ compiler, for example), load time, or at runtime. Spring AOP, like other pure Java AOP frameworks, performs weaving at runtime.

這個其實指的是一個過程,就是將通知和切點進行整合的一個過程。

advice

通知

Action taken by an aspect at a particular join point. Different types of advice include “around”, “before” and “after” advice. (Advice types are discussed later.) Many AOP frameworks, including Spring, model an advice as an interceptor and maintain a chain of interceptors around the join point.

就是我們需要織入的邏輯操作,通知分爲環繞通知,前置通知和後置通知等。

Aspect

通知,個人覺得就是上面整體的一個集合,作爲一個類而存在

A modularization of a concern that cuts across multiple classes. Transaction management is a good example of a crosscutting concern in enterprise Java applications. In Spring AOP, aspects are implemented by using regular classes (the schema-based approach) or regular classes annotated with the @Aspect annotation (the @AspectJ style).

AOP,Spring AOP,AspectJ的關係

這三者的關係是一個很迷惑的東西,前面已經介紹了AOP是一個概念,是對OOP對象的思想的一個補充。Spring AOP其實就是AOP的一種實現,在很久之前Spring AOP自己實現了一套AOP的語法,但是語法晦澀難用,後來引入了針對AspectJ的支持,可以理解爲Spring只是引入了AspectJ的相關語法,AspectJ是一個單獨的AOP框架。

spring中開啓AspectJ支持的方式有兩種

1、基於XML的方式

<aop:aspectj-autoproxy/>

2、基於java config的方式

@Configuration
@EnableAspectJAutoProxy //相當於<aop:aspectj-autoproxy/>
public class AopConfig {
}

helloworld的實例

基於xml

1、在xml文件中引入spring aop的命名空間,完整beans屬性如下

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd">

2、開啓spring對AspectJ的語法支持(可跳過)

<aop:aspectj-autoproxy/>

3、定義兩個bean,一個是切面類,一個是目標類

<bean id="daoAspectXml" class="com.learn.aop.aopxml.DaoAspectXml"></bean>
<bean id="indexDao" class="com.learn.aop.targetobject.IndexDao"></bean>

切面類

/**
 * autor:liman
 * createtime:2020/2/28
 * comment:xml切面類
 */
public class DaoAspectXml {
    public void beforeXmlAdvice(){
        System.out.println("this is xml before method");
    }
}

目標類

/**
 * autor:liman
 * createtime:2020/2/28
 * comment: 目標類
 */
public class IndexDao {
    public void query(){
        System.out.println("this is target query method");
    }
}

4、配置切面

<aop:config>
    <aop:pointcut id="xmlpointcut" expression="execution(* com.learn.aop.targetobject.*.*(..))"/>

    <aop:aspect ref="daoAspectXml">
        <aop:before method="beforeXmlAdvice" pointcut-ref="xmlpointcut"/>
    </aop:aspect>
</aop:config>

5、測試類

/**
 * autor:liman
 * createtime:2020/2/28
 * comment:基於xml的aop配置
 */
public class XmlTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext =
                new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        IndexDao indexDao = (IndexDao) classPathXmlApplicationContext.getBean("indexDao");
        indexDao.query();
    }
}

基於註解

1、容器中開啓註解並配置掃描包路徑

/**
 * autor:liman
 * createtime:2020/2/28
 * comment:
 */
@Configuration
@ComponentScan("com.learn.aop")
@EnableAspectJAutoProxy
public class AopConfig {
}

2、定義切面類即各種advice方法

目標類與上面xml實例中的一致,只是如果這裏要用註解的方式交給spring託管,需要在目標類上加上一個註解

/**
 * autor:liman
 * createtime:2020/2/28
 * comment:
 */
@Aspect
@Component
public class DaoAspectAnno {


    @Pointcut("execution(* com.learn.aop.targetobject.*.*(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void beforeMethod(){
        System.out.println("this is before method in aspect");
    }
}

3、基於註解的aop實例

/**
 * autor:liman
 * createtime:2020/2/28
 * comment:基於註解的aop
 */
public class AnnoTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext =
                new AnnotationConfigApplicationContext(AopConfig.class);
        IndexDao indexDao = annotationConfigApplicationContext.getBean(IndexDao.class);
        indexDao.query();
    }
}

但是這裏梳理不僅僅侷限於此。

spring支持的pointcut表達式

這個在spring文檔中的5.4.3節中,針對pointcut其實有多種表示方式並不知道有execution一種,官網中的介紹是這樣的

Spring AOP supports the following AspectJ pointcut designators (PCD) for use in pointcut expressions:

springAOP 支持一下幾種AspectJ的pointcut的標識符在切入點表達式中使用

  • execution: For matching method execution join points. This is the primary pointcut designator to use when working with Spring AOP.

    execution:用於匹配到方法級別的連接點,這也是spring aop中最爲常用的PCD。

  • within: Limits matching to join points within certain types (the execution of a method declared within a matching type when using Spring AOP).

    within:只能匹配到具體類的類型級別

  • this: Limits matching to join points (the execution of methods when using Spring AOP) where the bean reference (Spring AOP proxy) is an instance of the given type.

    this : 只能匹配目標類的代理類是否是this中指定的類型

  • target: Limits matching to join points (the execution of methods when using Spring AOP) where the target object (application object being proxied) is an instance of the given type.

    target:只能匹配目標類是否是target中指定的類型

  • args: Limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types.

    args:僅僅按照目標方法的參數類型來進行匹配,與方法所在類的包名和參數名無關。

  • @target: Limits matching to join points (the execution of methods when using Spring AOP) where the class of the executing object has an annotation of the given type.

    @target:匹配目標類中有@target指定的註解

  • @args: Limits matching to join points (the execution of methods when using Spring AOP) where the runtime type of the actual arguments passed have annotations of the given types.

    @args:匹配參數中有@args中指定的註解類型的方法

  • @within: Limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP).

    @within:匹配目標類有@within中指定的註解的類

  • @annotation: Limits matching to join points where the subject of the join point (the method being executed in Spring AOP) has the given annotation.

    @annotation:匹配方法上有@annotation中指定的註解類型的方法。

實例

其實關於pointcut的類型可以分爲兩個部分,一個是帶@標記的,一個是不帶@標記的,前者均和註解有關,上面的官網介紹有點讓人迷惑。這裏我們開始進入實例部分。

關於execution註解這裏不再寫出實例,上面介紹helloworld的時候已經給出了實例了。但是這裏需要給出execution的格式:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

其中帶有問號的表示可以省略的。

within

within與execution最大的區別是,兩者粒度不一樣,execution的粒度可以精確到方法級別,而within只能精確到類級別

/**
 * autor:liman
 * createtime:2020/2/28
 * comment:within的實例
 */
@Aspect
@Component
public class DaoAspectAnno {

    @Pointcut("within(com.learn.aop.targetobject.*))")
    public void pointCutWithin(){}

    @Before("pointCutWithin()")
    public void beforeMethod(){
        System.out.println("this is within before method in aspect");
    }
}

在目標類中的所有的方法均會被這個切面匹配到,能順利執行。

在這裏插入圖片描述

args

匹配方法中的參數類型,無關類的名稱和方法名稱。

/**
 * autor:liman
 * createtime:2020/2/28
 * comment:
 */
@Aspect
@Component
public class DaoAspectAnno {

    @Pointcut("args(java.lang.Integer)")
    public void pointCutArgs(){}

    @Before("pointCutArgs()")
    public void argsBeforeMethod(){
        System.out.println("this is args before method");
    }
}

目標類中定義兩個方法

@Repository
@AopAnnota
public class IndexDao {
    public void query(){
        System.out.println("this is target query method");
    }

    public void query(int test){
        System.out.println("this is interger args method");
    }
}

最後測試發現無參數的方法並不會有前置操作的通知

在這裏插入圖片描述

關於target和this的實例,這個我們後面再處理,畢竟這兩個會有很多需要絮叨的。

@annotation

這個就是標示這某一個方法,如果有指定的註解類型則纔會進行織入,如果沒有則並不會織入,因此這裏我們定義個自己的註解類型

/**
 * autor:liman
 * createtime:2020/2/28
 * comment: 自定義註解
 */
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface AopAnnota {
    
}

只是做一個標記,沒有什麼特別的作用

目標類的實例

@Repository
public class IndexDao {

    @AopAnnota
    public void query(){
        System.out.println("this is target query method");
    }

    public void query(int test){
        System.out.println("this is interger args method");
    }
}

我們在query()方法中加上我們自定義的註解。運行測試實例得到如下結果

在這裏插入圖片描述

@within

表示在目標類上是否加上我們指定類型的註解

切面類的實例

/**
 * autor:liman
 * createtime:2020/2/28
 * comment:
 */
@Aspect
@Component
public class DaoAspectAnno {

    @Pointcut("@within(com.learn.aop.annotationuse.AopAnnota)")
    public void pointCutAtWithin(){}

    @Before("pointCutAtWithin()")
    public void annotationWithinBeforeMethod(){
        System.out.println("this is @within before method");
    }

}

在目標類上加上@AopAnnota

/**
 * autor:liman
 * createtime:2020/2/28
 * comment: 目標類
 */
@Repository
@AopAnnota
public class IndexDao {

    public void query(){
        System.out.println("this is target query method");
    }

    public void query(int test){
        System.out.println("this is interger args method");
    }
}

最後運行結果爲:類中的所有方法均會被匹配到

在這裏插入圖片描述

@arg

表示目標方法中參數中是否有我們指定的註解類型。切面中我們可以這樣定義

/**
 * autor:liman
 * createtime:2020/2/28
 * comment:
 */
@Aspect
@Component
public class DaoAspectAnno {

    @Pointcut("@args(com.learn.aop.annotationuse.AopAnnota)")
    public void pointCutAtargs(){}

    @Before("pointCutAtargs()")
    public void annotationagrsBeforeMethod(){
        System.out.println("this is @args before method");
    }
}

目標類中我們加入一個參數的註解

/**
 * autor:liman
 * createtime:2020/2/28
 * comment:切面的實例
 */
@Aspect
@Component
public class DaoAspectAnno {
    public void query(){
        System.out.println("this is target query method");
    }
    public void query(@AopAnnota int test){
        System.out.println("this is @args before method");
    }
}

運行結果:

在這裏插入圖片描述

關於@target我們稍後一起總結

target,this,@target

先來看一個問題,我們將上面的IndexDao的實現類抽象成一個接口

/**
 * autor:liman
 * createtime:2020/2/28
 * comment:dao的抽象接口
 */
public interface IDao {

    public void query();

}

@Repository
public class IndexDao implements IDao {

    public void query(){
        System.out.println("this is target query method");
    }

}

我們的測試代碼如下:

public class AnnoTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext =
                new AnnotationConfigApplicationContext(AopConfig.class);
        IndexDao indexDao = (IndexDao) annotationConfigApplicationContext.getBean("indexDao");
        indexDao.query();
    }
}

編譯一頓猛如虎,測試結果就有意思了

在這裏插入圖片描述

顯示類型不一致,這個也是開發過程中的一個經典的錯誤。

我們運行如下代碼

/**
 * autor:liman
 * createtime:2020/2/28
 * comment:基於註解的aop
 */
public class AnnoTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext =
                new AnnotationConfigApplicationContext(AopConfig.class);
        //用接口類型去承接
        IDao indexDao = (IDao) annotationConfigApplicationContext.getBean("indexDao");
        System.out.println(indexDao instanceof IndexDao);	//false
        System.out.println(indexDao instanceof Proxy);		//true
        System.out.println(indexDao instanceof IDao);		//true
    }
}

運行結果如下:

在這裏插入圖片描述

發現這個並不是我們的目標類的類型,而是proxy類型和IDao類型,這就有意思了,這就說明這個時候我們通過IOC得到的並不是目標對象,而是一個代理對象。這種代理是JDK的動態代理生成的(spring aop的默認代理方式就是這種),如果我們在配置中更改配置如下

@EnableAspectJAutoProxy(proxyTargetClass = true)

之後我們運行如下代碼:

public class AnnoTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext =
                new AnnotationConfigApplicationContext(AopConfig.class);
        IndexDao indexDao = (IndexDao) annotationConfigApplicationContext.getBean("indexDao");
        System.out.println(indexDao instanceof IndexDao);	//true
        System.out.println(indexDao instanceof IDao);	//true
        indexDao.query();
    }
}

這個代碼就能正常運行了。迴歸正題

target,this

@Aspect
@Component
public class DaoAspectAnno {

    //匹配目標類的類型
    @Pointcut("target(com.learn.aop.targetobject.IndexDao)")
    public void pointTarget(){}

    //匹配目標類的代理類的類型,這個這裏並不會切入到目標類中
    @Pointcut("this(com.learn.aop.targetobject.IndexDao)")
    public void pointThis(){}

    @Before("pointTarget()")
    public void targetBeforeMethod(){
        System.out.println("this is target before method");
    }

    @Before("pointThis()")
    public void thisBeforeMethod(){
        System.out.println("this is this before method");
    }
}

運行上述結果會發現

在這裏插入圖片描述

如果將this切點中的類型匹配爲:com.learn.aop.targetobject.IDao則運行結果如下:

在這裏插入圖片描述

如果翻到spring官網中的5.4節中的examples中,會有如下看到如下關於this的一個實例

在這裏插入圖片描述

這也是上面實例中爲啥會抽象出一個接口的原因

@target

如果目標類上有這個實例,則會被匹配

@Aspect
@Component
public class DaoAspectAnno {

    @Pointcut("@target(com.learn.aop.annotationuse.AopAnnota)")
    public void pointAtTarget(){}

    @Before("pointAtTarget()")
    public void atTargetBeforeMethod(){
        System.out.println("this is @target before method");
    }
}

目標類上加上我們自定義的註解

/**
 * autor:liman
 * createtime:2020/2/28
 * comment: 目標類
 */
@Repository("indexDao")
@AopAnnota
public class IndexDao implements IDao {
    public void query(){
        System.out.println("this is query method");
    }
}

運行結果:

在這裏插入圖片描述

advice的幾種類型

這一塊對應總結spring官方文檔的5.4.4節,spring官方文檔中將通知總結爲5種,一種前置通知,一種環繞通知還有三種後置通知,下面根據官網進行實例級別的梳理

前置通知

通過@Before註解可以聲明一個前置通知

@Pointcut("execution(* com.learn.aop.targetobject.*.*(..))")
public void pointCutExecution(){}

@Before("pointCutExecution()")
public void beforeMethod(){
    System.out.println("this is before method in aspect");
}

可以處理返回值的後置通知

通過@AfterReturning註解來實現

切面的邏輯如下:

@AfterReturning(pointcut = "pointCutExecution()",returning = "returnValue")
public void dealAfterReturning(Object returnValue){
    System.out.println("this is after returning aspect method,the return value is "+returnValue);
}

目標方法如下:

public int queryById(int id){
    System.out.println("this is a method that has return value");
    return 1;
}

官網中有這樣一句話

The name used in the returning attribute must correspond to the name of a parameter in the advice method. When a method execution returns, the return value is passed to the advice method as the corresponding argument value. A returning clause also restricts matching to only those method executions that return a value of the specified type (in this case, Object, which matches any return value).

翻譯:返回屬性中使用的名稱必須與advice方法中的參數名稱相對應。當目標方法返回值時,這個返回值就是通過這個參數傳入到增強的方法中。同時這個返回值也會按照目標方法返回的參數類型來進行匹配。我們在上一個實例中用的Object類型來承接參數。

如果將我們的切面方法改成如下所示,則無法匹配到目標方法

@AfterReturning(pointcut = "pointCutExecution()",returning = "returnValue")
public void dealAfterReturning(String returnValue){//這裏只是改了返回值的類型,這就無法增強目標方法
    System.out.println("this is after returning aspect method,the return value is "+returnValue);
}

可以處理異常的後置通知

這個和上面一樣,通過@AfterThrowing來實現,這裏不再贅述,直接給出實例

@Pointcut("execution(* com.learn.aop.targetobject.ExceptionDao.*(..))")
public void pointCutException(){}

@AfterThrowing("pointCutException()")
public void dealException(){
    System.out.println("just deal exception");
}

@AfterThrowing(pointcut = "pointCutException()",throwing = "ex")
public void catchException(RuntimeException ex){
    System.out.println(ex.getMessage());
    System.out.println("just catch exception");
}

目標方法拋出一個異常

@Repository
public class ExceptionDao {

    public void throwExceptionMethod(){
        System.out.println("throw exception method");
        RuntimeException ex = new RuntimeException("this is exception");
        throw ex;
    }

}

同樣的,如果我們需要處理目標方法拋出來的異常,這裏需要保證throwing指定的名稱與增強方法中的參數名稱一致

正常的返回(處理finally)通知

這個通過@After註解處理即可,實例與上面差別不大,這裏不再介紹

環繞通知

環繞通知需要帶上一個參數,這個參數表示正在處理的目標方法,同時能正常調用目標方法的邏輯。

@Pointcut("execution(* com.learn.aop.targetobject.*.*(..))")
public void pointCutExecution(){}

/**
需要加入一個ProceedingJoinPoint類型的參數,這個參數後面會詳細討論。
*/
@Around("pointCutExecution()")
public void ProceedJoinPointAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
    System.out.println("before operator");
    try {
        proceedingJoinPoint.proceed();
    } catch (Throwable throwable) {
        throwable.printStackTrace();
    }
    System.out.println("after operator");
}

目標方法

@Repository
public class AroundDao{
    public void aroundTargetMethod(){
        System.out.println("this is target around method");
    }
}

這裏附上一下環繞通知執行的實例

在這裏插入圖片描述

關於JoinPoint與ProceedingJoinPoint

上述實例中我們在環繞通知中加上了ProceedingJoinPoint的類型,其源碼如下:

import org.aspectj.runtime.internal.AroundClosure;

//繼承了JoinPoint
public interface ProceedingJoinPoint extends JoinPoint {

    void set$AroundClosure(AroundClosure arc);
     default void stack$AroundClosure(AroundClosure arc) {
    	 throw new UnsupportedOperationException();
     }
	//執行無參的目標方法的邏輯
    public Object proceed() throws Throwable;

    //執行帶有參數的目標方法的邏輯
    public Object proceed(Object[] args) throws Throwable;

}

JoinPoint這個類其實封裝了一下關於目標方法的一些信息,目標方法所在源文件的位置,目標方法的參數,目標方法的完整簽名的信息都可以通過JoinPoint獲取,其中最重要的方法如下

1.java.lang.Object[] getArgs():獲取連接點方法運行時的入參列表; 
2.Signature getSignature() :獲取連接點的方法簽名對象; 
3.java.lang.Object getTarget() :獲取連接點所在的目標對象; 
4.java.lang.Object getThis() :獲取連接點所在的代理對象本身;

Introduction

這個有點冷門,實際中用的不是很多,也是spring的一個新特性,這個運行我們給目標類做整體上的邏輯擴展,這裏貼上一個稍微容易理解的博客 —— introduction的實例

官網中是這樣介紹的

Introductions (known as inter-type declarations in AspectJ) enable an aspect to declare that advised objects implement a given interface, and to provide an implementation of that interface on behalf of those objects.

introductions(也可以稱爲AspectJ中聲明的內部類)讓切面類能顯式的持有一個實現了給定接口的通知對象。也能爲代表這些對象的接口提供一個默認的實現。(說真的,句子結構不難,但是這些單詞全在一起翻譯出來實在很奇怪)

至少我們知道了一個信息,introduction是一個AspectJ中的內部屬性,下面直接上實例吧

先準備一個接口和默認實現

public interface IIntroductionInterface {
    public void introduction();
}

@Service
public class DefaultImpl implements IIntroductionInterface {
    @Override
    public void introduction() {
        System.out.println("this introduction default implement");
    }
}

切面中用@DeclareParents註解指定接口的默認實現

@Aspect
@Component
public class IntroductionAspect {

    @DeclareParents(value="com.learn.aop.introduction.*",defaultImpl = DefaultImpl.class)
    public static IIntroductionInterface iIntroductionInterface;

}

然後這些默認的實現邏輯,可以嵌入到任何其他類的調用方法中

一個無關的類,沒有任何實現

@Service
public class OtherService {
}

測試代碼

public class AnnoIntroductionTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext =
                new AnnotationConfigApplicationContext(AopConfig.class);
        //這裏將獲取出來的OtherService強轉成IIntroductionInterface
        IIntroductionInterface aroundDao = (IIntroductionInterface)annotationConfigApplicationContext.getBean(OtherService.class);
        aroundDao.introduction();
    }
}

運行結果:

在這裏插入圖片描述

OtherService並沒有任何實現,卻能打印相關的輸出,這裏可以理解爲我們在類層面給目標類注入了一些邏輯。

AspectJ的初始化模式

這部分對應spring官網中的5.4.6節,其中剛開始的時候提示了一句話,這個初始化模式是Aspect的高級部分。

這裏就簡單介紹一下。AspectJ其實有五種初始化模式,但是spring只是支持其中兩種——prethis和pretarget。

這個直接給出實例

prethis

@Aspect("prethis(com.learn.aop.targetobject.IDao)")
@Component
@Scope("prototype")
public class PrethisAndPreTargetAspectJ {
}

這裏的PrethisAndPreTargetAspectJ是一個切面類,但是是多例的,prethis可以爲每一個代理類是com.learn.aop.targetobject.IDao的目標對象生成多個實例(每次增強的時候生成一個切面類)

pretarget

@Aspect("pretarget(com.learn.aop.targetobject.IndexDao)")
@Component
@Scope("prototype")
public class PrethisAndPreTargetAspectJ {
}

這裏的PrethisAndPreTargetAspectJ是一個切面類,但是是多例的,pretarget可以爲目標l類com.learn.aop.targetobject.IndexDao對象生成多個實例(每次增強的時候生成一個切面類)

總結

基本整理出了spring官網中關於AOP的關鍵點,這裏說明一下,通常我們實際開發中AOP可能常用於什麼數據庫事務中,但是那種並不是AOP的核心理念,那是spring事務的內容,只是我們採用聲明式事務的時候用到了aop的東西,這個內容在spring後續的事務中我們再深入總結。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章