Spring AOP 之一:基本概念與流程

基本概念

  1. Joinpoint:攔截點,鏈接點。如:業務邏輯模塊中的方法或異常java

  2. Pointcut:Joinpoint的表達式,表示攔截哪些方法。一個Pointcut對應多個Joinpointspring

  3. Advice: 通知,要切入的邏輯express

  4. Before Advice: 在方法(鏈接點)前切入apache

  5. After Advice: 在方法(鏈接點)後切入,拋出異常時也會切入編程

  6. After Returning Advice: 在方法(鏈接點)返回後切入,拋出異常則不會切入maven

  7. After Throwing Advice: 在方法(鏈接點)拋出異常時切入ide

  8. Around Advice: 在方法(鏈接點)執行先後切入,能夠中斷或忽略原有流程的執行工具

  9. Aspect:切面,整個切入邏輯的抽象this

上面的概念若是是第一次接觸,那麼就顯得很是抽象。沒有關係,先拋開上面的概念,先來了解一下Spring AOP(或者更加廣義的說面向切面編程到底是什麼)。.net

能夠這樣理解:AOP是爲了非侵入式的處理一類邏輯。舉一個老生常談的例子:記錄方法執行時間,不少方法可能都須要這個功能,很顯然咱們不肯意去爲每個方法都寫記錄時間的代碼,這些代碼會重複,會和業務邏輯耦合,這是代碼的"bad smell"。

記錄時間方法這就是一類邏輯,咱們能夠把這一類邏輯處理爲一個Aspect(切面),固然這部分邏輯只是切面中的Advice(通知)部分,咱們還須要指定那些方法(Joinpont)須要執行這個記錄時間邏輯(Advice)。(Advice 其實包含3部分,什麼地點,什麼時間,作什麼工做)

因此AOP簡單總結:在什麼地點(Joinponit,多個地點Pointcut)什麼時間作什麼(Advice),Advice封裝了何時作、什麼地方和作什麼。

實例

上面的概念部分仍是過於抽象,下面咱們經過一個實例,把概念和實例一一對應來說,我儘可能簡化一些概念的東西,先着重於流程部分,這樣有利於從總體上把握AOP,而不是限於概念之中。

下圖是一個工程的總體的目錄結構圖,能夠對總體結構有一個瞭解。

工程目錄

下面的例子都使用的是AspectJ的註解的方式處理的,爲的是儘可能讓代碼簡化清晰,讓咱們專一在AOP上,而不是各類配置上。

業務方法

public interface HaveResultBusiness {
    
    Integer getResult(Integer div);

}
import org.springframework.stereotype.Service;

import cn.freemethod.business.HaveResultBusiness;

@Service
public class HaveResultBusinessImpl implements HaveResultBusiness {

    @Override
    public Integer getResult(Integer div) {
        System.out.println("HaveResultBusinessImpl getResult...");
        Integer result = 100 / div;
        return result;
    }

}

業務邏輯部分是很是簡單的就是獲取100除以div的結果,每個方法均可以做爲一個Joinpoint,可是Spring AOP只是抽象了Pointcut,因此咱們下面看一下Pointcut。

Pointcut

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class HaveResultBusinessPointcut {
    
    @Pointcut("this(cn.freemethod.business.HaveResultBusiness)")
//    @Pointcut("execution(* cn.freemethod.business.HaveResultBusiness.*(..))")
    public void haveResultBusinessPointcut(){};

}

上面是單獨定義Pointcut,爲了方便多出引用,固然也能夠直接寫在Advice中。咱們把@Pointcut("this(cn.freemethod.business.HaveResultBusiness)")中的this(cn.freemethod.business.HaveResultBusiness)部分稱做爲Pointcut表達式。定義的就是什麼地方,Pointcut的表達式有不少類型,例如上面的this表達式,還有expression表達式等,本文不詳細介紹,下一篇文章會仔細介紹。 @Pointcut("this(cn.freemethod.business.HaveResultBusiness)")表達式表示的是:實現了cn.freemethod.business.HaveResultBusiness接口的全部方法(鏈接點,Joinpoint)。

Aspect 切面

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.springframework.stereotype.Component;

@Component
@Aspect
public class TimingAspect {

    @Before("cn.freemethod.pointcut.HaveResultBusinessPointcut.haveResultBusinessPointcut()")
    // @Before("execution(* cn.freemethod.business.HaveResultBusiness.*(..))")
    public void before() {
        System.out.println("TimingAspect before...");
    }

    @Around("cn.freemethod.pointcut.HaveResultBusinessPointcut.haveResultBusinessPointcut()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        Object retVal = pjp.proceed();
        long end = System.currentTimeMillis();
        System.out.println("time elapse:" + (end - start));
        return retVal;
    }

    @After("cn.freemethod.pointcut.HaveResultBusinessPointcut.haveResultBusinessPointcut()")
    public void after() {
        System.out.println("TimingAspect after...");
    }

    @AfterThrowing(pointcut = "cn.freemethod.pointcut.HaveResultBusinessPointcut.haveResultBusinessPointcut()", throwing = "ex")
    public void doRecoveryActions(Exception ex) {
        System.out.println("@AfterThrowing:" + ex.getMessage());
    }

    @AfterReturning(pointcut = "cn.freemethod.pointcut.HaveResultBusinessPointcut.haveResultBusinessPointcut()", returning = "retVal")
    public void doAccessCheck(Object retVal) {
        System.out.println("@AfterReturning:"+retVal);
    }

}

上面就是最核心的Aspect類了,@Aspect代表這個類是一個切面,注意要和@Componet之類的組合使用。在這個Aspect中定義了5中類型的Advice(通知),每個Advice都使用了cn.freemethod.pointcut.HaveResultBusinessPointcut.haveResultBusinessPointcut()這一個Pointcut。

@Before是在Pointcut以前執行,@After是在Pointcut以後執行。

@Around封裝了鏈接點(方法)的處理過程,能夠在方法先後注入邏輯

@AfterThrowing是在拋出異常以後執行,能夠經過throwing = "ex"把異常注入進來

@AfterReturning是在返回以後執行,能夠經過returning = "retVal"的方式把返回值注入進來。

配置

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"cn.freemethod"})
public class AspectConfig {

}

@EnableAspectJAutoProxy是對AspectJ的支持,@ComponentScan配置掃描cn.freemethod及其子包。

工具

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

//@Component
public class ApplicationContextProvider implements ApplicationContextAware {

    private static ApplicationContext context;

    public void setApplicationContext(ApplicationContext context) throws BeansException {
        ApplicationContextProvider.context=context;
        String[] names = context.getBeanDefinitionNames();
        for(String name : names){
            System.out.println(name);
        }
    }

    public static ApplicationContext getApplicationContext() {
        return context;
    }

}

打印容器中的bean名字,這個查錯經常使用,爲了避免影響輸出已經註釋了。

啓動

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;

import cn.freemethod.business.HaveResultBusiness;
import cn.freemethod.config.AspectConfig;

public class AnnotationConfigStart {
    
    public static void main(String[] args) {
        AbstractApplicationContext  context = new AnnotationConfigApplicationContext(AspectConfig.class);
        HaveResultBusiness haveResultBusiness = context.getBean(HaveResultBusiness.class);
        Integer result = haveResultBusiness.getResult(100);
        System.out.println(result);
        haveResultBusiness.getResult(0);
        context.close();
    }

}

部分輸出

HaveResultBusinessImpl getResult...
time elapse:0
TimingAspect after...
@AfterReturning:1
1
TimingAspect before...
HaveResultBusinessImpl getResult...
TimingAspect after...
@AfterThrowing:/ by zero
java.lang.ArithmeticException: / by zero
    at cn.freemethod.business.impl.HaveResultBusinessImpl.getResult(HaveResultBusinessImpl.java:13)

附錄

參考

關於Spring的註解配置能夠參考:Spring註解配置

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>cn.freemethod</groupId>
  <artifactId>aop</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <properties>
        <spring_version>4.1.6.RELEASE</spring_version>
        <cglib_version>3.2.4</cglib_version>
        <javassist_version>3.12.1.GA</javassist_version>
        <junit_version>4.9</junit_version>
    </properties>
  
  <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring_version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring_version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring_version}</version>
        </dependency>
        
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit_version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring_version}</version>
            <scope>test</scope>
        </dependency>
        
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.7.3</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.11</version>
        </dependency>
  </dependencies>
</project>

資源連接

完整工程代碼

Spring AOP 之一:基本概念與流程

Spring AOP 之二:Pointcut註解表達式

Spring AOP 之三:通知(Advice)方法參數

相關文章
相關標籤/搜索