歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Spring AOP 簡介以及簡單用法

Spring AOP 簡介以及簡單用法

日期:2017/3/1 9:08:18   编辑:Linux編程

如果你去面試Java開發, 那麼spring的AOP和DI幾乎是必問的問題。

那麼AOP是什麼呢?

一. AOP

所謂Aop就是 Aspect-OrientedProgramming, 中文就是面向切面編程。

我們之前聽說面向過程編程, 以及面向對象編程, 而這個面向切面編程我們可以視為是面向對象編程的1個補充(增強);

在一般的方法(函數)中, 為了方便,可能只寫了業務代碼
1. 業務代碼

而我們我們可以將其補充成4個部分。

  1. 驗證參數
  2. 前置日志
  3. 業務代碼
  4. 後置日志

1.1 橫切關注點 (Crosscutting Concerns)

大家看上面的方法, 就像用刀子把1個方法橫向切成4塊, 我們把上面除業務代碼外任意一個部分就叫做橫切關注點.

1.2 切面 (Aspect)

我們可以把橫切關注點進行分組, 其中任意一組就叫做切面

例如上面的例子中, 我們可以分成

  1. 驗證切面 (1. 驗證參數)
  2. 日志切面 (2. 前置日志, 4.後置日志)

1.3 通知 (Advice)

所謂通知就是切面要完成的工作。

例如對於日志通知來講, 裡面執行日志的方法我們就可以稱為1個通知。

1.4 目標 (Target)

所謂目標就是被通知的對象, 也就是指上面例子中的原方法本身啦。

1.5 代理 (Proxy)

當目標被通知後產生的對象就叫做代理, 因為AOP的原理就是利用代理來實現的, 如果想了解動態代理的可以參考這裡

1.6 連接點 (Joinpoint)

所謂Joinpoint就是程序執行到的某個位置,

上面例子中, 業務代碼方法的執行之前, 執行之後, 拋出異常後 都可以視為某個連接點。

1.7 切點 (PointCut)

每個方法都存在多個連接點, 而Spring AOP利用切點來定位到具體那些連接點。
Joinpoint 和 PointCut 的關系可以作如下比喻,假如Joinpoint的數據裡的記錄, 那麼PointCut就相當於查詢條件

1.8 小結

而Spring的AOP 能在不修改具體某個方法的前提下, 利用動態代理技術將通知注入到這個方法的各個連接點中, 令到這個方法得到了必要的補充。

對於上面的例子種, 我們原來的方法只有業務代碼, 但是我們可以利用Spring AOP加入 驗證參數, 日志等功能!

二. 具體例子

2.1 還沒使用AOP的例子

我們首先利用spring創建兩個計算類。

1個加法類, 1個減法類。

bean config xml

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    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-4.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">

    <!-- auto scan -->
    <context:component-scan base-package="com.home.aop"></context:component-scan>
</beans>

注意引入aop命名空間

計算接口 Calculator

package com.home.aop;

public interface Calculator {
    public double getResult(double a, double b);
}

加法類 AddCalculator

package com.home.aop;

import org.springframework.stereotype.Component;

@Component
public class AddCalculator implements Calculator {

    @Override
    public double getResult(double a, double b) {
        return a + b;
    }
}

減法類 SubCalculator

package com.home.aop;

import org.springframework.stereotype.Component;

@Component
public class SubCalculator implements Calculator {

    @Override
    public double getResult(double a, double b) {
        return a - b;
    }
}

Client 代碼

package com.home.aop;

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

public class AopMain {
    public static void f(){
        g();
    }

    public static void g(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-aop.xml");

        Calculator addCal = (Calculator) ctx.getBean("addCalculator");
        Calculator subCal = (Calculator) ctx.getBean("subCalculator");

        System.out.println(addCal.getResult(3, 1));
        System.out.println(subCal.getResult(3, 1));
    }
}

執行結果

Jul 05, 2016 10:10:00 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@5d79a4c9: startup date [Tue Jul 05 22:10:00 CST 2016]; root of context hierarchy
Jul 05, 2016 10:10:00 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [bean-aop.xml]
4.0
2.0

2.2 小結

上面例子中, 加法類和減法類中只包含了業務代碼, 我們可以將視為兩個Target(目標), 下面我們就利用AOP技術為這兩個Target加入通知。

2.3 AOP 前置通知

這個任務的需求很簡單。

  1. 不能修加法類和減法類
  2. 在加法類和減法類的getResult()方法執行時, 在業務代碼執行前輸出兩個參數(log).

首先maven裡要引入這些lib

<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${org.springframework.version}</version>
    </dependency>

        <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${org.springframework.version}</version>
 </dependency>

 <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.8.8</version>
    </dependency>

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

然後在bean-config xml裡加入下面這個句話, enable spring aop 功能

<!--  enable @Aspect -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

然後新建1個類LoggingAspect

package com.home.aop;

import java.util.Arrays;
import java.util.List;

import org.springframework.stereotype.Component;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
@Component
public class LoggingAspect {

    //@Before("execution(public double com.home.aop.AddCalculator.getResult(double,double))")
    @Before("execution(public * com.home.aop.*.*(..))")
    public void beforeExecute(JoinPoint joinPoint){
        String classname = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("before Execute! --class name: " + classname + ", method name: " + methodName + " " + args );
    }

}

方法beforeExecute的意思就是我們要為目標執行之前 而注入的方法。

上面@Before 注解表示這是1個前置通知。 括號裡面的就是PointCut(切點), 上面說過了, 相當於數據庫裡的查詢條件

然後Spring AOP 會根據PointCut 查找出所有符合條件的目標。

內容就很簡單了, 無非就是輸出被執行的類名方法名和參數…

執行結果:

Jul 06, 2016 12:30:03 AM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6924181b: startup date [Wed Jul 06 00:30:03 CST 2016]; root of context hierarchy
Jul 06, 2016 12:30:03 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [bean-aop.xml]
before Execute! --class name: AddCalculator, method name: getResult [3.0, 1.0]
4.0
before Execute! --class name: SubCalculator, method name: getResult [3.0, 1.0]
2.0

三 總結

項目中, 如果不用AOP 我們往往要為每個方法添加日志代碼, 十分難於維護, 可讀性也大大下降, 而AOP的出現, 就能解決這些問題。

Spring AOP四種實現方式 http://www.linuxidc.com/Linux/2016-10/135993.htm

Spring AOP自定義注解方式實現日志管理 http://www.linuxidc.com/Linux/2015-11/125019.htm

Spring AOP進行日志記錄 http://www.linuxidc.com/Linux/2015-11/124731.htm

使用Spring AOP進行性能監控 http://www.linuxidc.com/Linux/2012-07/64681.htm

利用Spring AOP 更新Memcached 緩存策略的實現 http://www.linuxidc.com/Linux/2012-03/56503.htm

Spring AOP的兩種代理 http://www.linuxidc.com/Linux/2015-11/125017.htm

Spring AOP的注解實例 http://www.linuxidc.com/Linux/2015-11/125018.htm

Copyright © Linux教程網 All Rights Reserved