框架源碼系列十:Spring AOP(AOP的核心概念回顧、Spring中AOP的用法、Spring AOP 源碼學習)

1、AOP的核心概念回顧

https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#aophtml

咱們先來看一下下面的這張圖java

 

說明:spring

程序運行時會調用不少方法,調用的不少方法就叫作Join points(鏈接點,能夠被選擇來進行加強的方法點),在方法的前或者後選擇一個地方來切入,切入的的地方就叫作Pointcut(切入點,選擇加強的方法),而後把要加強的功能(Advice)加入到切入點所在的位置。Advice和Pointcut組成一個切面(Aspect)express

AOP的幾個概念:編程

 

Advice、Pointcut、Weaving的特色:緩存

Advice(功能加強):app

1)用戶性:由用戶提供加強功能的邏輯代碼框架

2)變化的:不一樣的加強需求,會有不一樣的邏輯ide

3)可選時機:可選擇在方法前、後、異常時進行功能加強學習

4)多重的:同一個切入點上能夠有多重加強 

Pointcut(切入點):

1)用戶性:由用戶來指定

2)變化的:用戶可靈活指定

3)多點性:用戶能夠選擇在多個點上進行功能加強

Weaving(織入):

1)無侵入性,由於不改變原類的代碼

2)咱們在框架中實現

2、Spring中AOP的用法

1. 傳統Advisor方式

掌握用法:
1)編程提供Advice,實現對應的Advice接口
2)配置Advisor(advice+pointcut)

Advice接口:

 

示例代碼:

被加強的目標對象:

BeanQ

package com.study.leesmall.spring.sample.aop;

//被加強的目標對象
public class BeanQ {

    public void do1(String task, int time) {
        System.out.println("-------------do1 do " + task + " time:" + time);
    }

    public String service1(String name) {
        System.out.println("-------------servce1 do " + name);
        return name;
    }

    public String service2(String name) {
        System.out.println("-------------servce2 do " + name);
        if (!"s1".equals(name)) {
            throw new IllegalArgumentException("參數 name != s1, name=" + name);
        }

        return name + " hello!";
    }
}

 編程提供Advice,實現對應的Advice接口:

前置加強:

MyBeforeAdvice

package com.study.leesmall.spring.sample.aop;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

//前置加強
public class MyBeforeAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("------ MyBeforeAdvice before 加強 " + target + " " + method);
    }

}

環繞加強:

MyArroundAdvice

package com.study.leesmall.spring.sample.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

//環繞加強
public class MyArroundAdvice implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("--------- 環繞 -前加強");
        Object ret = invocation.proceed();
        System.out.println("--------- 環繞 -後加強");
        return ret;
    }

}

在/spring-source-study/src/main/java/com/study/leesmall/spring/sample/aop/application.xml裏面配置Advisor(advice+pointcut):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!-- 傳統方式的aop begin -->
    <!--被加強的目標對象  -->
    <bean id="BeanQ" class="com.study.leesmall.spring.sample.aop.BeanQ" />
    
    <!--配置advice  -->
    <bean id="myBeforeAdvice" class="com.study.leesmall.spring.sample.aop.MyBeforeAdvice" />
    <bean id="yyArroundAdvice" class="com.study.leesmall.spring.sample.aop.MyArroundAdvice" />
    
    <!--配置pointcut  -->
    <aop:config >

<!--全局切入點,任何一個aop-config均可以使用 --> <aop:pointcut id="doMethods" expression="execution(* com.study.leesmall.spring.sample.aop.*.do*(..))" /> <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="doMethods" /> <aop:advisor advice-ref="yyArroundAdvice" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))"/> </aop:config> <!-- 傳統方式的aop end --> </beans>

配置文件裏面的注意點:

<aop:config> 的屬性瞭解

proxy-target-class="false"使用jdk的動態代理 默認配置
proxy-target-class="true"使用cglib的動態代理

 

掌握 spring Aop 的 API:

Advice:

Advisor的代碼:

 * Copyright 2002-2017 the original author or authors.

package org.springframework.aop;

import org.aopalliance.aop.Advice;

