0047 Spring的AOP入門基礎--切面組件--通知--切入點


AOP的全稱是Aspect Oriented Programming,面向切面編程。

切面是什么呢,就是一個Java類。之所以叫切面,就是說在調用其他一些類的方法的某個時候(之前,之后,拋異常等),調用這個類里的某個方法,因為要對很多類的很多方法進行相同的作用,就像一刀切一樣。

aop涉及到三個要素:將哪個類的哪個方法,在什么時候,切入到哪個類的哪個方法上。依次對應了切面組件Aspect、通知Advice、切入點pointcut

看下面的示例,一個Controller組件,一個Service組件,兩個Aspect組件,一個測試類,代碼如下:

//Controller組件
package net.sonng.controller;

import javax.annotation.Resource;

import net.sonng.service.TestService;

import org.springframework.stereotype.Controller;

@Controller
public class TestController {
    @Resource
    private TestService ts;
    public void testA(){
        System.out.println("Controller的testA方法開始執行");
        ts.testA();
        ts.testB();
    }
    
    public void testB(){
        System.out.println("Controller的testB方法開始執行");
    }
}


//Service組件
package net.sonng.service;

import org.springframework.stereotype.Service;

@Service
public class TestService {
    public void testA (){
        System.out.println("TestService的TestA方法開始執行");
    }
    
    public void testB(){
        System.out.println("TestService的TestB方法開始執行");
    }
}

//切面A
package net.sonng.aspect;

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

@Component
public class TestLogA {
    public void log(){
        System.out.println("TestLogA是切面組件,切入點是Controller的方法前面");
    }
}

//切面B
package net.sonng.aspect;

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

@Component
@Aspect
public class TestLogB {
    
    @Before("execution(* net.sonng.service.TestService.testA(..))")   //方法限定表達式
    public void log(){
        System.out.println("TestLogB是切面組件,在調用Service方法前調用");
    }
}

//測試類
package net.sonng.test;

import net.sonng.controller.TestController;

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

public class Test {
    public static void main(String[] args){
        ApplicationContext ac=new ClassPathXmlApplicationContext("ac.xml");
        TestController tc=ac.getBean("testController",TestController.class);
        tc.testA();
        tc.testB();
        
    }
}

采用xml配置,TestLogA的log方法在Controller組件的所有方法調用前調用

ac.xml配置如下:

<mvc:annotation-driven />   <!-- 開啟注解掃描 -->
<context:component-scan base-package="net.sonng" />  <!-- 開啟組件掃描 -->
<aop:aspectj-autoproxy />                            <!-- 開啟AOP注解掃描 -->

<bean id="log" class="net.sonng.aspect.TestLogA"></bean>  <!-- 聲明切面組件 -->
<aop:config>
    <aop:aspect ref="log" >                               <!-- 以log為切面組件 -->
        <aop:before method="log" pointcut="within(net.sonng.controller.TestController)"/>
    </aop:aspect>    <!-- aop:before是前置通知;method表示要調用的切面的方法;pointcut表示切入點,可以用類型/方法限定表達式 -->
</aop:config> 
<!-- 這個xml配置表示:在net.sonng.controller.TestController下的所有方法被調用前,先調用net.sonng.aspect.TestLogA的log方法 -->

用注解配置,TestLogB的log方法在TestService的testA()方法調用前調用

注解配置見代碼,要點如下:
1. 用@Aspect聲明TestLogB是一個切面組件
2. 在log方法前添加@Before注解,這是前置通知
3. 在@Before注解的屬性中指定切入點,這里用的是方法限定表達式

測試類輸出如下:

TestLogA是切面組件,切入點是Controller的方法前面 //TestLogA的log方法在Controller的testA()方法前調用
Controller的testA方法開始執行
TestLogB是切面組件,在調用Service方法前調用 //TestLogB的log方法在Service的testA()方法前調用
TestService的TestA方法開始執行
TestService的TestB方法開始執行 //因為TestLogB的log方法指定了只在testA前調用,在testB前不調用
TestLogA是切面組件,切入點是Controller的方法前面 //TestLogA的log方法在Controller的testB()方法前調用
Controller的testB方法開始執行

切面組件Aspect

切面組件一般是登錄檢查、日志記錄、事務管理等
xml配置中先聲明bean,再在aop:config的aop:aspect中指定為切面
或者用@Aspect注解指定

通知advice

通知就是:在調用目標方法的什么時候調用切面組件的方法,可以是:
前置通知:@Before,方法調用之前
后置通知:@AfterReturning,方法正常返回
異常通知:@AfterThrowing,方法調用過程中出現異常
最終通知:@After,不管方法是正常返回還是出現異常,這個通知都會調用,因此必須這兩種可能的情況
環繞通知:@Around,

切入點

切入點就是要在哪些類的哪些方法上進行切入,可以限定一些類下的所有方法,或者不同類下的一些方法,或者指定類下的指定方法
方法限定表達式:
    基本模式:execution([修飾符] 返回值類型 方法名(參數) [異常類型])。具體見Spring Reference 4.3.6版第223頁
    execution(public * * (..)):所有 的public方法
    execution(* set(..)):以set打頭的任意方法
    execution(
com.xyz.service.SomeService.(..):SomeService類下的任意方法
    execution(
com.xyz.service..(..)):service包下的任意類的任意方法
類型限定表達式:
    within(com.xyz.service.):service包下的任意類下的任意方法,不包含子包
    within(com.xyz.service..
):包含子包


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM