<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          細(xì)說SpringAOP的核心用法和原理解析

          共 16999字,需瀏覽 34分鐘

           ·

          2021-06-12 11:29


          汪偉俊 作者

          Java技術(shù)迷 | 出品

          初識AOP

          AOP:Aspect Oriented Programing,意為面向切面編程,對于習(xí)慣了傳統(tǒng)OOP思想的同學(xué)來說,AOP在一開始確實難以接受,但它能夠幫助我們在一些特定的業(yè)務(wù)上提供非常大的幫助。

          來看一個例子:

          public class Cacluate {

          public int add(int num1,int num2){
          return num1 + num2;
          }

          public int subtract(int num1,int num2){
          return num1 - num2;
          }

          public int multiply(int num1,int num2){
          return num1 * num2;
          }

          public int divide(int num1,int num2){
          return num1 / num2;
          }
          }

          這是一個做加減乘除運算的工具類,可以通過調(diào)用該類的方法計算兩個數(shù)的運算值:

          public static void main(String[] args) {
          Cacluate cacluate = new Cacluate();
          int addResult = cacluate.add(10,2);
          int subtractResult = cacluate.subtract(10,2);
          int multiplyResult = cacluate.multiply(10,2);
          int divideResult = cacluate.divide(10,2);
          System.out.println(addResult + "--" + subtractResult + "--" + multiplyResult + "--" + divideResult);
          }

          運行結(jié)果:

          12--8--20--5

          現(xiàn)在需求來了,若是想在每次做運算之前輸出一個日志信息,記錄當(dāng)前系統(tǒng)的運行情況,我們應(yīng)該如何實現(xiàn)呢?

          public class Cacluate {

          public int add(int num1,int num2){
          System.out.println("輸出日志信息,方法名:add,參數(shù):" + num1 + ";" + num2);
          return num1 + num2;
          }

          public int subtract(int num1,int num2){
          System.out.println("輸出日志信息,方法名:subtract,參數(shù):" + num1 + ";" + num2);
          return num1 - num2;
          }

          public int multiply(int num1,int num2){
          System.out.println("輸出日志信息,方法名:multiply,參數(shù):" + num1 + ";" + num2);
          return num1 * num2;
          }

          public int divide(int num1,int num2){
          System.out.println("輸出日志信息,方法名:divide,參數(shù):" + num1 + ";" + num2);
          return num1 / num2;
          }
          }

          這樣雖然實現(xiàn)了日志功能,但也能體會到這種方式的愚蠢,你需要在每個方法中添加相同重復(fù)的代碼,當(dāng)日志信息需要修改時,所有出現(xiàn)了日志的地方都需要修改,大大增加了維護(hù)成本。

          為此,AOP思想誕生了,它通過橫向抽取機(jī)制為這類無法通過縱向繼承體系進(jìn)行抽象的重復(fù)性代碼提供了解決方案,AOP將日志操作橫向抽取出來,再將日志操作融合到業(yè)務(wù)邏輯中實現(xiàn)功能。

          AOP的相關(guān)概念

          在具體講解AOP之前,我們需要來了解一下AOP的有關(guān)概念。

          JoinPoint——連接點

          何為連接點?連接點表示的是程序執(zhí)行的某個特定的位置,比如你要在項目中加入日志的操作,你可以將其設(shè)置在類加載前、類加載后、方法執(zhí)行前、方法執(zhí)行后,而Spring只支持方法的連接點,即你只能在方法執(zhí)行前后來設(shè)置你需要進(jìn)行的操作。

          PointCut——切點

          切點又是什么呢?我們知道,對于方法執(zhí)行前后的位置我們稱之為連接點,但是,我們需要將日志操作設(shè)置在哪些類的哪些方法執(zhí)行前后再執(zhí)行呢?這一定位稱為切點,通常需要通過切點表達(dá)式進(jìn)行過濾。

          Advice——通知

          通知,也叫增強(qiáng),當(dāng)我們通過切點表達(dá)式指定了需要切入的位置后,Spring就會在每個切點的位置增強(qiáng)該方法,例如添加上你的日志操作。

          Aspect——切面

          切面包括橫切邏輯的定義,也包括連接點的定義,Spring AOP將切面所定義的橫切邏輯切入到切面所指定的連接點中,即它的作用就是將切點和通知結(jié)合定位到連接點上。

          動態(tài)代理

          需要了解的是,Spring AOP使用動態(tài)代理在運行期切入增強(qiáng)的代碼,所以我們需要掌握動態(tài)代理的相關(guān)知識。

          動態(tài)代理是反射的高級應(yīng)用,JDK為我們提供了Proxy和InvocationHandler接口,通過該類和該接口生成代理對象實現(xiàn)方法的增強(qiáng)。

          看一個例子:

          public interface ICar {
          void use();
          }

          public class Car implements ICar {
          @Override
          public void use() {
          System.out.println("汽車使用汽油");
          }
          }

          定義一個接口和接口的實現(xiàn)類,表示使用汽車需要汽油,但是汽車并不一定只能使用汽油,還能使用電力驅(qū)動,為此,可以使用動態(tài)代理增強(qiáng)該use()方法:

          public class CarInvocatioHandler<T> implements InvocationHandler {

          private T t;

          public CarInvocatioHandler(T t){
          this.t = t;
          }

          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          //若執(zhí)行的是use()方法,則對該方法進(jìn)行增強(qiáng)
          if(method.getName().equals("use")){
          //調(diào)用原方法,保持原有的功能
          method.invoke(t,args);
          //增強(qiáng)方法
          System.out.println("汽車使用電力");
          }
          return null;
          }
          }

          首先需要實現(xiàn)InvocationHandler接口的invoke方法,并調(diào)用invoke()方法,保證原有的功能不被破壞,然后再編寫需要增強(qiáng)的邏輯,返回值是目標(biāo)代理方法的返回值,沒有就返回null即可,有了該接口的實現(xiàn)類后,接下來:

          public static void main(String[] args) {
          ICar car = new Car();
          CarInvocatioHandler invocatioHandler = new CarInvocatioHandler(car);
          ICar proxy = (ICar) Proxy.newProxyInstance(car.getClass().getClassLoader(), car.getClass().getInterfaces(), invocatioHandler);
          proxy.use();
          }

          通過Proxy類創(chuàng)建代理對象,并按照CarInvocatioHandler實現(xiàn)類的規(guī)則進(jìn)行增強(qiáng),運行結(jié)果:

          汽車使用汽油
          汽車使用電力

          我們將這一過程類比到剛才的場景中去,要對四種計算方法設(shè)置日志操作,只需要分別對這四個方法進(jìn)行動態(tài)代理,增強(qiáng)它們即可,然而動態(tài)代理有一個缺陷,就是只能針對接口做代理,所以我們需要對計算方法做一個處理:

          public interface CacluateDao {

          int add(int num1,int num2);
          int subtract(int num1,int num2);
          int multiply(int num1,int num2);
          int divide(int num1,int num2);
          }

          public class Cacluate implements CacluateDao{

          @Override
          public int add(int num1, int num2) {
          return num1 + num2;
          }

          @Override
          public int subtract(int num1, int num2) {
          return num1 - num2;
          }

          @Override
          public int multiply(int num1, int num2) {
          return num1 * num2;
          }

          @Override
          public int divide(int num1, int num2) {
          return num1 / num2;
          }
          }

          這樣我們就可以增強(qiáng)這些方法了:

          public class CacluateInvocationHandler<T> implements InvocationHandler {

          private T t;

          public CacluateInvocationHandler(T t){
          this.t = t;
          }

          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          int result = 0;
          if(method.getName().equals("add")){
          //增強(qiáng)方法
          System.out.println("輸出日志信息,方法名:" + method.getName() + ",參數(shù):" + Arrays.asList(args));
          //保持原有功能
          method.invoke(t,args);
          result = Integer.valueOf(args[0] + "") + Integer.valueOf(args[1] + "");
          }
          return result;
          }
          }

          然后編寫動態(tài)代理:

          public static void main(String[] args) {
          CacluateDao cacluate = new Cacluate();
          CacluateInvocationHandler invocationHandler = new CacluateInvocationHandler(cacluate);
          CacluateDao proxy = (CacluateDao) Proxy.newProxyInstance(cacluate.getClass().getClassLoader(), cacluate.getClass().getInterfaces(), invocationHandler);
          int result = proxy.add(3, 4);
          System.out.println(result);
          }

          運行結(jié)果:

          輸出日志信息,方法名:add,參數(shù):[3, 4]
          7

          其它方法的增強(qiáng)方式也是類似的,這里就不重復(fù)舉例了。

          剛才也說了,Java的動態(tài)代理只支持接口代理,所以是有一定的缺陷的,對此,Spring并不僅僅采用動態(tài)代理來實現(xiàn)方法的增強(qiáng),還使用了CGLib技術(shù),它可以為一個類創(chuàng)建子類,然后通過子類中的攔截方法攔截所有父類的方法并進(jìn)行增強(qiáng)。

          通知介紹

          Advice,確切地說它應(yīng)該被理解為增強(qiáng),前面也一直在強(qiáng)調(diào)方法的增強(qiáng),那么接下來我們來看看在Spring AOP中是如何去實現(xiàn)方法的增強(qiáng)的。

          前置通知

          public class Cacluate{

          public int add(int num1, int num2) {
          return num1 + num2;
          }

          public int subtract(int num1, int num2) {
          return num1 - num2;
          }

          public int multiply(int num1, int num2) {
          return num1 * num2;
          }

          public int divide(int num1, int num2) {
          return num1 / num2;
          }
          }

          首先我們從實現(xiàn)接口的束縛中脫離出來,然后使用Spring AOP進(jìn)行增強(qiáng),比如需要在計算方法之前輸出日志信息,你就可以這樣做:

          @Component
          @Aspect
          public class LoggingAspect {

          @Before("execution(* com.wwj.aop.util.*.*(..))")
          public void beforeMethod(JoinPoint joinPoint){
          String methodName = joinPoint.getSignature().getName();
          List<Object> list = Arrays.asList(joinPoint.getArgs());
          System.out.println("執(zhí)行在" + methodName + "方法前的日志信息,方法參數(shù)為:" + list);
          }
          }

          定義一個日志切面,并編寫一個方法,在該方法上添加@Before注解,然后編寫切面表達(dá)式,即指定該方法切入到哪個類的哪些方法,這里的execution(* com.wwj.aop.util.*.*(..))則表示將該方法切入到com.wwj.aop.util包下的所有類的所有方法,此時執(zhí)行計算方法:

          public static void main(String[] args) {
          ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
          Cacluate cacluate = (Cacluate) context.getBean("cacluate");
          int addResult = cacluate.add(3, 5);
          System.out.println(addResult);
          }

          運行結(jié)果:

          執(zhí)行在add方法前的日志信息,方法參數(shù)為:[3, 5]
          8

          后置通知

          學(xué)會了前置通知,那么后面的內(nèi)容就會非常簡單了,比如后置通知,只是簡單地修改一下注解名就可以了:

          @After("execution(* com.wwj.aop.util.*.*(..))")
          public void afterMethod(JoinPoint joinPoint){
          String methodName = joinPoint.getSignature().getName();
          List<Object> list = Arrays.asList(joinPoint.getArgs());
          System.out.println("執(zhí)行在" + methodName + "方法后的日志信息,方法參數(shù)為:" + list);
          }

          執(zhí)行測試代碼:

          執(zhí)行在divide方法前的日志信息,方法參數(shù)為:[10, 2]
          執(zhí)行在divide方法后的日志信息,方法參數(shù)為:[10, 2]
          5

          需要注意的是后置通知不管程序是否發(fā)生錯誤都會被執(zhí)行,比如:

          public static void main(String[] args) {
          ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
          Cacluate cacluate = (Cacluate) context.getBean("cacluate");
          int addResult = cacluate.divide(10,0);
          System.out.println(addResult);
          }

          在數(shù)學(xué)中,0作為除數(shù)是不被允許的,程序在進(jìn)行計算時肯定會產(chǎn)生異常,然而此時后置通知仍然會被執(zhí)行:

          執(zhí)行在divide方法前的日志信息,方法參數(shù)為:[10, 0]
          執(zhí)行在divide方法后的日志信息,方法參數(shù)為:[10, 0]
          Exception in thread "main" java.lang.ArithmeticException: / by zero

          返回通知

          返回通知與后置通知類似,區(qū)別在于,返回通知需要在程序正確執(zhí)行后才會執(zhí)行,若程序發(fā)生異常,則返回通知不會執(zhí)行:

          @AfterReturning(value = "execution(* com.wwj.aop.util.*.*(..))",returning = "result")
          public void afterReturningMethod(JoinPoint joinPoint,Object result){
          String methodName = joinPoint.getSignature().getName();
          List<Object> list = Arrays.asList(joinPoint.getArgs());
          System.out.println("執(zhí)行在" + methodName + "方法后的日志信息,方法參數(shù)為:" + list + ",運行結(jié)果為:" + result);
          }

          運行結(jié)果:

          執(zhí)行在divide方法前的日志信息,方法參數(shù)為:[10, 5]
          執(zhí)行在divide方法后的日志信息,方法參數(shù)為:[10, 5]
          執(zhí)行在divide方法后的日志信息,方法參數(shù)為:[10, 5],運行結(jié)果為:2
          2

          返回通知是能夠獲取到方法的執(zhí)行結(jié)果的,具體做法是在@AfterReturning中指定returning屬性值,然后在方法的入?yún)⒅卸x一個與其相同的變量即可。

          異常通知

          異常通知,顧名思義,只有當(dāng)程序發(fā)生異常時才會執(zhí)行,異常通知能夠獲取到方法發(fā)生了什么異常:

          @AfterThrowing(value = "execution(* com.wwj.aop.util.*.*(..))",throwing = "except")
          public void afterThrowingMethod(JoinPoint joinPoint,Exception except){
          String methodName = joinPoint.getSignature().getName();
          List<Object> list = Arrays.asList(joinPoint.getArgs());
          System.out.println("執(zhí)行在" + methodName + "方法后的日志信息,方法參數(shù)為:" + list + ",異常信息為:" + except);
          }

          獲取異常信息的方式與返回通知獲取結(jié)果值的方式是一樣的,看運行結(jié)果:

          執(zhí)行在divide方法前的日志信息,方法參數(shù)為:[10, 0]
          執(zhí)行在divide方法后的日志信息,方法參數(shù)為:[10, 0]
          執(zhí)行在divide方法后的日志信息,方法參數(shù)為:[10, 0],異常信息為:java.lang.ArithmeticException: / by zero

          環(huán)繞通知

          環(huán)繞通知的功能比較強(qiáng)大,它能夠通過一個方法實現(xiàn)之前的所有通知效果,直接看代碼:

          @Around("execution(* com.wwj.aop.util.*.*(..))")
          public Object aroundMethod(ProceedingJoinPoint joinPoint){
          Object result = null;
          String methodName = joinPoint.getSignature().getName();
          List<Object> args = Arrays.asList(joinPoint.getArgs());
          try{
          //前置通知
          System.out.println("執(zhí)行在" + methodName + "方法前的日志信息,方法參數(shù)為:" + args);
          //執(zhí)行目標(biāo)方法,保持原有功能
          result = joinPoint.proceed();
          //返回通知
          System.out.println("執(zhí)行在" + methodName + "方法后的日志信息,方法參數(shù)為:" + args + ",運行結(jié)果為:" + result);
          }catch (Throwable e){
          //異常通知
          System.out.println("執(zhí)行在" + methodName + "方法后的日志信息,方法參數(shù)為:" + args + ",異常信息為:" + e);
          }
          //后置通知
          System.out.println("執(zhí)行在" + methodName + "方法后的日志信息,方法參數(shù)為:" + args);
          return result;
          }

          運行結(jié)果:

          執(zhí)行在divide方法前的日志信息,方法參數(shù)為:[10, 2]
          執(zhí)行在divide方法后的日志信息,方法參數(shù)為:[10, 2],運行結(jié)果為:5
          執(zhí)行在divide方法后的日志信息,方法參數(shù)為:[10, 2]
          5

          從環(huán)繞通知應(yīng)該不難理解,為什么后置通知無論什么情況都會執(zhí)行,且只有返回通知能夠獲取到方法執(zhí)行結(jié)果,異常通知如何能夠獲取到異常信息,一目了然。

          需要注意,環(huán)繞通知必須攜帶ProceedingJoinPoint參數(shù)并且必須有返回值。

          xml配置實現(xiàn)AOP

          前面介紹了五種通知的用途和注意事項,順帶著還貼出了注解實現(xiàn)這五種通知的方法,在Spring中,通知還能通過xml文件進(jìn)行配置實現(xiàn),下面來看一看。

          先來看看前置通知的配置,在這之前,先將注解全部去除:

          @Component
          public class LoggingAspect {

          public void beforeMethod(JoinPoint joinPoint){
          String methodName = joinPoint.getSignature().getName();
          List<Object> list = Arrays.asList(joinPoint.getArgs());
          System.out.println("執(zhí)行在" + methodName + "方法前的日志信息,方法參數(shù)為:" + list);
          }

          public void afterMethod(JoinPoint joinPoint){
          String methodName = joinPoint.getSignature().getName();
          List<Object> list = Arrays.asList(joinPoint.getArgs());
          System.out.println("執(zhí)行在" + methodName + "方法后的日志信息,方法參數(shù)為:" + list);
          }

          public void afterReturningMethod(JoinPoint joinPoint,Object result){
          String methodName = joinPoint.getSignature().getName();
          List<Object> list = Arrays.asList(joinPoint.getArgs());
          System.out.println("執(zhí)行在" + methodName + "方法后的日志信息,方法參數(shù)為:" + list + ",運行結(jié)果為:" + result);
          }

          public void afterThrowingMethod(JoinPoint joinPoint,Exception except){
          String methodName = joinPoint.getSignature().getName();
          List<Object> list = Arrays.asList(joinPoint.getArgs());
          System.out.println("執(zhí)行在" + methodName + "方法后的日志信息,方法參數(shù)為:" + list + ",異常信息為:" + except);
          }
          }

          然后進(jìn)行配置:

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

          <context:component-scan base-package="com.wwj"/>

          <aop:config proxy-target-class="true">
          <!-- 聲明切點表達(dá)式 -->
          <aop:pointcut id="pointCut" expression="execution(* com.wwj.aop.util.*.*(..))"/>
          <!-- 配置切面,指定loggingAspect為需要切入的邏輯 -->
          <aop:aspect ref="loggingAspect">
          <!-- 前置通知,指定beforeMethod為前置通知,并指定將其切入到pointCut中 -->
          <aop:before method="beforeMethod" pointcut-ref="pointCut"/>
          </aop:aspect>
          </aop:config>
          </beans>

          這樣前置通知就完成了,運行結(jié)果:

          執(zhí)行在divide方法前的日志信息,方法參數(shù)為:[10, 2]
          5

          接下來配置后置通知:

          <aop:config proxy-target-class="true">
          <!-- 聲明切點表達(dá)式 -->
          <aop:pointcut id="pointCut" expression="execution(* com.wwj.aop.util.*.*(..))"/>
          <!-- 配置切面,指定loggingAspect為需要切入的邏輯 -->
          <aop:aspect ref="loggingAspect">
          <!-- 前置通知,指定beforeMethod為前置通知,并指定將其切入到pointCut中 -->
          <aop:before method="beforeMethod" pointcut-ref="pointCut"/>
          <!-- 后置通知 -->
          <aop:after method="afterMethod" pointcut-ref="pointCut"/>
          </aop:aspect>
          </aop:config>

          運行結(jié)果:

          執(zhí)行在add方法前的日志信息,方法參數(shù)為:[10, 2]
          執(zhí)行在add方法后的日志信息,方法參數(shù)為:[10, 2]
          12

          然后是返回通知、異常通知,由于配置方式與其相同,這里就直接一起配置了:

          <aop:config proxy-target-class="true">
          <!-- 聲明切點表達(dá)式 -->
          <aop:pointcut id="pointCut" expression="execution(* com.wwj.aop.util.*.*(..))"/>
          <!-- 配置切面,指定loggingAspect為需要切入的邏輯 -->
          <aop:aspect ref="loggingAspect">
          <!-- 前置通知,指定beforeMethod為前置通知,并指定將其切入到pointCut中 -->
          <aop:before method="beforeMethod" pointcut-ref="pointCut"/>
          <!-- 后置通知 -->
          <aop:after method="afterMethod" pointcut-ref="pointCut"/>
          <!-- 返回通知 -->
          <aop:after-returning method="afterReturningMethod" pointcut-ref="pointCut" returning="result"/>
          <!-- 異常通知 -->
          <aop:after-throwing method="afterThrowingMethod" pointcut-ref="pointCut" throwing="except"/>
          </aop:aspect>
          </aop:config>

          其實用法和注解配置是完全一樣的。

          運行結(jié)果:

          執(zhí)行在add方法前的日志信息,方法參數(shù)為:[10, 2]
          執(zhí)行在add方法后的日志信息,方法參數(shù)為:[10, 2]
          執(zhí)行在add方法后的日志信息,方法參數(shù)為:[10, 2],運行結(jié)果為:12
          12

          切面優(yōu)先級

          這里再強(qiáng)調(diào)一個切面優(yōu)先級的問題,比如現(xiàn)在又需要在項目中添加一個輸出當(dāng)前日期的操作,我們就需要再編寫一個切面類:

          @Component
          public class TimeAspect {

          public void beforeMethod(JoinPoint joinPoint){
          String methodName = joinPoint.getSignature().getName();
          System.out.println(methodName + "當(dāng)前執(zhí)行時間為:" + LocalDateTime.now());
          }
          }

          接下來配置一下:

          <aop:config proxy-target-class="true">
          <aop:pointcut id="pointCut" expression="execution(* com.wwj.aop.util.*.*(..))"/>
          <aop:aspect ref="loggingAspect">
          <aop:before method="beforeMethod" pointcut-ref="pointCut"/>
          <aop:after method="afterMethod" pointcut-ref="pointCut"/>
          <aop:after-returning method="afterReturningMethod" pointcut-ref="pointCut" returning="result"/>
          <aop:after-throwing method="afterThrowingMethod" pointcut-ref="pointCut" throwing="except"/>
          </aop:aspect>
          <!-- 配置時間切面 -->
          <aop:aspect ref="timeAspect">
          <!-- 前置通知 -->
          <aop:before method="beforeMethod" pointcut-ref="pointCut"/>
          </aop:aspect>
          </aop:config>

          你也可以使用注解進(jìn)行配置,這里就以xml配置方式舉例了,執(zhí)行以下測試方法:

          執(zhí)行在add方法前的日志信息,方法參數(shù)為:[10, 2]
          add當(dāng)前執(zhí)行時間為:2020-10-03T17:54:08.297
          執(zhí)行在add方法后的日志信息,方法參數(shù)為:[10, 2]
          執(zhí)行在add方法后的日志信息,方法參數(shù)為:[10, 2],運行結(jié)果為:12
          12

          需求來了,我若是想讓時間操作總是在日志操作執(zhí)行被執(zhí)行,該怎么辦呢?

          為此,Spring為我們提供了切面的優(yōu)先級,能夠非常靈活地解決切面的執(zhí)行順序問題,比如這里的需求,就可以這樣配置:

          <aop:config proxy-target-class="true">
          <!-- 聲明切點表達(dá)式 -->
          <aop:pointcut id="pointCut" expression="execution(* com.wwj.aop.util.*.*(..))"/>
          <!-- 配置日志切面,指定loggingAspect為需要切入的邏輯 -->
          <aop:aspect ref="loggingAspect" order="2">
          <!-- 前置通知,指定beforeMethod為前置通知,并指定將其切入到pointCut中 -->
          <aop:before method="beforeMethod" pointcut-ref="pointCut"/>
          <!-- 后置通知 -->
          <aop:after method="afterMethod" pointcut-ref="pointCut"/>
          <!-- 返回通知 -->
          <aop:after-returning method="afterReturningMethod" pointcut-ref="pointCut" returning="result"/>
          <!-- 異常通知 -->
          <aop:after-throwing method="afterThrowingMethod" pointcut-ref="pointCut" throwing="except"/>
          </aop:aspect>
          <!-- 配置時間切面 -->
          <aop:aspect ref="timeAspect" order="1">
          <!-- 前置通知 -->
          <aop:before method="beforeMethod" pointcut-ref="pointCut"/>
          </aop:aspect>
          </aop:config>

          這里將日志切面的order設(shè)置為2,將時間切面的order設(shè)置為1,因為order總是值越小,優(yōu)先級越高,所以時間切面的優(yōu)先級就會高于日志切面,看運行結(jié)果:

          add當(dāng)前執(zhí)行時間為:2020-10-03T17:57:36.779
          執(zhí)行在add方法前的日志信息,方法參數(shù)為:[10, 2]
          執(zhí)行在add方法后的日志信息,方法參數(shù)為:[10, 2]
          執(zhí)行在add方法后的日志信息,方法參數(shù)為:[10, 2],運行結(jié)果為:12
          12

          事實證明確實如此。

          而對于注解實現(xiàn),order是這樣配置的:

          @Component
          @Aspect
          @Order(1)
          public class TimeAspect {

          @Before("execution(* com.wwj.aop.util.*.*(..))")
          public void beforeMethod(JoinPoint joinPoint){
          String methodName = joinPoint.getSignature().getName();
          System.out.println(methodName + "當(dāng)前執(zhí)行時間為:" + LocalDateTime.now());
          }
          }

          直接通過@Order注解并設(shè)置value屬性值即可。

          本文作者:汪偉俊 為Java技術(shù)迷專欄作者 投稿,未經(jīng)允許請勿轉(zhuǎn)載。


          1、Intellij IDEA這樣 配置注釋模板,讓你瞬間高出一個逼格!
          2、基于SpringBoot的迷你商城系統(tǒng),附源碼!
          3、最牛逼的 Java 日志框架,性能無敵,橫掃所有對手!
          4、把Redis當(dāng)作隊列來用,真的合適嗎?
          5、驚呆了,Spring Boot居然這么耗內(nèi)存!你知道嗎?
          6、全網(wǎng)最全 Java 日志框架適配方案!還有誰不會?
          7、Spring中毒太深,離開Spring我居然連最基本的接口都不會寫了

          點分享

          點收藏

          點點贊

          點在看

          瀏覽 75
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲视频综合 | 狠狠人妻久久 | 欧洲亚洲无码视频 | 成人无码日日夜夜 | 午夜精品一区二区三区免费视频 |