/**
 * Base interface holding AOP <b>advice</b> (action to take at a joinpoint)
 * and a filter determining the applicability of the advice (such as
 * a pointcut). <i>This interface is not for use by Spring users, but to
 * allow for commonality in support for different types of advice.</i>
 *
 * <p>Spring AOP is based around <b>around advice</b> delivered via method
 * <b>interception</b>, compliant with the AOP Alliance interception API.
 * The Advisor interface allows support for different types of advice,
 * such as <b>before</b> and <b>after</b> advice, which need not be
 * implemented using interception.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
public interface Advisor {

    /**
     * Common placeholder for an empty {@code Advice} to be returned from
     * {@link #getAdvice()} if no proper advice has been configured (yet).
     * @since 5.0
     */
    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
     * @see org.aopalliance.intercept.MethodInterceptor
     * @see BeforeAdvice
     * @see ThrowsAdvice
     * @see AfterReturningAdvice
     */
    Advice getAdvice();

    /**
     * Return whether this advice is associated with a particular instance
     * (for example, creating a mixin) or shared with all instances of
     * the advised class obtained from the same Spring bean factory.
     * <p><b>Note that this method is not currently used by the framework.</b>
     * Typical Advisor implementations always return {@code true}.
     * Use singleton/prototype bean definitions or appropriate programmatic
     * proxy creation to ensure that Advisors have the correct lifecycle model.
     * @return whether this advice is associated with a particular target instance
     */
    boolean isPerInstance();

}

 

 

Pointcut:

 

Pointcut的子類:

 

 

Advisor的子類:

 

PointcutAdvisor擴展了Advisor之後就會有一個切面Aspect=Advice+Pointcut

PointcutAdvisor的代碼:

 * Copyright 2002-2012 the original author or authors.

package org.springframework.aop;

/**
 * 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.
 *
 * @author Rod Johnson
 */
public interface PointcutAdvisor extends Advisor {

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

}

 

PointcutAdvisor的子類:

測試類:

AopMain

package com.study.leesmall.spring.sample.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class AopMain {
    public static void main(String[] args) {
        ApplicationContext context = new GenericXmlApplicationContext(
                "classpath:com/study/leesmall/spring/sample/aop/application.xml");

        BeanQ bq = context.getBean(BeanQ.class);
        bq.do1("task1", 20);
        System.out.println();

        bq.service1("service1");

        System.out.println();
        bq.service2("ssss");
    }
}

測試結果:

------ MyBeforeAdvice before 加強 com.study.leesmall.spring.sample.aop.BeanQ@1d119efb public void com.study.leesmall.spring.sample.aop.BeanQ.do1(java.lang.String,int)
-------------do1 do task1 time:20

--------- 環繞 -前加強
-------------servce1 do service1
--------- 環繞 -後加強

--------- 環繞 -前加強
-------------servce2 do ssss
Exception in thread "main" java.lang.IllegalArgumentException: 參數 name != s1, name=ssss
    at com.study.leesmall.spring.sample.aop.BeanQ.service2(BeanQ.java:18)
    at com.study.leesmall.spring.sample.aop.BeanQ$$FastClassBySpringCGLIB$$3d1515ac.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at com.study.leesmall.spring.sample.aop.MyArroundAdvice.invoke(MyArroundAdvice.java:12)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
    at com.study.leesmall.spring.sample.aop.BeanQ$$EnhancerBySpringCGLIB$$2fc5343.service2(<generated>)
    at com.study.leesmall.spring.sample.aop.AopMain.main(AopMain.java:18)

2. Aspect語法方式

Aspect的advice是基於方法的。

掌握用法:

1)定義包含Advice方法的Bean類

2)配置Bean定義

3)配置Aspect(引用包含advice方法的bean),在裏面配置各類Advice(method+pointcut)

定義包含Advice方法的Bean類:

AspectAdviceBean:

package com.study.leesmall.spring.sample.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

//定義包含 Advice 方法的 Bean 類
public class AspectAdviceBean {

    public void before1() {
        System.out.println("----------- AspectAdviceBean before1 加強 ");
    }

