Search code examples
javaspringspring-bootaopspring-aop

@Autowire returns null after adding Spring AOP


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?


Solution

  • 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.