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

SpringBoot AOP導致service注入后是null的問題

 更新時間:2024年10月31日 14:14:01   作者:Xiao_zuo_ya  
本文主要講述了如何利用SpringAOP實現(xiàn)用戶操作日志的記錄,首先,引入SpringBoot的AOP依賴,然后,選擇基于注解的形式來實現(xiàn)日志操作,以避免污染原有代碼和邏輯,在理解了SpringBootAOP的一些注解后,需要記錄用戶的正常請求以及異常請求的信息

SpringBoot AOP導致service注入后是null

1.由于業(yè)務需求需要

記錄用戶操作日志,無疑需要使用到SpringAOP。

2.先引入SpringBoot的AOP maven 依賴

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

3.實現(xiàn)這個日志操作可以有很多種方法

比如寫攔截器,或者基于注解形式,在或者在原來寫好的代碼中添加也行,但是最后的一種無疑會污染原來的代碼,同時會對原來的邏輯有一定污染,而現(xiàn)在的業(yè)務場景,所有的邏輯代碼都已經(jīng)編寫完成,自測完成,而且查詢接口的數(shù)量明顯大于(增刪改),而且用戶的操作日志我們最關心的無疑是對數(shù)據(jù)的操作。所以選擇基于注解的形式實現(xiàn)。

4.注解編寫

上代碼

@Retention(RetentionPolicy.RUNTIME)//元注解,定義注解被保留策略,一般有三種策略
//1、RetentionPolicy.SOURCE 注解只保留在源文件中,在編譯成class文件的時候被遺棄
//2、RetentionPolicy.CLASS 注解被保留在class中,但是在jvm加載的時候北歐拋棄,這個是默認的聲明周期
//3、RetentionPolicy.RUNTIME 注解在jvm加載的時候仍被保留
@Target({ElementType.METHOD}) //定義了注解聲明在哪些元素之前
@Documented
public @interface SystemOperaLog {
    //定義成員
    String descrption() default "" ;//描述
    String actionType() default "添加" ;//操作的類型,1、添加 2、修改 3、刪除
}

5.下面需要對SpringBoot AOP 中一些注解

了解一下

  • @Aspect:描述一個切面類,定義切面類的時候需要打上這個注解
  • @Configuration:spring-boot配置類
  • @Pointcut:聲明一個切入點,切入點決定了連接點關注的內(nèi)容,使得我們可以控制通知什么時候執(zhí)行。Spring AOP只支持Spring bean的方法執(zhí)行連接點。所以你可以把切入點看做是Spring bean上方法執(zhí)行的匹配。一個切入點聲明有兩個部分:一個包含名字和任意參數(shù)的簽名,還有一個切入點表達式,該表達式?jīng)Q定了我們關注那個方法的執(zhí)行。

注:作為切入點簽名的方法必須返回void 類型

Spring AOP支持在切入點表達式中使用如下的切入點指示符:    

  • execution - 匹配方法執(zhí)行的連接點,這是你將會用到的Spring的最主要的切入點指示符。
  • within - 限定匹配特定類型的連接點(在使用Spring AOP的時候,在匹配的類型中定義的方法的執(zhí)行)。
  • this - 限定匹配特定的連接點(使用Spring AOP的時候方法的執(zhí)行),其中bean reference(Spring AOP 代理)是指定類型的實例。
  • target - 限定匹配特定的連接點(使用Spring AOP的時候方法的執(zhí)行),其中目標對象(被代理的應用對象)是指定類型的實例。
  • args - 限定匹配特定的連接點(使用Spring AOP的時候方法的執(zhí)行),其中參數(shù)是指定類型的實例。
  • @target - 限定匹配特定的連接點(使用Spring AOP的時候方法的執(zhí)行),其中正執(zhí)行對象的類持有指定類型的注解。
  • @args - 限定匹配特定的連接點(使用Spring AOP的時候方法的執(zhí)行),其中實際傳入?yún)?shù)的運行時類型持有指定類型的注解。
  • @within - 限定匹配特定的連接點,其中連接點所在類型已指定注解(在使用Spring AOP的時候,所執(zhí)行的方法所在類型已指定注解)。
  • @annotation - 限定匹配特定的連接點(使用Spring AOP的時候方法的執(zhí)行),其中連接點的主題持有指定的注解。

其中execution使用最頻繁,即某方法執(zhí)行時進行切入。定義切入點中有一個重要的知識,即切入點表達式,我們一會在解釋怎么寫切入點表達式。

切入點意思就是在什么時候切入什么方法,定義一個切入點就相當于定義了一個“變量”,具體什么時間使用這個變量就需要一個通知。

即將切面與目標對象連接起來。

如例子中所示,通知均可以通過注解進行定義,注解中的參數(shù)為切入點。