    //JoinPoint看具體哪一個方法被加強了,JoinPoint必定要放在第一個參數
    public void before2(JoinPoint jp) {
        System.out.println("----------- AspectAdviceBean before2 加強 for " + jp);
    }

    //調用被加強的方法時傳參數
    //<aop:before method="before3" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) 
    //and args(tk,..)" arg-names=""/>
    //args(tk,..)有兩個意思,第一個意思是被加強的方法的第一個參數的類型要和before3的參數tk的類型同樣
    //第二個意思是被加強的方法的第一個參數tk要賦值給before3的參數tk
    //arg-names="" 當不能肯定方法參數的順序時能夠用這個參數指定arg-names="param1,param2"
    public void before3(String tk) {
        System.out.println("----------- AspectAdviceBean before3  加強  參數tk= " + tk);
    }

    //調用被加強的方法時傳參數
    public void before4(String tk, int ti) {
        System.out.println("----------- AspectAdviceBean before4  加強  參數tk= " + tk + " ti=" + ti);
    }

    //ProceedingJoinPoint正在處理的方法
    public Object arround1(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("----------- AspectAdviceBean arround1 環繞-前加強 for " + pjp);
        Object ret = pjp.proceed();
        System.out.println("----------- AspectAdviceBean arround1 環繞-後加強 for " + pjp);
        return ret;
    }

    public Object arround2(ProceedingJoinPoint pjp, String name) throws Throwable {
        System.out.println("--------- AspectAdviceBean arround2 參數 name=" + name);
        System.out.println("----------- AspectAdviceBean arround2 環繞-前加強 for " + pjp);
        Object ret = pjp.proceed();
        System.out.println("----------- AspectAdviceBean arround2 環繞-後加強 for " + pjp);
        return ret;
    }

    public void afterReturning(Object retValue) {
        System.out.println("----------- AspectAdviceBean afterReturning 加強 , 返回值爲: " + retValue);
    }

    public void afterThrowing(JoinPoint jp, Exception e) {
        System.out.println("----------- AspectAdviceBean afterThrowing 加強  for " + jp);
        System.out.println("----------- AspectAdviceBean afterThrowing 加強  異常 :" + e);
    }

    public void after(JoinPoint jp) {
        System.out.println("----------- AspectAdviceBean after 加強  for " + jp);
    }

}

JoinPoint和ProceedingJoinPoint :

 

在/spring-source-study/src/main/java/com/study/leesmall/spring/sample/aop/application.xml裏面配置Bean定義

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!-- 傳統方式的aop begin -->
    <!--被加強的目標對象  -->
    <bean id="BeanQ" class="com.study.leesmall.spring.sample.aop.BeanQ" />
    
    <!--配置advice  -->
    <bean id="myBeforeAdvice" class="com.study.leesmall.spring.sample.aop.MyBeforeAdvice" />
    <bean id="yyArroundAdvice" class="com.study.leesmall.spring.sample.aop.MyArroundAdvice" />
    
    <!--配置pointcut  -->
    <aop:config >
        <aop:pointcut id="doMethods" expression="execution(* com.study.leesmall.spring.sample.aop.*.do*(..))" />
        <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="doMethods" />
        
        <aop:advisor advice-ref="yyArroundAdvice"
            pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))"/>
    </aop:config>
    <!-- 傳統方式的aop end -->
    
    <!-- AspectJ的aop begin -->
    <!-- 配置了包含advice方法的Bean -->
    <bean id="aspectAdviceBean" class="com.study.leesmall.spring.sample.aop.AspectAdviceBean" />

    
</beans>
    
    
    

