Reasons and Solutions of Executing SpringAop @ Around Twice

  • 2021-11-01 03:10:47
  • OfStack

In use AOP When logging around notifications, it is found that @Around Method is executed twice. Although the surrounding notification would have been executed twice here, it is normally executed once before and once after the tangent method, but in reality, it is executed twice before and twice after the tangent method.

The text is not easy to understand, or write 1 code:


    @Around("logPointCut()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        logger.debug("==========Request log==========");
        long startTime = System.currentTimeMillis();
        Object ob = pjp.proceed();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //JSONObject ipInfo = JSONObject.fromObject(URLDecoder.decode(WebUtils.getCookie(request,"IP_INFO").getValue(),"utf-8"));
        //logger.debug("IP: {}", ipInfo.get("ip"));
        //logger.debug("CITY {}", ipInfo.get("city"));
        logger.debug("IP: {}", BlogUtil.getClientIpAddr(request));
        logger.debug("REQUEST_URL: {}", request.getRequestURL().toString());
        logger.debug("HTTP_METHOD: {}", request.getMethod());
        logger.debug("CLASS_METHOD: {}", pjp.getSignature().getDeclaringTypeName() + "."
                + pjp.getSignature().getName());
        //logger.info(" Parameter  : " + Arrays.toString(pjp.getArgs()));
        logger.debug("USE_TIME: {}", System.currentTimeMillis() - startTime);
        logger.debug("==========Request log end==========");
        return ob;
    }

Then refresh the page below 1, and get the following log:

As you can see, although it was refreshed only once, the log was output twice, which should not be. Then through breakpoint debugging, it is discovered that it is because in the Controller Used in the @ModelAttribute


@ModelAttribute
public void counter(Model model) {
    counter.setCount(counter.getCount() + 1);
    model.addAttribute("count", counter.getCount());
}

@ModelAttribute The annotation method will be found in the Controller Method is executed once before it is executed, and I put it in the Controller And intercepts all Controller The method in,

This leads to:

1. First, the page requests to Controller , execution @ModelAttribute The method annotated will be used at this time AOP Intercepted once.

2. Finish execution @ModelAttribute After the method is annotated, execute the @RequestMapping The method of annotation is again AOP Intercepted once.

Therefore, there will be two log outputs.

Solution:

1. Will Controller In @ModelAttribute Method, extract it and put it in @ControllerAdvice Medium.

2. Right AOP Interception rules add annotation matching, for example:


execution(public * com.blog.controller.*.*(..)) && (@annotation(org.springframework.web.bind.annotation.RequestMapping))


&& (@annotation(org.springframework.web.bind.annotation.RequestMapping

Indicates that this will only intercept RequestMappping The method of annotation.

Note:

If it is a 1 method a() Call a method in the same class b() If the method a() If you do interception, AOP Will only intercept a() Without intercepting b() , because ah a() The call to b () is made through the this.b() Called by the AOP What is really executing is the generated proxy class, which is passed through this Naturally, you can't intercept methods b() It's over.

Understand the use and attention of Spring @ Around

Note:

1. @ModelAttribute0 Sequence of section annotations

@ModelAttribute1 @Around (The method to be proxy executes in it) @AfterReturning @after

2. No @Around The method execution exception to be proxy will be @AfterThrowing Capture;

3. In @Around How to Perform Method Execution to Proxy


@Around("execution(* cn.com.xalead.spring.MeInterface.*(..)) || execution(* cn.com.xalead.spring.KingInterface.*(..))")
public Object test(ProceedingJoinPoint proceeding) {
    Object o = null;
    try {
        // Execute 
        o = proceeding.proceed(proceeding.getArgs());
    } catch (Throwable e) {
        e.printStackTrace();
    }
    return o;
}

Related articles: