class Test {
#override
public String a(){
b();
d();
}
private String b() {
c();
}
private String c(){
d();
}
private String d(){}
}
I want to intercept each methods of class Test that is been called from overridden method A() and want to know how much time each method like b(), c() took while processing some business logic separately.
How can I achieve it using Spring AOP or Aspectj?
In order to
weave into private methods,
handle self-invocation within one class,
dynamically determine control flow and limit interception to only methods called directly or indirectly by your interface method
you need to switch from Spring AOP (proxy-based, many limitations, slow) to AspectJ using LTW (load-time weaving) as described in the Spring manual.
Here is an example in pure AspectJ (no Spring, Just Java SE) which you can easily adapt to your needs:
Sample interface
package de.scrum_master.app;
public interface TextTransformer {
String transform(String text);
}
Class implementing interface incl. main method:
As you can see, I made up an example like yours and also made the methods spend time in order to have something to measure in the aspect later:
package de.scrum_master.app;
public class Application implements TextTransformer {
#Override
public String transform(String text) {
String geekSpelling;
try {
geekSpelling = toGeekSpelling(text);
return toUpperCase(geekSpelling);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private String toGeekSpelling(String text) throws InterruptedException {
Thread.sleep(100);
return replaceVovels(text).replaceAll("[lL]", "1");
}
private String replaceVovels(String text) throws InterruptedException {
Thread.sleep(75);
return text.replaceAll("[oO]", "0").replaceAll("[eE]", "Ɛ");
}
private String toUpperCase(String text) throws InterruptedException {
Thread.sleep(50);
return text.toUpperCase();
}
public static void main(String[] args) throws InterruptedException {
System.out.println(new Application().transform("Hello world!"));
}
}
Aspect:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import static java.lang.System.currentTimeMillis;
#Aspect
public class TimingAspect {
#Around("execution(* *(..)) && cflow(execution(* de.scrum_master.app.TextTransformer.*(..)))")
public Object measureExecutionTime(ProceedingJoinPoint thisJoinPoint) throws Throwable {
long startTime = currentTimeMillis();
Object result = thisJoinPoint.proceed();
System.out.println(thisJoinPoint + " -> " + (currentTimeMillis() - startTime) + " ms");
return result;
}
}
Console log:
execution(String de.scrum_master.app.Application.replaceVovels(String)) -> 75 ms
execution(String de.scrum_master.app.Application.toGeekSpelling(String)) -> 189 ms
execution(String de.scrum_master.app.Application.toUpperCase(String)) -> 63 ms
execution(String de.scrum_master.app.Application.transform(String)) -> 252 ms
HƐ110 W0R1D!
You can also exclude the transform(..) method by just changing the pointcut from cflow() to cflowbelow():
#Around("execution(* *(..)) && cflowbelow(execution(* de.scrum_master.app.TextTransformer.*(..)))")
Then the console log is just:
execution(String de.scrum_master.app.Application.replaceVovels(String)) -> 77 ms
execution(String de.scrum_master.app.Application.toGeekSpelling(String)) -> 179 ms
execution(String de.scrum_master.app.Application.toUpperCase(String)) -> 62 ms
HƐ110 W0R1D!
Incidentally, please do read an AspectJ and/or Spring AOP manual.
Spring AOP is applied using proxies, when you call a method of the bean from outside, the proxy is used and the method could be intercepted, but when you call the method from inside the class, the proxy is not used and the class is used directly.
You have three options
The first and easy one, if you do not have problems using public methods is to move the functions b(), c(), and d() to another bean. This way each call to this methods would be intercepted.
public class Test {
public String a() { ... }
}
public class Test2 {
public String b() { ... }
public String c() { ... }
public String d() { ... }
}
You can also use it as inner static class if you want to keep all in the same file.
public class Test {
public String a() { ... }
public static class Test2 {
public String b() { ... }
public String c() { ... }
public String d() { ... }
}
}
You should autowire Test2 in the constructor of Test.
public class Test {
private final Test2 test2;
#Autowired public Test(final Test2 test2) {
this.test2 = test2;
}
public String a() {
test2.b();
test2.c();
test2.d();
}
}
And finally create the around method.
#Around(value = "execution(* package.of.the.class.Test.*(..))")
public Object aroundA(ProceedingJoinPoint pjp) throws Throwable { ... }
#Around(value = "execution(* package.of.the.class.Test2.*(..))")
public Object aroundBCD(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object output = pjp.proceed();
long elapsedTime = System.currentTimeMillis() - start;
// perform side efects with elapsed time e.g. print, store...
return output;
}
Or something like
#Around(value = "execution(* package.of.the.class.Test.*(..)) || " +
"execution(* package.of.the.class.Test2.*(..))")
public Object aroundABCD(ProceedingJoinPoint pjp) throws Throwable { ... }
The second option is to use a CGLIB bean, package private methods and self injection.
You declare a CGLIB bean just using the scope annotation
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
#Bean public Test test() {
return new Test();
}
Self injection and package private methods as follows
public class Test {
#Autowired private Test test;
// ...
public String a() {
test.b(); // call through proxy (it is intercepted)
}
String b() { ... } // package private method
// ...
}
The third solution is to use LWT Load-Time weaving that is using aspectj instead of the spring aop. This allows to intercept method calls even inside the same class. You can use the official spring documentation to implement it, but you will have to use a java agent to run.
Calling method
If you need to know if an specific method made the call to the intercepted function, you can use Thread.currentThread().getStackTrace() for the options 1 or 2. If you use aspectj (option 3), you could intercept the methods with cflow().
I am trying to intercept a method but I think that Spring AOP give me the wrong jointpoint. See the example below:
public interface IMyDummyComponent <K>{
public String getValue(final K key);
}
I have a class that implements this interfacce.
public class MyDummyComponent implements IMyDummyComponent<String>{
#Override
#Logging
public String getValue(String key) {
LOG.info("begin getValue()");
return "value";
}
}
Now here is my Spring AOP aspect:
#Aspect
public class LogginAspect {
private Logger LOG = LoggerFactory.getLogger(LogginAspect.class);
#Pointcut("#annotation(com.package.annotation.Logging)")
public void loggingAnnotationPointcut() {
}
#Around("loggingAnnotationPointcut()")
public Object methodsAnnotatedWithLogging(final ProceedingJoinPoint joinPoint) throws Throwable {
LOG.info("begin methodsAnnotatedWithLogging()");
Object result = null;
try {
LOG.debug("begin interpected method=" + joinPoint.getSignature());
result = joinPoint.proceed();
} finally {
LOG.debug("end interpected method=" + joinPoint.getSignature());
}
return result;
}
}
The problem is that the joinPoint.getSignature() return "public String getValue(Object key)" instead of "public String getValue(String key)". Is this a spring AOP bug ? I need to know the real signature of my joinpoint (getValue(String key)). Is there a form to get this using aspect? If you use reflection you can get this method by in my point of view this annotacion should get this. Thanks.
I have simple Spring Data JPA repository.
public interface UserRepository extends JpaRepository<UserEntity, Serializable>{ … }
Is there any way to monitor execution time for methods generated by Spring (for example findOne(…))?
The easiest way is to use a CustomizableTraceInterceptor as follows:
#Configuration
#EnableAspectJAutoProxy
public class SpringDataExecutionLoggingConfiguration {
#Bean
public CustomizableTraceInterceptor customizableTraceInterceptor() {
CustomizableTraceInterceptor customizableTraceInterceptor = new CustomizableTraceInterceptor();
customizableTraceInterceptor.setUseDynamicLogger(true);
customizableTraceInterceptor.setExitMessage("Executed $[methodName] in $[invocationTime]");
return customizableTraceInterceptor;
}
#Bean
public Advisor advisor() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(public * org.springframework.data.repository.CrudRepository+.*(..))");
return new DefaultPointcutAdvisor(pointcut, customizableTraceInterceptor());
}
}
Another working solution:
#Aspect
#Component
public class ProfilerAspect {
Logger logger = LoggerFactory.getLogger(this.getClass());
#Pointcut("execution(public * org.springframework.data.repository.Repository+.*(..))")
public void monitor() {}
#Around("monitor()")
public Object profile(ProceedingJoinPoint pjp) {
long start = System.currentTimeMillis();
logger.debug("JVM memory in use = "+ (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()));
Object output = null;
try {
output = pjp.proceed();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
long elapsedTime = System.currentTimeMillis() - start;
logger.debug(pjp.getTarget()+"."+pjp.getSignature()+": Execution time: " + elapsedTime + " ms. ("+ elapsedTime/60000 + " minutes)");
return output;
}
}
So I am testing a simple Google Guice interceptor -
My Annotation -
#Retention(RetentionPolicy.RUNTIME) #Target(ElementType.METHOD)
public #interface AppOpsOperation {
}
My Interceptor
public class AppOpsOperationDecorator implements MethodInterceptor {
private ServiceCallStack callStack = null ;
#Inject
public void setServiceCallStack(ServiceCallStack stack ){
callStack = stack ;
}
#Override
public Object invoke(MethodInvocation arg0) throws Throwable {
// Retrieve the call stack
// exclude service population if caller service is the same service
// else push the current service onto top of stack
System.out.println("Intercepting method -- :: " + arg0.getMethod().getName());
System.out.println("On object - :: " + arg0.getThis().getClass().getName());
System.out.println("On accessible object - :: " + arg0.getStaticPart().getClass().getName());
return invocation.proceed();
}
}
And now my Service interface and method
public interface MockCalledService extends AppOpsService {
#AppOpsOperation
public String methodOneCalled(String some);
#AppOpsOperation
public String methodTwoCalled(String some);
}
public class MockCalledServiceImpl extends BaseAppOpsService implements MockCalledService {
#Override
#AppOpsOperation
public String methodOneCalled(String some) {
System.out.println("MockCalledServiceImpl.methodOneCalled()");
return this.getClass().getCanonicalName() + "methodOneCalled";
}
#Override
public String methodTwoCalled(String some) {
System.out.println("MockCalledServiceImpl.methodTwoCalled()");
return this.getClass().getCanonicalName() + "methodTwoCalled";
}
}
And my Guice test module
public class MockTestGuiceModule extends AbstractModule {
#Override
protected void configure() {
bind(ServiceCallStack.class).toInstance(new ServiceCallStack());
AppOpsOperationDecorator decorator = new AppOpsOperationDecorator() ;
requestInjection(decorator);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(AppOpsOperation.class),
decorator);
bind(MockCalledService.class).toInstance(new MockCalledServiceImpl());
}
}
This interceptor doesn't execute when I run the test below -
public class AppOpsOperationDecoratorTest {
private Injector injector = null ;
#Before
public void init(){
injector = Guice.createInjector(new MockTestGuiceModule());
}
#Test
public void testDecoratorInvocation() {
MockCalledService called = injector.getInstance(MockCalledService.class);
called.methodOneCalled("Test String");
}
}
Can you please highlight what I am doing wrong ?
I am answering after finding the real reason. Its so simple that its really tricky.
Method interception only works if you bind the interface with the class and not an instance of this implementation.
so instead of bind(MockCalledService.class).toInstance(new MockCalledServiceImpl());
we should write bind(MockCalledService.class).to(MockCalledServiceImpl.class);
Seems instances are not proxied :(
I want to monitor all public methods of all Classes with specified annotation (say #Monitor) (note: Annotation is at class level). What could be a possible pointcut for this?
Note: I am using #AspectJ style Spring AOP.
You should combine a type pointcut with a method pointcut.
These pointcuts will do the work to find all public methods inside a class marked with an #Monitor annotation:
#Pointcut("within(#org.rejeev.Monitor *)")
public void beanAnnotatedWithMonitor() {}
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Pointcut("publicMethod() && beanAnnotatedWithMonitor()")
public void publicMethodInsideAClassMarkedWithAtMonitor() {}
Advice the last pointcut that combines the first two and you're done!
If you're interested, I have written a cheat sheet with #AspectJ style here with a corresponding example document here.
Using annotations, as described in the question.
Annotation: #Monitor
Annotation on class, app/PagesController.java:
package app;
#Controller
#Monitor
public class PagesController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public #ResponseBody String home() {
return "w00t!";
}
}
Annotation on method, app/PagesController.java:
package app;
#Controller
public class PagesController {
#Monitor
#RequestMapping(value = "/", method = RequestMethod.GET)
public #ResponseBody String home() {
return "w00t!";
}
}
Custom annotation, app/Monitor.java:
package app;
#Component
#Target(value = {ElementType.METHOD, ElementType.TYPE})
#Retention(value = RetentionPolicy.RUNTIME)
public #interface Monitor {
}
Aspect for annotation, app/MonitorAspect.java:
package app;
#Component
#Aspect
public class MonitorAspect {
#Before(value = "#within(app.Monitor) || #annotation(app.Monitor)")
public void before(JoinPoint joinPoint) throws Throwable {
LogFactory.getLog(MonitorAspect.class).info("monitor.before, class: " + joinPoint.getSignature().getDeclaringType().getSimpleName() + ", method: " + joinPoint.getSignature().getName());
}
#After(value = "#within(app.Monitor) || #annotation(app.Monitor)")
public void after(JoinPoint joinPoint) throws Throwable {
LogFactory.getLog(MonitorAspect.class).info("monitor.after, class: " + joinPoint.getSignature().getDeclaringType().getSimpleName() + ", method: " + joinPoint.getSignature().getName());
}
}
Enable AspectJ, servlet-context.xml:
<aop:aspectj-autoproxy />
Include AspectJ libraries, pom.xml:
<artifactId>spring-aop</artifactId>
<artifactId>aspectjrt</artifactId>
<artifactId>aspectjweaver</artifactId>
<artifactId>cglib</artifactId>
Something like that:
#Before("execution(* com.yourpackage..*.*(..))")
public void monitor(JoinPoint jp) {
if (jp.getTarget().getClass().isAnnotationPresent(Monitor.class)) {
// perform the monitoring actions
}
}
Note that you must not have any other advice on the same class before this one, because the annotations will be lost after proxying.
Use
#Before("execution(* (#YourAnnotationAtClassLevel *).*(..))")
public void beforeYourAnnotation(JoinPoint proceedingJoinPoint) throws Throwable {
}
it should be enough to mark your aspect method like this:
#After("#annotation(com.marcot.CommitTransaction)")
public void after() {
have a look at this for a step by step guide on this.
You can also define the pointcut as
public pointcut publicMethodInsideAClassMarkedWithAtMonitor() : execution(public * (#Monitor *).*(..));
The simplest way seems to be :
#Around("execution(#MyHandling * com.exemple.YourService.*(..))")
public Object aroundServiceMethodAdvice(final ProceedingJoinPoint pjp)
throws Throwable {
// perform actions before
return pjp.proceed();
// perform actions after
}
It will intercept execution of all methods specifically annotated with '#MyHandling' in 'YourService' class. To intercept all methods without exception, just put the annotation directly on the class.
No matter of the private / public scope here, but keep in mind that spring-aop cannot use aspect for method calls in same instance (typically private ones), because it doesn't use the proxy class in this case.
We use #Around advice here, but it's basically the same syntax with #Before, #After or any advice.
By the way, #MyHandling annotation must be configured like this :
#Retention(RetentionPolicy.RUNTIME)
#Target( { ElementType.METHOD, ElementType.TYPE })
public #interface MyHandling {
}
I share with you a code that can be useful, it is to create an annotation that can be used either in a class or a method.
#Target({TYPE, METHOD})
#Retention(RUNTIME)
#Documented
public #interface AnnotationLogger {
/**
* It is the parameter is to show arguments in the method or the class.
*/
boolean showArguments() default false;
}
#Aspect
#Component
public class AnnotationLoggerAspect {
#Autowired
private Logger logger;
private static final String METHOD_NAME = "METHOD NAME: {} ";
private static final String ARGUMENTS = "ARGS: {} ";
#Before(value = "#within(com.org.example.annotations.AnnotationLogger) || #annotation(com.org.example.annotations.AnnotationLogger)")
public void logAdviceExecutionBefore(JoinPoint joinPoint){
CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature();
AnnotationLogger annotationLogger = getAnnotationLogger(joinPoint);
if(annotationLogger!= null) {
StringBuilder annotationLoggerFormat = new StringBuilder();
List<Object> annotationLoggerArguments = new ArrayList<>();
annotationLoggerFormat.append(METHOD_NAME);
annotationLoggerArguments.add(codeSignature.getName());
if (annotationLogger.showArguments()) {
annotationLoggerFormat.append(ARGUMENTS);
List<?> argumentList = Arrays.asList(joinPoint.getArgs());
annotationLoggerArguments.add(argumentList.toString());
}
logger.error(annotationLoggerFormat.toString(), annotationLoggerArguments.toArray());
}
}
private AnnotationLogger getAnnotationLogger(JoinPoint joinPoint) {
AnnotationLogger annotationLogger = null;
try {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = joinPoint.getTarget().getClass().
getMethod(signature.getMethod().getName(), signature.getMethod().getParameterTypes());
if (method.isAnnotationPresent(AnnotationLogger.class)){
annotationLogger = method.getAnnotation(AnnotationLoggerAspect.class);
}else if (joinPoint.getTarget().getClass().isAnnotationPresent(AnnotationLoggerAspect.class)){
annotationLogger = joinPoint.getTarget().getClass().getAnnotation(AnnotationLoggerAspect.class);
}
return annotationLogger;
}catch(Exception e) {
return annotationLogger;
}
}
}
From Spring's AnnotationTransactionAspect:
/**
* Matches the execution of any public method in a type with the Transactional
* annotation, or any subtype of a type with the Transactional annotation.
*/
private pointcut executionOfAnyPublicMethodInAtTransactionalType() :
execution(public * ((#Transactional *)+).*(..)) && within(#Transactional *);
You could use Spring's PerformanceMonitoringInterceptor and programmatically register the advice using a beanpostprocessor.
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#Documented
public #interface Monitorable
{
}
public class PerformanceMonitorBeanPostProcessor extends ProxyConfig implements BeanPostProcessor, BeanClassLoaderAware, Ordered,
InitializingBean
{
private Class<? extends Annotation> annotationType = Monitorable.class;
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private Advisor advisor;
public void setBeanClassLoader(ClassLoader classLoader)
{
this.beanClassLoader = classLoader;
}
public int getOrder()
{
return LOWEST_PRECEDENCE;
}
public void afterPropertiesSet()
{
Pointcut pointcut = new AnnotationMatchingPointcut(this.annotationType, true);
Advice advice = getInterceptor();
this.advisor = new DefaultPointcutAdvisor(pointcut, advice);
}
private Advice getInterceptor()
{
return new PerformanceMonitoringInterceptor();
}
public Object postProcessBeforeInitialization(Object bean, String beanName)
{
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName)
{
if(bean instanceof AopInfrastructureBean)
{
return bean;
}
Class<?> targetClass = AopUtils.getTargetClass(bean);
if(AopUtils.canApply(this.advisor, targetClass))
{
if(bean instanceof Advised)
{
((Advised)bean).addAdvisor(this.advisor);
return bean;
}
else
{
ProxyFactory proxyFactory = new ProxyFactory(bean);
proxyFactory.copyFrom(this);
proxyFactory.addAdvisor(this.advisor);
return proxyFactory.getProxy(this.beanClassLoader);
}
}
else
{
return bean;
}
}
}