3)配置Aspect(引用包含advice方法的bean),在裏面配置各類Advice(method+pointcut)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!-- 傳統方式的aop begin -->
    <!--被加強的目標對象  -->
    <bean id="BeanQ" class="com.study.leesmall.spring.sample.aop.BeanQ" />
    
    <!--配置advice  -->
    <bean id="myBeforeAdvice" class="com.study.leesmall.spring.sample.aop.MyBeforeAdvice" />
    <bean id="yyArroundAdvice" class="com.study.leesmall.spring.sample.aop.MyArroundAdvice" />
    
    <!--配置pointcut  -->
    <aop:config >
        <aop:pointcut id="doMethods" expression="execution(* com.study.leesmall.spring.sample.aop.*.do*(..))" />
        <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="doMethods" />
        
        <aop:advisor advice-ref="yyArroundAdvice"
            pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))"/>
    </aop:config>
    <!-- 傳統方式的aop end -->
    
    <!-- AspectJ的aop begin -->
    <!-- 配置了包含advice方法的Bean -->
    <bean id="aspectAdviceBean" class="com.study.leesmall.spring.sample.aop.AspectAdviceBean" />
    
    <!--配置 Aspect (引用包含 advice 方法的 bean),在裏面配置各類 Advice(method + pointcut) -->
    <aop:config>
        <aop:pointcut id="services" expression="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))" />
        <aop:aspect id="a1" ref="aspectAdviceBean" order="1">
            <aop:before method="before1" pointcut-ref="doMethods" />
            <aop:before method="before2" pointcut-ref="doMethods"/>
            <!--args(tk,..)有兩個意思,第一個意思是被加強的方法的第一個參數的類型要和before3的參數tk的類型同樣
            第二個意思是被加強的方法的第一個參數tk要賦值給before3的參數tk;
            arg-names="" 當不能肯定方法參數的順序時能夠用這個參數指定arg-names="param1,param2"
              -->
            <aop:before method="before3" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) and args(tk,..)"/>
            <aop:before method="before4" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) and args(tk,ti)"/>
            <aop:around method="arround1" pointcut-ref="services"/>
            <aop:around method="arround2" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..)) and args(name)"/>
            <aop:after-returning method="afterReturning" pointcut-ref="services" returning="retValue"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="services" throwing="e"/>
            <aop:after method="after" pointcut-ref="services"/>
        </aop:aspect>
    </aop:config>
    <!-- AspectJ的aop end -->

    
</beans>
    
    
    

被加強的目標對象:

package com.study.leesmall.spring.sample.aop;

//被加強的目標對象
public class BeanQ {

    public void do1(String task, int time) {
        System.out.println("-------------do1 do " + task + " time:" + time);
    }

    public String service1(String name) {
        System.out.println("-------------servce1 do " + name);
        return name;
    }

    public String service2(String name) {
        System.out.println("-------------servce2 do " + name);
        /**if (!"s1".equals(name)) {
            throw new IllegalArgumentException("參數 name != s1, name=" + name);
        }**/

        return name + " hello!";
    }
}

測試類:

AopMain

package com.study.leesmall.spring.sample.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class AopMain {
    public static void main(String[] args) {
        ApplicationContext context = new GenericXmlApplicationContext(
                "classpath:com/study/leesmall/spring/sample/aop/application.xml");

        BeanQ bq = context.getBean(BeanQ.class);
        bq.do1("task1", 20);
        System.out.println();

        bq.service1("service1");

        System.out.println();
        bq.service2("ssss");
    }
}

測試結果:

----------- AspectAdviceBean before1 加強 
----------- AspectAdviceBean before2 加強 for execution(void com.study.mike.spring.sample.aop.BeanQ.do1(leesmall,int))
----------- AspectAdviceBean before3  加強  參數tk= task1
----------- AspectAdviceBean before4  加強  參數tk= task1 ti=20
------ MyBeforeAdvice before 加強 com.study.mike.spring.sample.aop.BeanQ@3a0baae5 public void com.study.mike.spring.sample.aop.BeanQ.do1(java.lang.leesmall,int)
-------------do1 do task1 time:20

----------- AspectAdviceBean arround1 環繞-前加強 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))
--------- AspectAdviceBean arround2 參數 name=service1
----------- AspectAdviceBean arround2 環繞-前加強 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))
--------- 環繞 -前加強
-------------servce1 do service1
--------- 環繞 -後加強
----------- AspectAdviceBean arround2 環繞-後加強 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))
----------- AspectAdviceBean arround1 環繞-後加強 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))
----------- AspectAdviceBean afterReturning 加強 , 返回值爲: service1
----------- AspectAdviceBean after 加強  for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service1(leesmall))

