background

In real development we often need to print logs or monitor method execution, using Spring Ao can do this very well, but the pointcuts in the aspect are fixed values and cannot be changed, resulting in a lot of duplicated code for multiple applications using aop with the same functionality. We want to encapsulate and separate these same functionality into a common component package that can dynamically configure pointcuts.

Implementation approach

As you know from Spring’s AOP mechanism, the following steps are required to enter a method or class

  • Pointcut, implement spring Pointcut interface custom Pointcut, according to the specific business implementation

    This article USES AspectJExpressionPointcut implementation in spring

  • AbstractBeanFactoryPointcutAdvisor Advisor, it can extend the Spring

  • MethodInterceptor, which implements the intercepting logic in this interface

  • Instantiation is injected into the Spring container

The specific practices

DynamicPointcutAdvisor Dynamic pointcut
public class DynamicPointcutAdvisor extends AbstractBeanFactoryPointcutAdvisor {
    /** * Expression */
    private String expression;

    public DynamicPointcutAdvisor(String expression) {
        this.expression = expression;
    }

    public Pointcut aspectJExpressionPointcut(a) {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(this.expression);
        return pointcut;
    }

    @Override
    public Pointcut getPointcut(a) {
        returnaspectJExpressionPointcut(); }}Copy the code
DynamicMethodInterceptor DynamicMethodInterceptor
@Slf4j
public class DynamicMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object ret;
        JSONObject rest = new JSONObject();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        try {
            UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
            rest.put("RequestUrl", request.getRequestURL().toString());
            rest.put("RequestType", request.getMethod());
            rest.put("RequestHeader", RequestUtils.getHeadersInfo(request));
            rest.put("RequestMethod", invocation.getMethod().getName());
            rest.put("RequestParams", invocation.getArguments());
            rest.put("ServerAddr", StrFormatter.format("{} {} {} {} {}", request.getScheme(), ": / /", request.getServerName(), ":", request.getServerPort()));
            rest.put("RemoteAddr", RequestUtils.getRemoteAddr(request));
            rest.put("Browser", userAgent.getBrowser().toString());
            rest.put("BrowserVersion", userAgent.getBrowserVersion());
            rest.put("operatingSystem", userAgent.getOperatingSystem().toString());
            rest.put("requestTime", LocalDateTime.now());
            ret = invocation.proceed();
            rest.put("responseResult", ret);
        } catch (Throwable throwable) {
            rest.put("ExceptionName", throwable.getClass().getName());
            rest.put("ExceptionMessage", throwable.getMessage());
            JSONObject e = new JSONObject();
            e.put("ExceptionName", throwable.getClass().getName());
            e.put("ExceptionMessage", throwable.getMessage());
            log.error(e.toString());
            throw throwable;
        } finally {
            rest.put("Time", System.currentTimeMillis() - startTime);
            log.info(rest.toString());
        }
        returnret; }}Copy the code
DynamicAdvisorConfiguration instantiation
@Configuration
@ConditionalOnProperty(name = "log.dynamic.pointcut.expression")
public class DynamicAdvisorConfiguration {
    @Value("${log.dynamic.pointcut.expression:}")
    private String expression;

    @Bean(name = "dynamicPointcutAdvisor")
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public DynamicPointcutAdvisor adapterServiceAdvisor(a) {
        DynamicPointcutAdvisor advisor = new DynamicPointcutAdvisor(expression);
        advisor.setAdviceBeanName("dynamicPointcutAdvisor");
        advisor.setAdvice(new DynamicMethodInterceptor());
        advisor.setOrder(Ordered.HIGHEST_PRECEDENCE);
        returnadvisor; }}Copy the code
The configuration fileapplication.properties
log.dynamic.pointcut.expression=execution(public * com.devin.dynamic.controller.. *. * (..) ) log.dynamic.pointcut.expression=trueCopy the code

The results of

[nio-9000-exec-2] c.s.s.a.d.DynamicMethodInterceptor       : {"responseResult": {"code":200."message":"16003908714631539353"},"RequestMethod":"dynamicNumber"."RequestUrl":"http://localhost:9000/dynamic/number"."ServerAddr":"http://localhost:9000"."Time":52."RemoteAddr":"0:0:0:0:0:0:0:1"."operatingSystem":"WINDOWS_10"."RequestParams": []."requestTime":": the 2020-03-25 T11 thing. 344"."RequestHeader": {"accept-language":"zh-CN,en-US; Q = 0.7, en. Q = 0.3"."host":"localhost:9000"."upgrade-insecure-requests":"1"."connection":"keep-alive"."accept-encoding":"gzip, deflate"."user-agent":"Mozilla / 5.0 (Windows NT 10.0; Win64; x64; The rv: 74.0) Gecko / 20100101 Firefox / 74.0"."accept":"text/html,application/xhtml+xml,application/xml; Q = 0.9, image/webp, * / *; Q = 0.8"},"BrowserVersion": {"majorVersion":"74"."minorVersion":"0"."version":"74.0"},"RequestType":"GET"."Browser":"FIREFOX7"}
Copy the code

expandPointcut

Spring has some pointcut implementations by default, and we just need to instantiate them

However, for those that do not meet our needs, we can implement the Pointcut interface.

package org.springframework.aop;

public interface Pointcut {
    Pointcut TRUE = TruePointcut.INSTANCE;
    ClassFilter getClassFilter(a);
    MethodMatcher getMethodMatcher(a);
}

Copy the code

Others please explore on your own.

Refer to the article

[Spring AOP – Dynamic Pointcut](