1. 程式人生 > >6.spring:AOP(註解)

6.spring:AOP(註解)

 spring Aop

 AOP面向切面程式設計,與OOP面向物件程式設計相輔相成

AOP中最基本的單元是切面

 

問題:
程式碼混亂:越來越多的業務需求(日誌&驗證)加入後,原有的業務方法急劇膨脹,每個方法在處理核心程式碼的同時還必須兼顧其他的多個關注點
程式碼分散,以日誌為例,知識為了滿足這一單一的需求,就不得不在程式碼塊中裡多次呼叫重複的日誌程式碼,如果日誌發生變化,必須修改所有的模組

簡介:
AOP:面向切面程式設計,是一種新的方法,式傳統OOP面向物件程式設計的補充
AOP主要程式設計物件是切面,而切面模組化橫切的關注點

在應用AOP程式設計時,仍需要定義公共的功能,但可以明確定義的功能在哪,以什麼方式應用,並且不必修改影響的類,這樣橫切關注點就被模組化到特殊的
物件(切面)裡

AOP的好處:
->每個事物邏輯位於一個位置,程式碼不分散,便於維護
->業務模組化更簡潔,只包含核心的業務程式碼

AOP術語:
切面(Aspect):橫切關注點被模組化的特殊物件
通知(Advice):切換必須完成的工作
目標(Target):被通知的物件
代理(Proxy):向目標物件應用的通知之後建立的物件
連線點(Joinpoint):程式執行的特定位置
切點(pointcut):每個類都有若干個連線點,
AOP即使通過定位到特定的連線點

 

每次做方法的時候,都在執行之前和之後都有相關的列印操作!
AtithmeticCalculator.java
public interface AtithmeticCalculator {
    int add(int i,int j);
    int sub(int i,int j);
    int mul(int i,int j);
    int div(int i,int j);
}

 AtithmeticCalculatorImp.java

介面中方法的實現。

@Component
public class AtithmeticCalculatorImp implements AtithmeticCalculator{
    
public int add(int i, int j) { int res = i + j; return res; } public int sub(int i, int j) { int res = i - j; return res; } public int mul(int i, int j) { int res = i * j; return res; } public int div(int i, int j) { int
res = i / j; return res; } }
LoggionAspect.java
切面類
//把這個類升級稱為一個切面:返給到IOC容器中---->在宣告為一個註解
@Order(2) @Aspect @Component public class LoggionAspect { @Before("execution(public * com.MrChengsc.AOP.AtithmeticCalculator.*(..))") public void beforeMethod(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); Object [] args = joinPoint.getArgs(); System.out.println("The method " + methodName + " begins with " + Arrays.asList(args)); } /** * 在方法執行之後執行的程式碼. 無論該方法是否出現異常 */ @After("execution(public * com.MrChengsc.AOP.AtithmeticCalculator.*(..))") public void afterMethod(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); System.out.println("The method " + methodName + " ends"); } /** * 在方法法正常結束受執行的程式碼 * 返回通知是可以訪問到方法的返回值的! */ @AfterReturning(value="execution(public * com.MrChengsc.AOP.AtithmeticCalculator.*(..))", returning="result") public void afterReturning(JoinPoint joinPoint, Object result){ String methodName = joinPoint.getSignature().getName(); System.out.println("The method " + methodName + " ends with " + result); } /** * 在目標方法出現異常時會執行的程式碼. * 可以訪問到異常物件; 且可以指定在出現特定異常時在執行通知程式碼 */ @AfterThrowing(value="execution(public * com.MrChengsc.AOP.AtithmeticCalculator.*(..))",throwing="e") public void afterThrowing(JoinPoint joinPoint, Exception e){ String methodName = joinPoint.getSignature().getName(); System.out.println("The method " + methodName + " occurs excetion:" + e); } //環繞通知 //需要攜帶ProceedingJoinPoint這個型別的引數 //環繞通知類似動態代理的全類名:ProceedingJoinPoint可以絕對是否執行目標方法 //必須要有返回值,返回值即為目標方法的返回值 // @Around("execution(public * com.MrChengsc.AOP.AtithmeticCalculator.*(..))") // public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){ // System.out.println("@Around"); // //返回值 // Object result = null; // //前置通知 // String methodName = proceedingJoinPoint.getSignature().getName(); // try { // //前置通知 // System.out.println("The methodName:" + methodName + " _The args :" + Arrays.asList(proceedingJoinPoint.getArgs())); // //執行目標方法 // result = proceedingJoinPoint.proceed(); // //後置通知 // System.out.println("End result:" + result); // } catch (Throwable e) { // //e.printStackTrace(); // //異常通知 // System.out.println("Exception:" + e); // } // //後置通知 // System.out.println("End..."); // //返回值 // return result; // } }
LoggionAspect2.java
切面類2
//把這個類升級稱為一個切面:返給到IOC容器中---->在宣告為一個註解
@Order(1)
@Aspect
@Component
public class LoggionAspect2 {
    