----------- AspectAdviceBean arround1 環繞-前加強 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))
--------- AspectAdviceBean arround2 參數 name=ssss
----------- AspectAdviceBean arround2 環繞-前加強 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))
--------- 環繞 -前加強
-------------servce2 do ssss
--------- 環繞 -後加強
----------- AspectAdviceBean arround2 環繞-後加強 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))
----------- AspectAdviceBean arround1 環繞-後加強 for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))
----------- AspectAdviceBean afterReturning 加強 , 返回值爲: ssss hello!
----------- AspectAdviceBean after 加強  for execution(leesmall com.study.mike.spring.sample.aop.BeanQ.service2(leesmall))

3. AspectJ註解方式

1)先在pom.xml文件裏面引入AspectJ的依賴

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.1</version>
        </dependency>

 

2)被加強的目標對象

package com.study.leesmall.spring.sample.aop;

//被加強的目標對象
public class BeanQ {

    public void do1(String task, int time) {
        System.out.println("-------------do1 do " + task + " time:" + time);
    }

    public String service1(String name) {
        System.out.println("-------------servce1 do " + name);
        return name;
    }

    public String service2(String name) {
        System.out.println("-------------servce2 do " + name);
        if (!"s1".equals(name)) {
            throw new IllegalArgumentException("參數 name != s1, name=" + name);
        }

        return name + " hello!";
    }
}

3)AspectJ註解方式實現AOP

AspectAdviceBeanUseAnnotation

package com.study.leesmall.spring.sample.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

//AspectJ註解方式
@Aspect
public class AspectAdviceBeanUseAnnotation {

    // 定義一個全局的Pointcut
    @Pointcut("execution(* com.study.leesmall.spring.sample.aop.*.do*(..))")
    public void doMethods() {
    }

    // 定義一個全局的Pointcut
    @Pointcut("execution(* com.study.leesmall.spring.sample.aop.*.service*(..))")
    public void services() {
    }

    // 定義一個Before Advice
    @Before("doMethods() and args(tk,..)")
    public void before3(String tk) {
        System.out.println("----------- AspectAdviceBeanUseAnnotation before3  加強  參數tk= " + tk);
    }

    //環繞加強
    @Around("services() and args(name,..)")
    public Object around2(ProceedingJoinPoint pjp, String name) throws Throwable {
        System.out.println("--------- AspectAdviceBeanUseAnnotation arround2 參數 name=" + name);
        System.out.println("----------- AspectAdviceBeanUseAnnotation arround2 環繞-前加強 for " + pjp);
        Object ret = pjp.proceed();
        System.out.println("----------- AspectAdviceBeanUseAnnotation arround2 環繞-後加強 for " + pjp);
        return ret;
    }

    @AfterReturning(pointcut = "services()", returning = "retValue")
    public void afterReturning(Object retValue) {
        System.out.println("----------- AspectAdviceBeanUseAnnotation afterReturning 加強 , 返回值爲: " + retValue);
    }

    @AfterThrowing(pointcut = "services()", throwing = "e")
    public void afterThrowing(JoinPoint jp, Exception e) {
        System.out.println("----------- AspectAdviceBeanUseAnnotation afterThrowing 加強  for " + jp);
        System.out.println("----------- AspectAdviceBeanUseAnnotation afterThrowing 加強  異常 :" + e);
    }

    @After("doMethods()")
    public void after(JoinPoint jp) {
        System.out.println("----------- AspectAdviceBeanUseAnnotation after 加強  for " + jp);
    }

}

4)在/spring-source-study/src/main/java/com/study/leesmall/spring/sample/aop/application2.xml裏面配置bean和開啓AspectJ註解的支持

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!--被加強的目標對象  -->
    <bean id="BeanQ" class="com.study.leesmall.spring.sample.aop.BeanQ" />
    
    <!--AspectJ註解方式實現的AOP  -->
    <bean id="aspectAdviceBeanUseAnnotation" class="com.study.leesmall.spring.sample.aop.AspectAdviceBeanUseAnnotation" />
    
    <!--開啓AspectJ註解的支持  -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
</beans>
    
    
    

