I want to use @Autowired annotation into an "Aspect". I want to inject a repository in my aspect but when I try to call a method of my autowired class a NullPointException occurs.
@Aspect
public class AspectSecurity {
@Autowired
private UserRepository userRepository;
@After("execution(public * dash.*.*Controller.*(..))")
public void authentication(JoinPoint jp) throws UnauthorizedException {
System.out.println("SECURITY !");
System.out.println(userRepository.findAll().toString());
}
}
I have already tried to add @Component
above my aspect Class but I have the same error.
If I don't use an aspect class but a @Controller
I can call my repository without problems.
Some documentation speak about spring configuration with xml files but with spring boot I don't have these files.
Here a part of my pom.xml which calls the aspectJ plugin:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.4</version>
<configuration>
<showWeaveInfo>true</showWeaveInfo>
<source>1.6</source>
<target>1.6</target>
<Xlint>ignore</Xlint>
<complianceLevel>${compiler.version}</complianceLevel>
<encoding>UTF-8</encoding>
<verbose>false</verbose>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
Here my Application class:
package dash;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Here the Controller class which the aspect is called:
package dash.user;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import dash.GenericController;
@Controller
@RequestMapping("/user")
public class UserController extends GenericController {
@Autowired
private UserRepository repository;
@RequestMapping("/findAll")
public @ResponseBody User create(
@RequestParam(value="login", required=true) String login,
@RequestParam(value="password", required=true) String password) {
System.out.println(login);
System.out.println(password);
System.out.println("Users found with findAll():");
System.out.println("-------------------------------");
for (User user : repository.findAll()) {
System.out.println(user);
}
return repository.findOne("root");
}
}
Note: I have already tried to add @EnableAspectJAutoProxy
above my application class.
It's quite tricky to set up AspectJ weaving so a few things could be wrong here. I would suggest that you do not use @Component
on your @Aspect
(or at least make sure it is excluded from a @ComponentScan
). The reason for that is you have to create a @Bean
of that type and explicitly use the same creation mechanism that AspectJ does, so that Spring and AspectJ agree on the value of the singleton instance. I believe the correct way to do that is to use the static convenience methods in Aspects
in your @Bean
definition. E.g.
@Bean
public AspectSecurity interceptor() {
AspectSecurity aspect = Aspects.aspectOf(AspectSecurity.class);
// ... inject dependencies here if not using @Autowired
return aspect;
}
In addition you will need an aop.xml
to ensure that the compiled aspect is on the AspectJ weaver path. It could be that is what you are doing with the Maven AspectJ plugin, but if it was me doing this I would probably just create an aop.xml
manually, use @EnableLoadTimeWeaving
, and ditch the plugin. You can probably decide yourself based on what works.
There can also be lifecycle issues if the aspect needs to intercept something that is used during the construction of the application context. You can maybe avoid that by not relying on any interception in @Bean
methods, or else you end up playing games with @DependsOn
to try and force a particular order of bean creation. Whether your app suffers from that yet I can't say.
PREVIOUSLY (obsolete with Spring Boot 1.3):
Another stumbling block is that you are using Spring Boot and @EnableAutoConfiguration
which explicitly switches on @EnableAspectJAutoProxy
, and that switches off the AspectJ weaving for the Spring bean aspects. I actually have no idea if that is an intended side effect of @EnableAspectJAutoProxy
, but you can disable it by excluding it from the autoconfig, e.g.
@ComponentScan
@EnableAutoConfiguration(exclude=AopAutoConfiguration.class)
public class Application {
...
}
N.B. you might not notice that weaving is switched off if you forget to exclude this config because Spring will create proxies for you and many of your aspects will just work anyway.