简体   繁体   English

Spring AOP和AspectJ加载时编织:围绕建议将为私有方法调用两次

[英]Spring AOP and AspectJ Load-Time Weaving: Around advice will be invoked twice for private methods

I will use Spring AOP and AspectJ Load-Time Weaving to measure execution time of specific private/protected/public methods in my code. 我将使用Spring AOP和AspectJ Load-Time Weaving来测量代码中特定私有/受保护/公共方法的执行时间。

To do this I wrote following annotation with one I will annotate the methods which execution time should be measured: 为此,我用以下注释编写了一个注释,我将注释应该测量执行时间的方法:

package at.scan.spring.aop.measuring;

import org.aspectj.lang.ProceedingJoinPoint;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Annotation for pointcut associated with the advice {@link MeasuringAspect#aroundAdvice(ProceedingJoinPoint)}.
 * @author ilyesve
 * @since 02.12.2015
 */
@Target(value = ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Measured {

}

I also wrote the following Aspect: 我还写了以下方面:

package at.scan.spring.aop.measuring;

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * An aspect which contains an advice to measure execution of methods that are annotated with {@link Measured} if it
 * is enabled.
 * After the execution of the annotated method the captured data over its execution will be forwarded to the
 * configured {@link MeasuringReporter}.
 * @author ilyesve
 * @since 02.12.2015
 */
@Aspect
public class MeasuringAspect {

    /** LOGGER. */
    private static final Logger LOGGER = LoggerFactory.getLogger(MeasuringAspect.class.getPackage().getName());

    /** Determines whether the Around advice is enabled. Default is disabled. */
    private boolean enabled = false;

    /** The {@link MeasuringReporter} to report the captured measuring data. */
    private MeasuringReporter reporter;

    /**
     * The Around advice which will be executed on calling of methods annotated with {@link Measured}.
     * @param pjp the join point
     * @throws Throwable on failure
     * @return result of proceeding of the join point
     */
    @Around("@annotation(Measured)")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        Object result = null;

        if (enabled && reporter != null) {
            LOGGER.debug("Starting measuring of method '{}.{}()'...",
                    pjp.getSignature().getDeclaringTypeName(),
                    pjp.getSignature().getName());

            MeasuringDataDto measuringData = new MeasuringDataDto(pjp.getSignature(), pjp.getArgs());

            measuringData.setStartTs(System.currentTimeMillis());
            try {
                measuringData.setResult(pjp.proceed());
            } catch (Throwable t) {
                measuringData.setThrowable(t);
            }
            measuringData.setEndTs(System.currentTimeMillis());

            try {
                reporter.report(measuringData);
            } catch (Throwable t) {
                LOGGER.error("Unable to report captured measuring data because of an error. MeasuringData [{}]",
                        ReflectionToStringBuilder.toString(measuringData, ToStringStyle.DEFAULT_STYLE, true, true),
                        t);
            }

            if (measuringData.getThrowable() != null) {
                throw measuringData.getThrowable();
            }

            result = measuringData.getResult();
        } else {
            result = pjp.proceed();
        }

        return result;
    }

    /**
     * @param theEnabled if {@code true} the contained advice will be enabled, otherwise disabled
     */
    public final void setEnabled(final boolean theEnabled) {
        enabled = theEnabled;
        if (enabled && reporter != null) {
            LOGGER.info("Methods will be measured. Reporter [{}]", reporter.getClass().getCanonicalName());
        }
    }

    /**
     * @param theReporter the {@link MeasuringReporter} to be used to report the captured measuring data about
     *                    execution of an method annotated with {@link Measured}
     */
    public final void setReporter(final MeasuringReporter theReporter) {
        reporter = theReporter;
        if (enabled && reporter != null) {
            LOGGER.info("Methods will be measured. Reporter [{}]", reporter.getClass().getCanonicalName());
        }
    }
}

My Spring configuration is as follows: 我的Spring配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd"
       default-autowire="byName">

    <context:load-time-weaver aspectj-weaving="autodetect" />

    <bean id="measuringAspect" class="at.scan.spring.aop.measuring.MeasuringAspect"
          factory-method="aspectOf">
        <property name="enabled" value="${measuring.enabled}" />
        <property name="reporter" ref="measuringReporterService" />
    </bean>
</beans>

I have also placed in the directory src/main/resources/META-INF of my project the following aop.xml : 我还将以下aop.xml放在我的项目的目录src/main/resources/META-INF中:

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
    <weaver>
        <include within="at.scan..*" />
    </weaver>
    <aspects>
        <aspect name="at.scan.spring.aop.measuring.MeasuringAspect" />
    </aspects>
</aspectj>

Also I added following Spring AOP and/or AspectJ specific dependencies to my POM: 另外,我还向我的POM添加了以下Spring AOP和/或AspectJ特定的依赖项:

  • org.aspectj:aspectjrt:1.8.6 org.aspectj:aspectjrt:1.8.6
  • org.aspectj:aspectjtools:1.8.6 org.aspectj:aspectjtools:1.8.6
  • org.springframework:spring-aop:4.1.6 org.springframework:spring-aop:4.1.6

Furthermore I use org.aspectj:aspectweaver:1.8.6 as Java Agent by starting of my Tomcat. 此外,通过启动Tomcat,我将org.aspectj:aspectweaver:1.8.6用作Java代理。

Everything works fine for annotated public and protected methods but for annotated private methods the around advice of my aspect will by invoked twice and I don't know why. 对于带注释的公共方法和受保护的方法,一切都可以正常工作,但是对于带注释的私有方法,我方面的周围建议将被调用两次,而我不知道为什么。

Your pointcut expression matches all joinpoints where the subject of the joinpoint has the @Measured annotation. 您的切入点表达式匹配所有@Measured点,其中@Measured点的主题具有@Measured批注。 This includes both method-execution and method-call type joinpoints. 包括方法,执行方法调用类型joinpoints。 You probably see the advice executing twice on private methods only because your private methods are the ones which are invoked locally from advised classes. 您可能只看到在私有方法上执行两次建议,因为您的私有方法是从建议类本地调用的。 Should you have method calls from advised code to @Measured annotated methods of other visibility, you'd see the double advice execution on those methods as well, not only private methods. 如果您有从建议代码到其他可见性的@Measured注释方法的方法调用 ,那么您不仅会看到私有方法,还会看到对这些方法的双重建议执行。 The solution is to change your pointcut expression to restrict the joinpoint to either method-execution or method-call . 解决方案是更改切入点表达式以将连接点限制为method-executionmethod-call My guess in your case would be the method execution itself, so your pointcut expression would become this: 我的猜测是方法执行本身,因此您的切入点表达式将变为:

@Around("execution(@Measured * *(..))")

Since you don't bind the annotation anywhere in your advice, you don't even need the @annotation(Measured) part. 由于您没有在建议中的任何地方绑定注释,因此甚至不需要@annotation(Measured)部分。

While setting up new aspects in your project, it's always a good idea to check the weaving process by enabling -showWeaveInfo and -verbose in your aop.xml. 在项目中设置新方面时,始终建议在aop.xml中启用-showWeaveInfo-verbose来检查编织过程。

<weaver options="-showWeaveInfo -verbose">
...
</weaver>

That would expose log messages on standard error similar to these (note the line numbers too): 这将暴露类似于以下内容的标准错误日志消息(也请注意行号):

[AppClassLoader@62b103dd] weaveinfo Join point 'method-call(void at.scan.spring.aop.measuring.MeasuredClass.test3())' in Type 'at.scan.spring.aop.measuring.MeasuredClass' (MeasuredClass.java:18) advised by around advice from 'at.scan.spring.aop.measuring.MeasuringAspect' (MeasuringAspect.java)
[AppClassLoader@62b103dd] weaveinfo Join point 'method-execution(void at.scan.spring.aop.measuring.MeasuredClass.test3())' in Type 'at.scan.spring.aop.measuring.MeasuredClass' (MeasuredClass.java:27) advised by around advice from 'at.scan.spring.aop.measuring.MeasuringAspect' (MeasuringAspect.java)
@Around("execution(* *(..)) && @annotation(Measured)")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
   ...
}

Adding execution(* *(..)) 添加执行(* *(..))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM