1. 程式人生 > >Spring學習筆記(十一)AOP的註解方式cglib代理

Spring學習筆記(十一)AOP的註解方式cglib代理

JDK動態代理與CGLib動態代理均是實現Spring AOP的基礎,切點,切面,如何定義切點,前置、後置、放回、異常、環繞通知

1.切點、切面

  1. 紅色的地方就是切面,增加額外的功能
  2. 連線點+增加功能的位置 = 切點

2.專案結構

3.jar包

<dependencies>
   <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>
4.11</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version
>
4.3.6.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.3.6.RELEASE</version
>
</dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.3.6.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.6.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context-support --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.3.6.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.6.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-expression --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>4.3.6.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-tx --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>4.3.6.RELEASE</version> </dependency> <!--新增的jar--> <!-- https://mvnrepository.com/artifact/aopalliance/aopalliance --> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.13</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.6</version> </dependency> </dependencies>

4.核心配置檔案beans.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:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
      http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd">
   <!--1.啟動Spring註解-->
   <context:annotation-config></context:annotation-config>
   <!--2.掃描-->
   <context:component-scan base-package="com.hs"/>
   <!--Spring提供了多種AOP的實現方式,但是我們只用第三方的AOP標準方式-->
   <!--3.啟動AOP註解,false是使用預設的java代理,true是使用CGLIB代理-->
   <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>

5.核心程式碼

package com.hs.service;

/**
 * 對外提供的核心業務,完成了加法/減法/乘法運算
 */
public interface ArithmeticService {
   int add(int x, int y);

   int sub(int x, int y);

   int mul(int x, int y);

   int div(int x, int y);
}
package com.hs.service.impl;

import com.hs.service.ArithmeticService;
import org.springframework.stereotype.Service;

/**
 * 核心程式碼
 */
@Service
public class ArithmeticServiceImpl implements ArithmeticService {
   @Override
   public int add(int x, int y) {
      //核心程式碼
      int result = x + y;
      return result;
   }

   @Override
   public int sub(int x, int y) {
      int result = x - y;
      return result;
   }

   @Override
   public int mul(int x, int y) {
      int result = x * y;
      return result;
   }

   @Override
   public int div(int x, int y) {
      int result = x / y;
      return result;
   }
}

6.定義切面

package com.hs.service.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Aspect //標記這是切面
public class Logger {

   //在你執行方法之前執行,並且監控的是介面的執行Add方法,value可以省略
   @Before(value = "execution(int com.hs.service.ArithmeticService.add(int ,int))")
   //這句話的意思,
   //執行返回型別為int,com.hs.service.ArithmeticService介面下的add方法,傳遞的兩個引數型別為int,int
   //簡單理解為,接口裡寫的方法,只是少了變數值   int add(int x, int y);
   public void test01() {
      System.out.println("@Before在程式之前執行");
   }
}

測試

package com.hs.test;

import com.hs.service.ArithmeticService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ArithmeticServiceTest {
   private ArithmeticService arithmeticService;
   private ApplicationContext ac;

   @Before
   public void init() {
      this.ac = new ClassPathXmlApplicationContext("beans.xml");
      this.arithmeticService = this.ac.getBean("arithmeticServiceImpl", ArithmeticService.class);
   }

   @Test
   public void testAddMethod() {
      int result = this.arithmeticService.add(2,1);
      System.out.println("執行的結果:"+ result);
   }
}

控制檯輸出:

@Before在程式之前執行
執行的結果:3

7.如何定義註解切點說明

1.使用邏輯運算子

@Before(value = "execution(int com.hs.service.ArithmeticService.add(int ,int)) or execution(int com.hs.service.ArithmeticService.sub(int ,int))")
public void test02() {
   System.out.println("我可以使用邏輯運算子  &&(and) ||(or)");
}

2.使用萬用字元——推薦

/**
 * 比較推薦
 * 在execution表示式中可以使用萬用字元,但是請注意任意的表示式是 ..
 * 第一個*位置:代表返回任意的資料型別
 * ..表示任意的意思,而不是我們之前學習的**
 * 第二個* 監控的以Service結尾的類(介面)
 * 第三個* 監控介面或者類下的所有的方法
 * ..標識任意個引數型別或者引數的個數
 */
@Before(value = "execution(* com.hs..*Service.*(..))")
public void test03() {
   System.out.println("我可以使用萬用字元");
}

3.如何定義切點

//如何定義切點(可以隨意組合)
@Pointcut(value = "execution(int com.hs.service.ArithmeticService.add(int ,int))")
public void addPointCut() { }   //切點的名稱是“addPointCut()”
@Pointcut(value = "execution(int com.hs.service.ArithmeticService.sub(int ,int))")
public void subPointCut() { }   //切點的名稱是“subPointCut()”
@Pointcut(value = "execution(int com.hs.service.ArithmeticService.mul(int ,int))")
public void mulPointCut() { }   //切點的名稱是“mulPointCut()”
@Pointcut(value = "addPointCut() || mulPointCut()")
public void addAndMulPointCut() { } //切點的名稱是“addAndMulPointCut()”
@Pointcut(value = "execution(* com.hs..service.*Service.*(..))")
    public void allMethodPointCut(){}

//@Before   叫前置通知,也叫前置增強
@Before(value = "addAndMulPointCut()")
public void test04() {
   System.out.println("如何定義切點");
}

8.前置通知:不管程式是否正確都會執行

//1.前置通知(增強)特點:不管程式是否正確都會執行
    @Before(value = "allMethodPointCut()")
    public void test05(JoinPoint jp) {
        System.out.println("==前置通知==");
        //獲取get
        System.out.println(Arrays.asList(jp.getArgs()));
        System.out.println(jp.getSignature().getName());
        System.out.println(jp.getTarget().getClass().getName());
        System.out.println("==前置通知==");

    }

9.後置通知:不管程式是否正確都會執行

//2.後置通知(增強)特點:不管程式是否正確都會執行
@After(value = "allMethodPointCut()")
public void test06(JoinPoint jp) {
   //獲取get
   System.out.println("==後置增強==");
   System.out.println(Arrays.asList(jp.getArgs()));
   System.out.println(jp.getSignature().getName());
   System.out.println(jp.getTarget().getClass().getName());
   System.out.println("==後置增強==");

}

10.返回通知:只有程式是正確的時候才會執行,並且可以獲取執行後的資料

//3.返回通知(增強):只有程式是正確的時候才會執行,並且可以獲取執行後的資料
@AfterReturning(value = "allMethodPointCut()",returning = "hs")
public void test07(JoinPoint jp,int hs) {
   System.out.println("==返回通知==");
   System.out.println("程式正常執行,正確的執行結果為==>"+hs);
   System.out.println("==返回通知==");
}

11.異常通知:只有程式是錯誤的時候才會執行

//4.異常通知(增強):只有程式是錯誤的時候才會執行,
@AfterThrowing(value = "allMethodPointCut()",throwing = "hs")
public void test08(JoinPoint jp,ArithmeticException hs) {
   System.out.println("==異常通知==");
   System.out.println("程式執行錯誤==>"+hs);
   System.out.println("==異常通知==");
}

12.環繞通知

//5.環繞通知
@Around("allMethodPointCut()")
public Object test09(ProceedingJoinPoint pjp) {
   System.out.println("寫某個功能,前置通知");
   Object result = null;
   try {
      result = pjp.proceed(); //執行程式返回執行的結果
      System.out.println("寫某個功能,返回通知");
   } catch (Throwable throwable) {
      throwable.printStackTrace();
      System.out.println("寫某個功能,異常通知");
   }
   System.out.println("寫某個功能,後置通知");
   return result;
}

測試

package com.hs.test;

import com.hs.service.ArithmeticService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ArithmeticServiceTest {
   private ArithmeticService arithmeticService;
   private ApplicationContext ac;

   @Before
   public void init() {
      this.ac = new ClassPathXmlApplicationContext("beans.xml");
      this.arithmeticService = this.ac.getBean("arithmeticServiceImpl", ArithmeticService.class);
   }

   @Test
   public void testAddMethod() {
      int result = this.arithmeticService.add(2,1);
      System.out.println("執行的結果:"+ result);
   }
}