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.