開啓@Aspectj註解方式支持:
Xml中<aop:aspectj-autoproxy></aop:aspectj-autoproxy>注意瞭解它的屬性、及子元素

註解方式開啓:

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}

 

5)測試類

package com.study.leesmall.spring.sample.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class AopMainUseAspectAnnotation {
    public static void main(String[] args) {
        ApplicationContext context = new GenericXmlApplicationContext(
                "classpath:com/study/leesmall/spring/sample/aop/application2.xml");

        BeanQ bq = context.getBean(BeanQ.class);
        bq.do1("task1", 20);
        System.out.println();

        bq.service1("service1");

    }
}

7)測試結果

----------- AspectAdviceBeanUseAnnotation before3  加強  參數tk= task1
-------------do1 do task1 time:20
----------- AspectAdviceBeanUseAnnotation after 加強  for execution(void com.study.leesmall.spring.sample.aop.BeanQ.do1(String,int))

--------- AspectAdviceBeanUseAnnotation arround2 參數 name=service1
----------- AspectAdviceBeanUseAnnotation arround2 環繞-前加強 for execution(String com.study.leesmall.spring.sample.aop.BeanQ.service1(String))
-------------servce1 do service1
----------- AspectAdviceBeanUseAnnotation arround2 環繞-後加強 for execution(String com.study.leesmall.spring.sample.aop.BeanQ.service1(String))
----------- AspectAdviceBeanUseAnnotation afterReturning 加強 , 返回值爲: service1

3、Spring AOP 源碼學習

一、spring aop的工做流程是怎樣?以傳統的Advisor配置爲例進行思考

2. 源碼閱讀思路

一、先看配置解析,看標籤解析過程都作了什麼、完成了什麼。

入口:
E:\repository\org\springframework\spring-aop\5.1.3.RELEASE\spring-aop-5.1.3.RELEASE.jar/META-INF/spring.handlers

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

org.springframework.aop.config.AopNamespaceHandler:

a)解析<aop:config> 及它的子元素

org.springframework.aop.config.ConfigBeanDefinitionParser:

 

1)註冊了autoProxyCreator的Bean定義,它是一個ProxyConfig、BeanFactoryAwrare、BeanPostProcessor

下面來看一下配置自動代理的建立者的代碼:

org.springframework.aop.config.ConfigBeanDefinitionParser.configureAutoProxyCreator(ParserContext, Element)

 ->

org.springframework.aop.config.AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(ParserContext, Element)

 ->

org.springframework.aop.config.AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry, Object)

 ->

org.springframework.aop.config.AopConfigUtils.registerOrEscalateApcAsRequired(Class<?>, BeanDefinitionRegistry, Object)

因此說自動代理建立者是一個ProxyConfig、BeanFactoryAwrare、BeanPostProcessor

 ->

org.springframework.aop.config.AopConfigUtils.findPriorityForClass(String)

 

說明:

org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator如今已經不使用了

org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator對應於AspectJ的xml方式

<aop:config>
        <aop:pointcut id="services" expression="execution(* com.study.leesmall.spring.sample.aop.*.service*(..))" />
        <aop:aspect id="a1" ref="aspectAdviceBean" order="1">
            <aop:before method="before1" pointcut-ref="doMethods" />
            <aop:before method="before2" pointcut-ref="doMethods"/>
            <!--args(tk,..)有兩個意思,第一個意思是被加強的方法的第一個參數的類型要和before3的參數tk的類型同樣
            第二個意思是被加強的方法的第一個參數tk要賦值給before3的參數tk;
            arg-names="" 當不能肯定方法參數的順序時能夠用這個參數指定arg-names="param1,param2"
              -->
            <aop:before method="before3" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) and args(tk,..)" arg-names=""/>
            <aop:before method="before4" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.do*(..)) and args(tk,ti)"/>
            <aop:around method="arround1" pointcut-ref="services"/>
            <aop:around method="arround2" pointcut="execution(* com.study.leesmall.spring.sample.aop.*.service*(..)) and args(name)"/>
            <aop:after-returning method="afterReturning" pointcut-ref="services" returning="retValue"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="services" throwing="e"/>
            <aop:after method="after" pointcut-ref="services"/>
        </aop:aspect>
    </aop:config>

 查看org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator的繼承體系:

 父類:

