问题
I will use Spring AOP and AspectJ Load-Time Weaving to measure execution time of specific private/protected/public methods in my code.
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:
<?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
:
<!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:
- org.aspectj:aspectjrt:1.8.6
- org.aspectj:aspectjtools:1.8.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.
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.
回答1:
Your pointcut expression matches all joinpoints where the subject of the joinpoint has the @Measured
annotation. This includes both method-execution and method-call type 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. The solution is to change your pointcut expression to restrict the joinpoint to either method-execution
or method-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.
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.
<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)
回答2:
@Around("execution(* *(..)) && @annotation(Measured)")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
...
}
Adding execution(* *(..))
来源:https://stackoverflow.com/questions/34218012/spring-aop-and-aspectj-load-time-weaving-around-advice-will-be-invoked-twice-fo