亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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è)務場景,所有的邏輯代碼都已經編寫完成,自測完成,而且查詢接口的數(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:聲明一個切入點,切入點決定了連接點關注的內容,使得我們可以控制通知什么時候執(zhí)行。Spring AOP只支持Spring bean的方法執(zhí)行連接點。所以你可以把切入點看做是Spring bean上方法執(zhí)行的匹配。一個切入點聲明有兩個部分:一個包含名字和任意參數(shù)的簽名,還有一個切入點表達式,該表達式決定了我們關注那個方法的執(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存在不正確行 這里直接摘抄一段來自網上獲取ip的代碼
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        SysUserDetails sysUserDetails = (SysUserDetails) authentication.getPrincipal();
        Signature signature = joinPoint.getSignature();
        if(!(signature instanceof MethodSignature)) {
            throw new IllegalArgumentException("暫不支持非方法注解");
        }
        //調用實際方法
        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ù)組轉換的集合類型在此轉換為集合類型
        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存在不正確行 這里直接摘抄一段來自網上獲取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ù)組轉換的集合類型在此轉換為集合類型
        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ù)網卡取本機配置的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)在日志已經可以正常工作了,但是業(yè)務代碼卻失效了,service注入的時候是空的(部分代碼),

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

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

10.解決的根本辦法

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

總結

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

相關文章

  • springboot使用注解獲取yml配置的兩種方法

    springboot使用注解獲取yml配置的兩種方法

    本文主要介紹了springboot使用注解獲取yml配置的兩種方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-09-09
  • Java中return的用法(兩種)

    Java中return的用法(兩種)

    這篇文章主要介紹了Java中return的用法(兩種)的相關資料,需要的朋友可以參考下
    2016-01-01
  • Java中的main函數(shù)的詳細介紹

    Java中的main函數(shù)的詳細介紹

    這篇文章主要介紹了Java中的main函數(shù)的詳細介紹的相關資料,main()函數(shù)在java程序中必出現(xiàn)的函數(shù),這里就講解下使用方法,需要的朋友可以參考下
    2017-09-09
  • Java利用Redis實現(xiàn)高并發(fā)計數(shù)器的示例代碼

    Java利用Redis實現(xiàn)高并發(fā)計數(shù)器的示例代碼

    這篇文章主要介紹了Java利用Redis實現(xiàn)高并發(fā)計數(shù)器的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-02-02
  • java實現(xiàn)時鐘效果

    java實現(xiàn)時鐘效果

    這篇文章主要為大家詳細介紹了java實現(xiàn)時鐘效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • Java8?Stream?流常用方法合集

    Java8?Stream?流常用方法合集

    這篇文章主要介紹了?Java8?Stream?流常用方法合集,Stream?是?Java8?中處理集合的關鍵抽象概念,它可以指定你希望對集合進行的操作,可以執(zhí)行非常復雜的查找、過濾和映射數(shù)據(jù)等操作,下文相關資料,需要的朋友可以參考一下
    2022-04-04
  • Springboot整合quartz實現(xiàn)多個定時任務實例

    Springboot整合quartz實現(xiàn)多個定時任務實例

    這篇文章主要介紹了Springboot整合quartz實現(xiàn)多個定時任務代碼實例,Quartz?是一款功能強大的開源任務調度框架,幾乎可以集成到任何?Java?應用程序中,Quartz?可用于創(chuàng)建簡單或復雜的任務調度,用以執(zhí)行數(shù)以萬計的任務,需要的朋友可以參考下
    2023-08-08
  • win10 64位 jdk1.8的方法教程詳解

    win10 64位 jdk1.8的方法教程詳解

    這篇文章主要介紹了win10 64位 jdk1.8的方法教程詳解,本文給大家介紹的非常詳細,對大家的工作或學習具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03
  • Java中基于Shiro,JWT實現(xiàn)微信小程序登錄完整例子及實現(xiàn)過程

    Java中基于Shiro,JWT實現(xiàn)微信小程序登錄完整例子及實現(xiàn)過程

    這篇文章主要介紹了Java中基于Shiro,JWT實現(xiàn)微信小程序登錄完整例子 ,實現(xiàn)了小程序的自定義登陸,將自定義登陸態(tài)token返回給小程序作為登陸憑證。需要的朋友可以參考下
    2018-11-11
  • Spring Boot 排除某個類加載注入IOC的操作

    Spring Boot 排除某個類加載注入IOC的操作

    這篇文章主要介紹了Spring Boot 排除某個類加載注入IOC的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08

最新評論