子類:

查看org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator的繼承體系,搞清楚它繼承實現了什麼,他的父類和子類有哪些

父類:

 

 子類:

org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator對應於AspectJ的註解方式:

<bean id="aspectAdviceBeanUseAnnotation" class="com.study.leesamll.spring.sample.aop.AspectAdviceBeanUseAnnotation" />
    
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 <aop:config proxy-target-class="false" expose-proxy="false">對應代碼:

org.springframework.aop.config.AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(ParserContext, Element)

->
org.springframework.aop.config.AopNamespaceUtils.useClassProxyingIfNecessary(BeanDefinitionRegistry, Element)

2)兩種配置方式(advisor和aspectJ)解析以後都是向Bean工廠註冊了Pointcut和Advisor的Bean定義

b)<aop:aspectj-autoproxy>開啓AspectJ註解支持
1)註冊了autoProxyCreator的Bean定義,它是一個ProxyConfig、BeanFactoryAwrare、BeanPostProcessor

org.springframework.aop.config.AopNamespaceHandler.init()

 

org.springframework.aop.config.AspectJAutoProxyBeanDefinitionParser

org.springframework.aop.config.AspectJAutoProxyBeanDefinitionParser.parse(Element, ParserContext)

org.springframework.aop.config.AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext, Element)

 

<!--開啓AspectJ註解的支持  -->
    <aop:aspectj-autoproxy>
        <!-- 知足name裏面的表達式(bean名稱的表達式)的才進行切面的處理 -->
        <aop:include name="bean名稱的表達式"/>
    </aop:aspectj-autoproxy>

 

<aop:include name="bean名稱的表達式"/>對應源碼:

 

c)AspectJ註解的切面在哪裏讀取加載的?

可能的地方:

BeanDefinitionRegistryPostProcessor x

BeanFactoryPostProcessor x

InstantiationAwareBeanPostProcessor Bean 實例建立先後

BeanPostProcessor

Xml方式的解析:
org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator
註解方式的解析:
org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
二者的關係:
public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator

org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors()

 

 BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors() 中完成了註解的讀取、Advisor對象的建立及緩存。

 org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors()

 

org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory.getAdvisors(MetadataAwareAspectInstanceFactory)

 

二、 看織入的過程

a) 在哪裏作的織入?

org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors()裏面打個斷點拿到調用棧

入口:

com.study.leesmall.spring.sample.aop.AopMainUseAspectAnnotation

 

b) 如何判斷 Bean 要不要被建立代理?如何排除 advice Bean 的?

排除 advice Bean :

跳過advice bean 跳過帶有@Aspect註解的本身,不能本身爲本身建立代理,不然進入死循環,如AspectAdviceBeanUseAnnotation

org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator.shouldSkip(Class<?>, String)

 

 

 再次進來,是在 PostProcessAfterInitialization()

 

建立代理的方法:

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(Object, String, Object)

如何判斷 Bean 要不要被建立代理:

 

c)如何選擇 jdk動態代理 仍是cglib的動態代理

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(Class<?>, String, Object[], TargetSource)

org.springframework.aop.framework.ProxyFactory.getProxy(ClassLoader)

org.springframework.aop.framework.ProxyCreatorSupport.createAopProxy()

org.springframework.aop.framework.DefaultAopProxyFactory.createAopProxy(AdvisedSupport)

 

d) 如何來建立代理的,涉及哪些類,如何協做的。

AutoProxyCreator

ProxyConfig ProcxyFactory

AopProxyFactory

AopProxy

3 、看方法被調用時的加強過程

a) 在代理中如何決定對當前方法合格的 advice 的?
  調用的 Advisor 中 Pointcut 進行匹配
b) 如何組織多個 advice 執行的?
  責任鏈模式

org.springframework.aop.framework.JdkDynamicAopProxy.invoke(Object, Method, Object[])

org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()

四、源碼對應的類圖

相關文章
相關標籤/搜索