spring aop支持的通知:

  • @Before:前置通知:在某連接點之前執(zhí)行的通知,但這個通知不能阻止連接點之前的執(zhí)行流程(除非它拋出一個異常)。
  • @AfterReturning :后置通知:在某連接點正常完成后執(zhí)行的通知,通常在一個匹配的方法返回的時候執(zhí)行。

6.了解完以上步驟

需要做的是記錄用戶正常請求,以及異常請求需要記錄的信息,對應一下實體操作

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="SysOperaLog對象", description="")
public class SysOperaLog implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "log_id", type = IdType.AUTO)
    private Integer logId;

    @ApiModelProperty(value = "操作類型")
    private String type;

    @ApiModelProperty(value = "訪問資源路徑")
    private String url;

    @ApiModelProperty(value = "操作人員")
    private String operaUser;

    @ApiModelProperty(value = "方法名稱")
    private String methodName;

    @ApiModelProperty(value = "訪問攜帶參數(shù)")
    private String params;

    @ApiModelProperty(value = "遠程ip地址")
    private String ipAddress;

    @ApiModelProperty(value = "訪問時間")
    @TableField("operaTime")
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime operaTime;

    @ApiModelProperty(value = "訪問總時長單位毫秒")
    @TableField("timeLong")
    private Long timeLong;

    @ApiModelProperty(value = "描述")
    private String des;

    @ApiModelProperty(value = "訪問狀態(tài) 0 訪問成功 -1 訪問失敗")
    private Integer visitState;

    @ApiModelProperty(value = "異常信息")
    private String exceptionDetail;


}

7.用戶正常請求

在用戶正常請求頭部加上自定義的注解

    @PostMapping("/save")
    @SystemOperaLog(descrption = "新增車位信息")
    public Result saveParkLot(@ApiParam(name = "parkLotsDTO", value = "車位信息") @RequestBody ParkLotsDTO parkLotsDTO, BindingResult bindingResult) {
        // 現(xiàn)在表示執(zhí)行的驗證出現(xiàn)錯誤
        if (bindingResult.hasErrors()) {
            // 獲取全部錯誤信息
            List<ObjectError> allErrors = bindingResult.getAllErrors();
            String errorMsg = "";
            if (!CollectionUtils.isEmpty(allErrors)) {
                errorMsg = allErrors.get(0).getDefaultMessage();
            }
            return ResultSupport.fail(errorMsg);
        } else {
            parkLotsService.save(parkLotsDTO);
            return ResultSupport.saveSuccess();
        }
    }

8.在AOP中攔截進入該方法的前置操作以及異常操作

@Aspect
@Configuration
@Slf4j
public class SystemOperaLogAop {

    @Autowired
    private SysOperaLogMapper sysOperaLogMapper;

    /***
     * 定義controller切入點攔截規(guī)則,攔截SystemControllerLog注解的方法
     */
    @Pointcut("@annotation(cn.hayll.parking.local.common.annotation.SystemOperaLog)")
    public void controllerAspect(){}

