This question is a follow up to the question here AspectJ @around a static initializer
I accepted that answer because it did apply the aspect to the code, but i hadn't actually run it yet to see there were still problems...
I have this aspect
package com.acme.aspects.exceptions;
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;
@Aspect
public class StaticInitializerExceptions {
private static final Logger LOGGER = LoggerFactory.getLogger(StaticInitializerExceptions.class);
@Around("staticinitialization(!is(InterfaceType)) && !within(MyAspect) && !within(com.acme.aspects.exceptions.StaticInitializerExceptions)")
public Object logStaticInitializerException(ProceedingJoinPoint joinPoint) throws Throwable {
try {
return joinPoint.proceed();
} catch (Throwable t) {
LOGGER.error("STATIC INITIALIZER EXCEPTION CAUGHT", t);
throw t;
}
}
}
In an attempt to find the source of a mysterious NoClassDefFoundError in an initializer.
It seemed to weave ok, however now practically ever static initializer in the program throws, because the method context from which the target clinit is run is from the aspect, it appears.
So say i have a class, such as
public class AcmeContextListener implements ServletContextListener {
private static final Logger LOGGER = LoggerFactory.getLogger(AcmeContextListener.class);
....
}
Then it's clinit will get weaved in such a way that where the LOGGER = LoggerFactory.getLogger(AcmeContextListener.class);
isn't called from the clinit, but from an aspect generated method.
Giving this error
<ri > <an > - STATIC INITIALIZER EXCEPTION CAUGHT
at com.acme.listener.AcmeContextListener.clinit$_aroundBody0(AcmeContextListener.java:20) ~[_servlets.jar:25.6.0.0]
at com.acme.listener.AcmeContextListener.clinit$_aroundBody1$advice(AcmeContextListener.java:17) ~[_servlets.jar:25.6.0.0]
java.lang.IllegalAccessError: Update to static final field com.acme.listener.AcmeContextListener.LOGGER attempted from a different method (clinit$_aroundBody0) than the initializer method <clinit>
at com.acme.listener.AcmeContextListener.<clinit>(AcmeContextListener.java:1) ~[_servlets.jar:25.6.0.0]
Is there anything that can be done here?
This is a special case. The around advice is not inlined but compiled into a helper method, and that one cannot set a final field, hence the error.
The solution is simple: Just do not use an @Around
advice. In your sample code, you do nothing before calling proceed()
anyway, so an @After
advice is sufficient, more exactly @AfterThrowing
, because you are only interested in joinpoints throwing exceptions. This also simplifies the advice code:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Aspect
public class StaticInitializerExceptions {
private static final Logger LOGGER = LoggerFactory.getLogger(StaticInitializerExceptions.class);
@AfterThrowing(pointcut = "staticinitialization(!is(InterfaceType)) && !within(StaticInitializerExceptions)", throwing = "t")
public void myAdvice(JoinPoint joinPoint, Throwable t) {
LOGGER.error("STATIC INITIALIZER EXCEPTION CAUGHT", t);
}
}
BTW, if you need to do something before joinpoint execution, simply add a @Before
advice. If you need to carry state from before to after advice, probably you need something like a thread-local field in the aspect.