最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringAop日志找不到方法的處理

 更新時(shí)間:2021年06月21日 16:14:44   作者:BackgroundHero  
這篇文章主要介紹了SpringAop日志找不到方法的處理方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

SpringAop日志找不到方法

錯(cuò)誤截圖:

在這里插入圖片描述

顯示沒有找到該方法,于是我找到對(duì)應(yīng)的類和對(duì)應(yīng)的方法:

在這里插入圖片描述

這里我用了反射來獲取方法名和參數(shù):

在這里插入圖片描述

錯(cuò)誤打印的結(jié)果顯示方法名獲取沒有錯(cuò)誤,于是我查看參數(shù)的類型是否有錯(cuò)

結(jié)果一個(gè)都對(duì)不上…

int類型反射得到的class:

在這里插入圖片描述

在這里插入圖片描述

Integer反射得到的Class:

在這里插入圖片描述

在這里插入圖片描述

…終于知道之前錯(cuò)誤里的Ljavexxxx是哪里來的了…

由于model是一個(gè)接口

在這里插入圖片描述

model反射的Class得到的是他的子類org.springframework.validation.support.BindingAwareModelMap:

在這里插入圖片描述

所以參數(shù)類型對(duì)不上號(hào)導(dǎo)致了錯(cuò)誤的產(chǎn)生

解決方法:

將int類型的參數(shù)改為Integer(以后都寫Integer,舍棄int!),將參數(shù)里的Model刪去,將方法返回值改為ModeAndView,一樣實(shí)現(xiàn)頁面的跳轉(zhuǎn)和參數(shù)的傳遞:

在這里插入圖片描述

運(yùn)行代碼:

在這里插入圖片描述

運(yùn)行okkkk~

SpringBoot用SpringAOP實(shí)現(xiàn)日志記錄功能

背景:

我需要在一個(gè)SpringBoot的項(xiàng)目中的每個(gè)controller加入一個(gè)日志記錄,記錄關(guān)于請(qǐng)求的一些信息。

代碼類似于:

logger.info(request.getRequestUrl());

之類的。

代碼不難,但由于Controller的數(shù)量不少,干起來也是體力活。所以想到了用Spring AOP來解決這個(gè)問題。

首先,在pom中加入SpringAOP的相關(guān)依賴:

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

上一篇我們說到,如果要直接用@Aspect注解的話,要在spring的配置文件中加入

<aop:aspectj-autoproxy />

那么我們這里要不要在程序的主類中增加@EnableAspectJAutoProxy來啟用呢? 實(shí)際并不需要,可以看下面關(guān)于AOP的默認(rèn)配置屬性,其中spring.aop.auto屬性默認(rèn)是開啟的,也就是說只要引入了AOP依賴后,默認(rèn)已經(jīng)增加了

@EnableAspectJAutoProxy

好的也就是說,只要引入SpringAOP相關(guān)的jar包依賴,我們就可以開始相關(guān)的Aspet的編程了。

這里直接上代碼,然后再做解釋:

首先是包結(jié)構(gòu)的圖:

這里涉及到接收請(qǐng)求的Controller的包有兩個(gè),com.stuPayment.controller還有com.stuPayment.uiController

然后看我們的切面類WebLogAspect類的代碼:

package com.stuPayment.util;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Aspect
@Component
public class WebLogAspect {    
    private final Logger logger = LoggerFactory.getLogger(WebLogAspect.class);    
    @Pointcut("execution(public * com.stuPayment.controller..*.*(..))")//切入點(diǎn)描述 這個(gè)是controller包的切入點(diǎn)
    public void controllerLog(){}//簽名,可以理解成這個(gè)切入點(diǎn)的一個(gè)名稱
    
    @Pointcut("execution(public * com.stuPayment.uiController..*.*(..))")//切入點(diǎn)描述,這個(gè)是uiController包的切入點(diǎn)
    public void uiControllerLog(){}
    