    //定義一個方法用於切點表示式
    //一般的該方法不需要填入其他的程式碼
    @Pointcut("execution(public * com.MrChengsc.AOP.AtithmeticCalculator.*(..))")
    public void declareJointPointExpression(){    }
    
@Before(
"declareJointPointExpression()") public void beforeMethod1(JoinPoint joinPoint){ System.out.println("--->beforeMethod1"); String methodName = joinPoint.getSignature().getName(); Object [] args = joinPoint.getArgs(); System.out.println("The method " + methodName + " begins with " + Arrays.asList(args)); System.out.println("--->beforeMethod1"); } /** * 在方法執行之後執行的程式碼. 無論該方法是否出現異常 */ @After("declareJointPointExpression()") public void afterMethod1(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); System.out.println("The method " + methodName + " ends"); } /** * 在方法法正常結束受執行的程式碼 * 返回通知是可以訪問到方法的返回值的! */ @AfterReturning(value="declareJointPointExpression())", returning="result") public void afterReturning1(JoinPoint joinPoint, Object result){ String methodName = joinPoint.getSignature().getName(); System.out.println("The method " + methodName + " ends with " + result); } /** * 在目標方法出現異常時會執行的程式碼. * 可以訪問到異常物件; 且可以指定在出現特定異常時在執行通知程式碼 */ @AfterThrowing(value="declareJointPointExpression()",throwing="e") public void afterThrowing1(JoinPoint joinPoint, Exception e){ String methodName = joinPoint.getSignature().getName(); System.out.println("The method " + methodName + " occurs excetion:" + e); } //環繞通知 //需要攜帶ProceedingJoinPoint這個型別的引數 //環繞通知類似動態代理的全類名:ProceedingJoinPoint可以絕對是否執行目標方法 //必須要有返回值,返回值即為目標方法的返回值 // @Around("execution(public * com.MrChengsc.AOP.AtithmeticCalculator.*(..))") // public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){ // System.out.println("@Around"); // //返回值 // Object result = null; // //前置通知 // String methodName = proceedingJoinPoint.getSignature().getName(); // try { // //前置通知 // System.out.println("The methodName:" + methodName + " _The args :" + Arrays.asList(proceedingJoinPoint.getArgs())); // //執行目標方法 // result = proceedingJoinPoint.proceed(); // //後置通知 // System.out.println("End result:" + result); // } catch (Throwable e) { // //e.printStackTrace(); // //異常通知 // System.out.println("Exception:" + e); // } // //後置通知 // System.out.println("End..."); // //返回值 // return result; // } }

 applicationContext.xml

<!-- 配置自動掃描的包 -->
<context:component-scan base-package="com.MrChengsc.AOP"></context:component-scan>
<!-- 前置通知,使切面裡的註解起作用 -->
<!-- 使AspectJ註解起作用:自動為匹配的類生成代理效果 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 main:

    public static void main(String[] args) {
//        AtithmeticCalculator atithmeticCalculator = null;
//        atithmeticCalculator = new AtithmeticCalculatorImp();
//        
//        atithmeticCalculator.add(1, 3);
//        System.out.println("---");
//        atithmeticCalculator.sub(3, 1);
//        System.out.println("---");
//        atithmeticCalculator.mul(1, 3);
//        System.out.println("---");
//        atithmeticCalculator.div(10, 2);
//        System.out.println("---");
        
        
        ApplicationContext ctx = new 
                ClassPathXmlApplicationContext("AOPContext.xml");
        //強制的型別使用介面的型別
        AtithmeticCalculator atithmeticCalculator = (AtithmeticCalculator) ctx.getBean(AtithmeticCalculator.class);
        
        int res = atithmeticCalculator.add(3, 6);
        System.out.println("res:" + res);
        System.out.println("-----");
//        int res1 = atithmeticCalculator.mul(2, 3);
//        System.out.println("res1:" + res1);
        
        //異常程式碼的測試
//        int res2 = atithmeticCalculator.div(10, 0);
//        System.out.println("res2:" + res2);
        
        
    }

 

執行的順序以及結果:

--->beforeMethod1
The method add begins with [3, 6]
--->beforeMethod1
The method add begins with [3, 6]
The method add ends
The method add ends with 9
The method add ends
The method add ends with 9
res:9
-----

 

注:

1.首先需要在xml檔案中進行包的掃描<context:component-scan>

2.使切面的註解起作用:<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

3.使用@Component在要進行處理的類、切面類上

4.使用@Aspect進行標註切面類

5.使用 @Before()在方法上作為前置通知

6. 使用@After()在方法上作為後置通知,無論該方法是否出現異常

7. 使用@AfterReturning(value="",returning=""),正常結束後那倒返回值

8.使用 @AfterThrowing(value="",throwing=""),在執行發生異常時,那倒異常資訊

9.使用@Around()作為環繞通知,使用比較少

10.使用@Pointcut()放在空方法上,作為切點表示式,引用切點直接使用方法名

11.使用@Order(1)放在類上指定切面的優先順序,數值越小優先順序越高

12.@After("execution(public * com.MrChengsc.AOP.AtithmeticCalculator.*(..))")

   *:代表所有,任意的

  ..:代表任意的形參

 

 

AOP的實現都有這個符號!