After adding Spring AOP to my Spring Boot project, the following aspect produces a NullPointerException on an autowired service component in my controllers:
@Aspect
@Component
@Slf4j
public class LogRequestAspect {
@Around("@annotation(org.springframework.web.bind.annotation.RequestMapping) && execution(public * *(..))")
public Object log(final ProceedingJoinPoint joinPoint) throws Throwable {
final HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.currentRequestAttributes())
.getRequest();
final Object proceed = joinPoint.proceed();
log.info(
"{} {} from {},{}",
request.getMethod(),
request.getRequestURI(),
request.getRemoteAddr(),
request.getHeader("X-Forwarded-For"));
return proceed;
}
}
Example controller:
@RestController
public class AController {
@Autowired
AService aService;
@RequestMapping("/doSomething")
private List<Map<String, Object>> doSomething() {
return aService.doSomething();
}
}
Example service:
@Service
public class AService {
public List<Map<String, Object>> doSomething() {
List<Map<String, Object>> results = new ArrayList<>();
return results;
}
}
Example configuration:
@EnableAspectJAutoProxy
@SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String... strings) {
}
}
As soon as i remove the aspect, everything works perfectly. Any idea what i'm missing here?
Spring AOP, by default, works using proxies. IN this case a class based proxy is being used because no interface has been implemented. A class based proxy extends the actual class and overrides all the methods to apply the interceptors/aspects.
However a private
method cannot be overriden in a subclass and as such your controller method will be invoked on the proxy instead of the proxied object. The proxy never has anything injected and hence the aService
field is always null
on there.
To fix make the method public
or protected
so that a subclass can override the method and eventually the method will be called on the proxied instance instead of the proxy.