    @Before("controllerLog() || uiControllerLog()") //在切入點(diǎn)的方法run之前要干的
    public void logBeforeController(JoinPoint joinPoint) {        
        
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();//這個(gè)RequestContextHolder是Springmvc提供來獲得請(qǐng)求的東西
        HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
        
         // 記錄下請(qǐng)求內(nèi)容
        logger.info("################URL : " + request.getRequestURL().toString());
        logger.info("################HTTP_METHOD : " + request.getMethod());
        logger.info("################IP : " + request.getRemoteAddr());
        logger.info("################THE ARGS OF THE CONTROLLER : " + Arrays.toString(joinPoint.getArgs()));
        
        //下面這個(gè)getSignature().getDeclaringTypeName()是獲取包+類名的   然后后面的joinPoint.getSignature.getName()獲取了方法名
        logger.info("################CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        //logger.info("################TARGET: " + joinPoint.getTarget());//返回的是需要加強(qiáng)的目標(biāo)類的對(duì)象
        //logger.info("################THIS: " + joinPoint.getThis());//返回的是經(jīng)過加強(qiáng)后的代理類的對(duì)象
    }   
}

針對(duì)這個(gè)切面類,來展開說明@Aspect切面類的編程。

@Aspect和@Component

首先,這個(gè)@Aspect注釋告訴Spring這是個(gè)切面類,然后@Compoment將轉(zhuǎn)換成Spring容器中的bean或者是代理bean。 總之要寫切面這兩個(gè)注解一起用就是了。

既然是切面類,那么肯定是包含PointCut還有Advice兩個(gè)要素的,下面對(duì)幾個(gè)注解展開講來看看在@Aspect中是怎么確定切入點(diǎn)(PointCut)和增強(qiáng)通知(Advice)的。

@PointCut

這個(gè)注解包含兩部分,PointCut表達(dá)式和PointCut簽名。表達(dá)式是拿來確定切入點(diǎn)的位置的,說白了就是通過一些規(guī)則來確定,哪些方法是要增強(qiáng)的,也就是要攔截哪些方法。

@PointCut(...........)括號(hào)里面那些就是表達(dá)式。這里的execution是其中的一種匹配方式,還有:

execution: 匹配連接點(diǎn)

within: 某個(gè)類里面

this: 指定AOP代理類的類型

target:指定目標(biāo)對(duì)象的類型

args: 指定參數(shù)的類型

bean:指定特定的bean名稱,可以使用通配符(Spring自帶的)

@target: 帶有指定注解的類型

@args: 指定運(yùn)行時(shí)傳的參數(shù)帶有指定的注解

@within: 匹配使用指定注解的類

@annotation:指定方法所應(yīng)用的注解

注意,由于是動(dòng)態(tài)代理的實(shí)現(xiàn)方法,所以不是所有的方法都能攔截得下來,對(duì)于JDK代理只有public的方法才能攔截得下來,對(duì)于CGLIB只有public和protected的方法才能攔截。

這里我們主要介紹execution的匹配方法,因?yàn)榇蠖鄶?shù)時(shí)候都會(huì)用這個(gè)來定義pointcut:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
            throws-pattern?)

execution(方法修飾符(可選) 返回類型 類路徑 方法名 參數(shù) 異常模式(可選))

除了返回類型,方法名還有參數(shù)之外,其他都是可選的

ret-type-pattern:可以為*表示任何返回值,全路徑的類名等.

name-pattern:指定方法名,*代表所以,set*,代表以set開頭的所有方法.

parameters pattern:指定方法參數(shù)(聲明的類型), ()匹配沒有參數(shù); (..)代表任意多個(gè)參數(shù); (*)代表一個(gè)參數(shù),但可以是任意型; (*,String)代表第一個(gè)參數(shù)為任何值,第二個(gè)為String類型。

下面給幾個(gè)例子:

1)execution(public * *(..))——表示匹配所有public方法

2)execution(* set*(..))——表示所有以“set”開頭的方法

3)execution(* com.xyz.service.AccountService.*(..))——表示匹配所有AccountService接口的方法

4)execution(* com.xyz.service.*.*(..))——表示匹配service包下所有的方法

5)execution(* com.xyz.service..*.*(..))——表示匹配service包和它的子包下的方法

然后其他的匹配法要用的時(shí)候再百度吧~

然后是@PointCut的第二個(gè)部分,簽名signature,也就是代碼中的

@Pointcut("execution(public * com.stuPayment.uiController..*.*(..))")//切入點(diǎn)描述,這個(gè)是uiController包的切入點(diǎn)
    public void uiControllerLog(){}

像方法定義的這個(gè)Public void uiControllerLog(){}這個(gè)看起來像是方法定義的東西,就是簽名,簽名沒有實(shí)際用處,只是用來標(biāo)記一個(gè)Pointcut,可以理解成這個(gè)切入點(diǎn)的一個(gè)記號(hào)。

@Before

這個(gè)是決定advice在切入點(diǎn)方法的什么地方執(zhí)行的標(biāo)簽,這個(gè)注解的意思是在切入點(diǎn)方法執(zhí)行之前執(zhí)行我們定義的advice。

@Before("controllerLog() || uiControllerLog()") //在切入點(diǎn)的方法run之前要干的
    public void logBeforeController(JoinPoint joinPoint) {

@Before注解括號(hào)里面寫的是一個(gè)切入點(diǎn),這里看見切入點(diǎn)表達(dá)式可以用邏輯符號(hào)&&,||,!來描述。 括號(hào)里面也可以內(nèi)置切點(diǎn)表達(dá)式,也就是直接寫成:

@Before("execution(public * com.stuPayment.uiController..*.*(..))")

跟寫成@Before("uiControllerLog()")的效果是一樣的。

然后看到注解下面的方法,就是描述advice的,我們看到有個(gè)參數(shù)JoinPoint,這個(gè)東西代表著織入增強(qiáng)處理的連接點(diǎn)。JoinPoint包含了幾個(gè)很有用的參數(shù):

  • Object[] getArgs:返回目標(biāo)方法的參數(shù)
  • Signature getSignature:返回目標(biāo)方法的簽名
  • Object getTarget:返回被織入增強(qiáng)處理的目標(biāo)對(duì)象
  • Object getThis:返回AOP框架為目標(biāo)對(duì)象生成的代理對(duì)象

除了注解@Around的方法外,其他都可以加這個(gè)JoinPoint作參數(shù)。@Around注解的方法的參數(shù)一定要是ProceedingJoinPoint,下面會(huì)介紹。

@After

這個(gè)注解就是在切入的方法運(yùn)行完之后把我們的advice增強(qiáng)加進(jìn)去。一樣方法中可以添加JoinPoint。

@Around

這個(gè)注解可以簡(jiǎn)單地看作@Before和@After的結(jié)合。這個(gè)注解和其他的比比較特別,它的方法的參數(shù)一定要是ProceedingJoinPoint,這個(gè)對(duì)象是JoinPoint的子類。我們可以把這個(gè)看作是切入點(diǎn)的那個(gè)方法的替身,這個(gè)proceedingJoinPoint有個(gè)proceed()方法,相當(dāng)于就是那切入點(diǎn)的那個(gè)方法執(zhí)行,簡(jiǎn)單地說就是讓目標(biāo)方法執(zhí)行,然后這個(gè)方法會(huì)返回一個(gè)對(duì)象,這個(gè)對(duì)象就是那個(gè)切入點(diǎn)所在位置的方法所返回的對(duì)象。

除了這個(gè)Proceed方法(很重要的方法),其他和那幾個(gè)注解一樣。

@AfterReturning

顧名思義,這個(gè)注解是在目標(biāo)方法正常完成后把增強(qiáng)處理織入。這個(gè)注解可以指定兩個(gè)屬性(之前的三個(gè)注解后面的括號(hào)只寫一個(gè)@PointCut表達(dá)式,也就是只有一個(gè)屬性),一個(gè)是和其他注解一樣的PointCut表達(dá)式,也就是描述該advice在哪個(gè)接入點(diǎn)被織入;然后還可以有個(gè)returning屬性,表明可以在Advice的方法中有目標(biāo)方法返回值的形參。

@AfterReturning(returning = "returnOb", pointcut = "controllerLog() || uiControllerLog()")
    public void doAfterReturning(JoinPoint joinPoint, Object returnOb) {
        System.out.println("##################### the return of the method is : " + returnOb);
    }

瀏覽器發(fā)出一個(gè)請(qǐng)求后,效果截圖:

(這里是一個(gè)請(qǐng)求登錄界面的請(qǐng)求,所以u(píng)icontroller返回一個(gè)String作為視圖。)

@AfterThrowing

異常拋出增強(qiáng),在異常拋出后織入的增強(qiáng)。有點(diǎn)像上面的@AfterReturning,這個(gè)注解也是有兩個(gè)屬性,pointcut和throwing。

用法也和剛剛的那個(gè)returning差不多:

@AfterThrowing(pointcut = "controllerLog() || uiControllerLog()", throwing = "ex")
public void doAfterThrowing(JoinPoint joinPoint, Exception ex) {
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        System.out.println("連接點(diǎn)方法為:" + methodName + ",參數(shù)為:" + args + ",異常為:" + ex);
          
}

好了現(xiàn)在注解都介紹完了,這里還要提到上面用到的一個(gè)類:RequestContextHolder

比如說,有個(gè)需求需要在service中獲得request和response,我們一般會(huì)(我就是)直接在controller那把request或response作為參數(shù)傳到service,這就很不美觀。后來知道,原來SpringMVC提供了個(gè)很強(qiáng)大的類ReqeustContextHolder,通過他你就可以獲得request和response什么的。

//下面兩個(gè)方法在沒有使用JSF的項(xiàng)目中是沒有區(qū)別的
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
//                                            RequestContextHolder.getRequestAttributes();
//從session里面獲取對(duì)應(yīng)的值
String str = (String) requestAttributes.getAttribute("name",RequestAttributes.SCOPE_SESSION);
 
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
HttpServletResponse response = ((ServletRequestAttributes)requestAttributes).getResponse();

好了完成了這個(gè)切面的編程后,你就成功把日志功能切入到各個(gè)controller中了??磦€(gè)效果圖。

最后,再記錄一下各個(gè)不同的advice的攔截順序的問題。

情況一,只有一個(gè)Aspect類:

無異常:@Around(proceed()之前的部分) → @Before → 方法執(zhí)行 → @Around(proceed()之后的部分) → @After → @AfterReturning

有異常:@Around(proceed(之前的部分)) → @Before → 扔異常ing → @After → @AfterThrowing (大概是因?yàn)榉椒]有跑完拋了異常,沒有正確返回所有@Around的proceed()之后的部分和@AfterReturning兩個(gè)注解的加強(qiáng)沒有能夠織入)