    /***
     * 攔截控制層的操作日志
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("controllerAspect()")
    public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {
        //1、開始時間
        long beginTime = System.currentTimeMillis();
        //利用RequestContextHolder獲取requst對象
        ServletRequestAttributes requestAttr = (ServletRequestAttributes)RequestContextHolder.currentRequestAttributes();
        String uri = requestAttr.getRequest().getServletPath();
        //訪問目標方法的參數(shù) 可動態(tài)改變參數(shù)值
        Object[] args = joinPoint.getArgs();
        //方法名獲取
        String methodName = joinPoint.getSignature().getName();
        //可能在反向代理請求進來時,獲取的IP存在不正確行 這里直接摘抄一段來自網(wǎng)上獲取ip的代碼
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        SysUserDetails sysUserDetails = (SysUserDetails) authentication.getPrincipal();
        Signature signature = joinPoint.getSignature();
        if(!(signature instanceof MethodSignature)) {
            throw new IllegalArgumentException("暫不支持非方法注解");
        }
        //調(diào)用實際方法
        Object object = joinPoint.proceed();
        //獲取執(zhí)行的方法
        MethodSignature methodSign = (MethodSignature) signature;
        Method method = methodSign.getMethod();
        //判斷是否包含了 無需記錄日志的方法
        long endTime = System.currentTimeMillis();
        //模擬異常
        //System.out.println(1/0);
        SysOperaLog systemLogDTO = new SysOperaLog();
        systemLogDTO.setType(getAnnontationMethodDescription(joinPoint,1));
        systemLogDTO.setUrl(uri);
        systemLogDTO.setOperaUser(sysUserDetails.getUsername());
        systemLogDTO.setMethodName(methodName);
        systemLogDTO.setIpAddress(getIpAddr(requestAttr.getRequest()));
        List list = CollectionUtils.arrayToList(args);
        List arrayList = new ArrayList(list);
        //移除操作需要將數(shù)組轉(zhuǎn)換的集合類型在此轉(zhuǎn)換為集合類型
        Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()){
            if(iterator.next().toString().contains("BeanPropertyBindingResult")){
                iterator.remove();
                break;
            }
        }
        systemLogDTO.setParams(arrayList.toString());
        systemLogDTO.setOperaTime(LocalDateTime.now());
        systemLogDTO.setDes(getAnnontationMethodDescription(joinPoint,0));
        systemLogDTO.setTimeLong(endTime - beginTime);
        sysOperaLogMapper.insert(systemLogDTO);
        return object;
    }

    //異常處理
    @AfterThrowing(pointcut = "controllerAspect()",throwing="e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) throws Throwable{
        //1、開始時間
        long beginTime = System.currentTimeMillis();
        //利用RequestContextHolder獲取requst對象
        ServletRequestAttributes requestAttr = (ServletRequestAttributes)RequestContextHolder.currentRequestAttributes();
        String uri = requestAttr.getRequest().getServletPath();
        //訪問目標方法的參數(shù) 可動態(tài)改變參數(shù)值
        Object[] args = joinPoint.getArgs();
        //方法名獲取
        String methodName = joinPoint.getSignature().getName();
        //可能在反向代理請求進來時,獲取的IP存在不正確行 這里直接摘抄一段來自網(wǎng)上獲取ip的代碼
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        SysUserDetails sysUserDetails = (SysUserDetails) authentication.getPrincipal();
        Signature signature = joinPoint.getSignature();
        if(!(signature instanceof MethodSignature)) {
            throw new IllegalArgumentException("暫不支持非方法注解");
        }
        //獲取執(zhí)行的方法
        MethodSignature methodSign = (MethodSignature) signature;
        Method method = methodSign.getMethod();
        //判斷是否包含了 無需記錄日志的方法
        long endTime = System.currentTimeMillis();
        //模擬異常
        SysOperaLog systemLogDTO = new SysOperaLog();
        systemLogDTO.setUrl(uri);
        systemLogDTO.setType(getAnnontationMethodDescription(joinPoint,1));
        systemLogDTO.setOperaUser(sysUserDetails.getUsername());
        systemLogDTO.setMethodName(methodName);
        systemLogDTO.setIpAddress(getIpAddr(requestAttr.getRequest()));
        List list = CollectionUtils.arrayToList(args);
        List arrayList = new ArrayList(list);
        //移除操作需要將數(shù)組轉(zhuǎn)換的集合類型在此轉(zhuǎn)換為集合類型
        Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()){
            if(iterator.next().toString().contains("BeanPropertyBindingResult")){
                iterator.remove();
                break;
            }
        }
        systemLogDTO.setParams(arrayList.toString());
        systemLogDTO.setOperaTime(LocalDateTime.now());
        systemLogDTO.setTimeLong(endTime - beginTime);
        systemLogDTO.setDes(getAnnontationMethodDescription(joinPoint,0));
        systemLogDTO.setVisitState(-1);
        systemLogDTO.setExceptionDetail(e.getMessage());
        sysOperaLogMapper.insert(systemLogDTO);
    }

    public static String getIpAddr(HttpServletRequest request) {
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals("127.0.0.1")) {
                    // 根據(jù)網(wǎng)卡取本機配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        log.error("獲取ip異常:{}" ,e.getMessage());
                        e.printStackTrace();
                    }
                    ipAddress = inet.getHostAddress();
                }
            }
            // 對于通過多個代理的情況,第一個IP為客戶端真實IP,多個IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) {
                // = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress = "";
        }
        return ipAddress;
    }

    /***
     * 獲取controller的操作信息
     * @param point
     * @return
     */
    public String getAnnontationMethodDescription(JoinPoint point,Integer type) throws  Exception{
        //獲取連接點目標類名
        String targetName = point.getTarget().getClass().getName() ;
        //獲取連接點簽名的方法名
        String methodName = point.getSignature().getName() ;
        //獲取連接點參數(shù)
        Object[] args = point.getArgs() ;
        //根據(jù)連接點類的名字獲取指定類
        Class targetClass = Class.forName(targetName);
        //獲取類里面的方法
        Method[] methods = targetClass.getMethods() ;
        String description="" ;
        for (Method method : methods) {
            if (method.getName().equals(methodName)){
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == args.length){
                    if(type==0){
                        description = method.getAnnotation(SystemOperaLog.class).descrption();
                        break;
                    }else{
                        description = method.getAnnotation(SystemOperaLog.class).actionType();
                        break;
                    }

                }
            }
        }
        return description ;
    }

9.說一下踩的一個坑

現(xiàn)在日志已經(jīng)可以正常工作了,但是業(yè)務代碼卻失效了,service注入的時候是空的(部分代碼),

一頓百度以后發(fā)現(xiàn),原來AOP只能對public 和provide 生效,如果你的方法限制是private,那么service注入就為空,在springboot 中默認使用的是cglib來代理操作對象,首先,私有方法是不會出現(xiàn)在代理類中,這也就是為什么代理對象無法對private操作的根本原因

  • jdk是代理接口,私有方法必然不會存在在接口里,所以就不會被攔截到; 
  • cglib是子類,private的方法照樣不會出現(xiàn)在子類里,也不能被攔截。 

10.解決的根本辦法

不是強制使用cglib來代理,而是要將你的controller中的方法不設置私有屬性,以上僅僅代表個人觀點喲。

總結(jié)

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

相關文章

  • Java常見的數(shù)據(jù)結(jié)構(gòu)之棧和隊列詳解

    Java常見的數(shù)據(jù)結(jié)構(gòu)之棧和隊列詳解

    這篇文章主要介紹了Java常見的數(shù)據(jù)結(jié)構(gòu)之棧和隊列詳解,棧(Stack) 是一種基本的數(shù)據(jù)結(jié)構(gòu),具有后進先出(LIFO)的特性,類似于現(xiàn)實生活中的一疊盤子,棧用于存儲一組元素,但只允許在棧頂進行插入(入棧)和刪除(出棧)操作,需要的朋友可以參考下
    2023-10-10
  • Java中equals()方法重寫實現(xiàn)代碼

    Java中equals()方法重寫實現(xiàn)代碼

    這篇文章主要介紹了Java中equals()方法重寫實現(xiàn)代碼的相關資料,需要的朋友可以參考下
    2017-05-05
  • 布隆過濾器的原理以及java 簡單實現(xiàn)

    布隆過濾器的原理以及java 簡單實現(xiàn)

    這篇文章主要介紹了布隆過濾器的原理以及java 簡單實現(xiàn),幫助大家更好的理解和學習Java,感興趣的朋友可以了解下
    2020-11-11
  • Java使用RandomAccessFile類對文件進行讀寫

    Java使用RandomAccessFile類對文件進行讀寫

    本篇文章主要介紹了Java使用RandomAccessFile類對文件進行讀寫,詳細的介紹了RandomAccessFile類的使用技巧和實例應用,有興趣的可以了解一下
    2017-04-04
  • 淺談Spring事務傳播行為實戰(zhàn)

    淺談Spring事務傳播行為實戰(zhàn)

    這篇文章主要介紹了淺談Spring事務傳播行為實戰(zhàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-09-09
  • MyBatis-Plus中如何使用ResultMap的方法示例

    MyBatis-Plus中如何使用ResultMap的方法示例

    本文主要介紹了MyBatis-Plus中如何使用ResultMap,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Java中防止SQL注入和XSS攻擊的處理指南

    Java中防止SQL注入和XSS攻擊的處理指南

    在當今互聯(lián)網(wǎng)時代,Web應用安全已成為開發(fā)者必須重視的核心問題,SQL注入和XSS攻擊作為OWASP?Top?10中的常見威脅,本文將深入探討這兩種攻擊方式的原理,并提供Java開發(fā)中的實用防護方案
    2025-10-10
  • java random.nextInt的坑及解決

    java random.nextInt的坑及解決

    這篇文章主要介紹了java random.nextInt的坑及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • springboot 配置文件配置項前綴為0的數(shù)字特殊處理方式

    springboot 配置文件配置項前綴為0的數(shù)字特殊處理方式

    這篇文章主要介紹了springboot 配置文件配置項前綴為0的數(shù)字特殊處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • spring異步service中處理線程數(shù)限制詳解

    spring異步service中處理線程數(shù)限制詳解

    這篇文章主要給大家介紹了關于spring異步service中處理線程數(shù)限制的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用spring具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-09-09

最新評論

南昌县| 青浦区| 安吉县| 齐河县| 卢氏县| 农安县| 绥中县| 锡林郭勒盟| 绥德县| 临漳县| 武威市| 乌拉特后旗| 延吉市| 随州市| 新乡市| 扶绥县| 十堰市| 梅州市| 平利县| 哈巴河县| 鄯善县| 禄丰县| 临湘市| 海伦市| 隆化县| 泸州市| 青田县| 手游| 泰兴市| 兰州市| 永川市| 嵊泗县| 平塘县| 阜城县| 白河县| 山阴县| 兰考县| 都江堰市| 新乐市| 方正县| 潜江市|