I have my own Spring Boot application and want to create my annotation, which will check incoming data to null
.
When I created the @Aspect
annotated class, all other beans became null
, what should I do?
@Aspect
class code:
@Aspect
@Component
public class EnableRestCallLogAspect {
@Around("@annotation(EnableRestCallLogs)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long initTime = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - initTime;
System.out.println("============================================================================================================");
System.out.println("Method Signature is : "+joinPoint.getSignature() );
System.out.println("Method executed in : " + executionTime + "ms");
System.out.println("Input Request: " + joinPoint.getArgs()[0]);
System.out.println("Output Response : " + proceed);
return proceed;
}
}
Annotation EnableRestCallLogs
is annotated only in methods and in PostMapping
methods:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableRestCallLogs {
}
Actually, the question is quite interesting, so I want to explain the situation with a demo. Before talking about this problem, please first read and try to understand my answer to "Why does self-invocation not work for Spring proxies (e.g. with AOP)?". The main take-away it the fact that Spring proxies use a delegation pattern.
Next, let us talk about auto-wiring: Spring injects the values into the original instance, i.e. the proxy's corresponding field values remain null
, because the proxy's sole purpose is to intercept methods, possibly
An entity and its auto-wired dependency:
package de.scrum_master.spring.q77134178;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MyEntity {
@Autowired
private MyDependency myDependency;
public void canOverridePublicNonFinal() {
System.out.println("canOverridePublicNonFinal");
System.out.println(" class = " + this.getClass().getName());
System.out.println(" dependency = " + myDependency);
cannotOverridePrivate();
}
private void cannotOverridePrivate() {
System.out.println("cannotOverridePrivate");
System.out.println(" class = " + this.getClass().getName());
System.out.println(" dependency = " + myDependency);
}
public final void cannotOverrideFinal() {
System.out.println("cannotOverrideFinal");
System.out.println(" class = " + this.getClass().getName());
System.out.println(" dependency = " + myDependency);
}
}
package de.scrum_master.spring.q77134178;
import org.springframework.stereotype.Component;
@Component
public class MyDependency {}
An aspect intercepting entity methods:
Why do we need an aspect? Because
package de.scrum_master.spring.q77134178;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspect {
@Before("execution(* MyEntity.*(..))")
public void myAdvice(JoinPoint joinPoint) {
System.out.println(joinPoint);
}
}
A driver application:
package de.scrum_master.spring.q77134178;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@Configuration
@EnableAspectJAutoProxy
public class DemoApplication {
public static void main(String[] args) {
try (ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args)) {
MyEntity myEntity = context.getBean(MyEntity.class);
myEntity.canOverridePublicNonFinal();
myEntity.cannotOverrideFinal();
}
}
}
Console log:
execution(void de.scrum_master.spring.q77134178.MyEntity.canOverridePublicNonFinal())
canOverridePublicNonFinal
class = de.scrum_master.spring.q77134178.MyEntity
dependency = de.scrum_master.spring.q77134178.MyDependency@6c24f61d
cannotOverridePrivate
class = de.scrum_master.spring.q77134178.MyEntity
dependency = de.scrum_master.spring.q77134178.MyDependency@6c24f61d
cannotOverrideFinal
class = de.scrum_master.spring.q77134178.MyEntity$$EnhancerBySpringCGLIB$$e1109d0e
dependency = null
Please note:
The aspect is only triggered once for method canOverridePublicNonFinal()
, because it is the only entity method which is neither final nor private. This is easy to understand, because in Java private or final methods cannot be overridden. Therefore, they cannot be proxied either.
The public, non-final method can "see" the injected dependency, because the proxy delegates to the original method, hence this.myDependency
or simply myDependency
refers to the auto-wired dependency.
The private method also can "see" the injected dependency, because the method only exists in the original class and no proxying is going on there, hence this.myDependency
or simply myDependency
refers to the auto-wired dependency.
The final method, however, both is callable from outside, i.e. callable on the proxy instance, and cannot be proxied, which means that this.myDependency
or simply myDependency
refers to the proxy instance(!) itself, which finally yields null
when trying to access the field.
Here is a plain Java version (no Spring or dynamic proxies), emulating the behaviour in POJO fashion in order to hopefully make it even clearer what goes on behind the curtains in Spring AOP.
Entity and injectable dependency:
You see, the classes are identical to the Spring example. I just removed @Component
and @Autowired
and added a setter method to enable dependency injection.
package de.scrum_master.app;
public class MyEntity {
private MyDependency myDependency;
public void setMyDependency(MyDependency myDependency) {
this.myDependency = myDependency;
}
public void canOverridePublicNonFinal() {
System.out.println("canOverridePublicNonFinal");
System.out.println(" class = " + this.getClass().getName());
System.out.println(" dependency = " + myDependency);
cannotOverridePrivate();
}
private void cannotOverridePrivate() {
System.out.println("cannotOverridePrivate");
System.out.println(" class = " + this.getClass().getName());
System.out.println(" dependency = " + myDependency);
}
public final void cannotOverrideFinal() {
System.out.println("cannotOverrideFinal");
System.out.println(" class = " + this.getClass().getName());
System.out.println(" dependency = " + myDependency);
}
}
package de.scrum_master.app;
public class MyDependency {}
Pseudo proxy class:
Here we see how delegation works in Spring AOP.
package de.scrum_master.app;
public class MyEntityProxy extends MyEntity {
private MyEntity delegate;
public MyEntityProxy(MyEntity delegate) {
this.delegate = delegate;
}
@Override
public void canOverridePublicNonFinal() {
System.out.println("MyEntityProxy.canOverridePublicNonFinal delegating to original method (emulating aspect or interceptor)");
delegate.canOverridePublicNonFinal();
}
}
Driver application:
Here, we emulate dependency injection by simply wiring the dependency into the entity manually via setter call.
package de.scrum_master.app;
public class DemoApplication {
public static void main(String[] args) {
// Create entity
MyEntity myEntity = new MyEntity();
// Pseudo "auto-wire" a value into the entity
myEntity.setMyDependency(new MyDependency());
// Create pseudo "dynamic proxy" for entity
MyEntityProxy myEntityProxy = new MyEntityProxy(myEntity);
// Call proxy method overriding and delegating to original entity parent method
myEntityProxy.canOverridePublicNonFinal();
// Call parent method (final -> cannot be overridden and therefore not intercepted by the proxy)
myEntityProxy.cannotOverrideFinal();
}
}
Console log:
Unsurprisingly, the console log looks just like for the Spring AOP example, only the proxy class has a less cryptic name, because we manually created a pseudo proxy instead of relying on a CGLIB proxy.
MyEntityProxy.canOverridePublicNonFinal delegating to original method (emulating aspect or interceptor)
canOverridePublicNonFinal
class = de.scrum_master.app.MyEntity
dependency = de.scrum_master.app.MyDependency@4554617c
cannotOverridePrivate
class = de.scrum_master.app.MyEntity
dependency = de.scrum_master.app.MyDependency@4554617c
cannotOverrideFinal
class = de.scrum_master.app.MyEntityProxy
dependency = null