情況二,同一個(gè)方法有多個(gè)@Aspect類攔截:

單個(gè)Aspect肯定是和只有一個(gè)Aspect的時(shí)候的情況是一樣的,但不同的Aspect里面的advice的順序呢??答案是不一定,像是線程一樣,沒有誰先誰后,除非你給他們分配優(yōu)先級(jí),同樣地,在這里你也可以為@Aspect分配優(yōu)先級(jí),這樣就可以決定誰先誰后了。

優(yōu)先級(jí)有兩種方式:

  • 實(shí)現(xiàn)org.springframework.core.Ordered接口,實(shí)現(xiàn)它的getOrder()方法
  • 給aspect添加@Order注解,該注解全稱為:org.springframework.core.annotation.Order

不管是哪種,都是order的值越小越先執(zhí)行:

@Order(5)
@Component
@Aspect
public class Aspect1 {
    // ...
}
@Order(6)
@Component
@Aspect
public class Aspect2 {
    // ...
}

這樣Aspect1就永遠(yuǎn)比Aspect2先執(zhí)行了。

注意點(diǎn):

  • 如果在同一個(gè) aspect 類中,針對(duì)同一個(gè) pointcut,定義了兩個(gè)相同的 advice(比如,定義了兩個(gè) @Before),那么這兩個(gè) advice 的執(zhí)行順序是無法確定的,哪怕你給這兩個(gè) advice 添加了 @Order 這個(gè)注解,也不行。這點(diǎn)切記。
  • 對(duì)于@Around這個(gè)advice,不管它有沒有返回值,但是必須要方法內(nèi)部,調(diào)用一下 pjp.proceed();否則,Controller 中的接口將沒有機(jī)會(huì)被執(zhí)行,從而也導(dǎo)致了 @Before這個(gè)advice不會(huì)被觸發(fā)。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論

墨江| 沾化县| 乐至县| 青龙| 治县。| 故城县| 清流县| 临海市| 宁化县| 文水县| 新乐市| 壶关县| 杭锦后旗| 原阳县| 贡嘎县| 台山市| 尼木县| 墨玉县| 汉沽区| 灵台县| 漾濞| 金堂县| 黔南| 大同市| 湄潭县| 勃利县| 南通市| 行唐县| 威海市| 蚌埠市| 吴旗县| 茌平县| 麦盖提县| 什邡市| 积石山| 乳山市| 神木县| 平阳县| 永善县| 闻喜县| 丽水市|