Spring dependency injection not managed beans
I have a JPA domain class that is not managed. It is created using the operator new
.
UserAccount account = new UserAccount();
userRepository.save(account)
In my class UserAccount
, I have a method beforeSave()
that depends on my SecurityService
password hash encoding.
My questions are, "How do I get spring DI to inject security into my entity?". It seems that AspectJ and LoadTimeWeaving are what I need. I've tried an array for configs, but I can't seem to get them to work. I always get NullPointerException
when I try to call a method on a nested object.
UserAccount.java (This is a JPA Object)
@Entity
@Repository
@Configurable(autowire = Autowire.BY_TYPE)
public class UserAccount implements Serializable {
@Transient
@Autowired
SecurityService securityService;
private String passwordHash;
@Transient
private String password;
public UserAccount() {
super();
}
@PrePersist
public void beforeSave() {
if (password != null) {
// NullPointerException Here!!!
passwordHash = securityService.hashPassword(password);
}
}
}
Trying to tell spring to use AspectJ:
NitroApp.java (Main class)
@SpringBootApplication
@EnableTransactionManagement
@EnableSpringConfigured
@PropertySources(value = {@PropertySource("classpath:application.properties")})
public class NitroApp extends SpringBootServletInitializer {
public static void main (String[] args) {
SpringApplication.run(NitroApp.class);
}
}
build.gradle (config)
buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.2.2.RELEASE"
classpath "org.springframework:springloaded:1.2.2.RELEASE"
classpath "org.springframework:spring-aspects:4.1.6.RELEASE"
}
}
apply plugin: 'java'
apply plugin: 'aspectj'
apply plugin: 'application'
apply plugin: 'idea'
apply plugin: 'spring-boot'
repositories {
jcenter()
mavenLocal()
mavenCentral()
}
mainClassName = 'com.noxgroup.nitro.NitroApp'
applicationName = "Nitro"
idea {
module {
inheritOutputDirs = false
outputDir = file("$buildDir/classes/main/")
}
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-thymeleaf")
compile("org.springframework.boot:spring-boot-starter-actuator")
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile("net.sourceforge.nekohtml:nekohtml:1.9.15")
compile("commons-codec:commons-codec:1.9")
compile("org.postgresql:postgresql:9.4-1201-jdbc41")
}
task wrapper(type: Wrapper) {
gradleVersion = '2.3'
}
source to share
You can inject Spring applicationContext into the class used to start UserAccount.
@Autowired
private ApplicationContext applicationContext;
Then create your UserAccount bean like this:
UserAccount userAccount = applicationContext.getBean(UserAccount.class);
This way you can inject the required dependencies in the UserAccount class.
source to share
From your configuration, I am assuming that you somehow expect Spring to manage AOP for you. However, since you are looking @Autowired
for a non-managed bean, you will need to weave yourself either by slicing the load times or by compiling the command. Spring will support default method level parameters.
Since load time over time is related to the use of javaagent as described in 9.8.4 (not always practical in a production scenario), I got ahead of myself and use compile time. The following code and configuration works for me.
Boot configuration
@SpringBootApplication
@EnableSpringConfigured
public class App {
public static void main(String[] args) {
System.out.println("Hello World!");
ApplicationContext ctx = SpringApplication.run(App.class, args);
Account account = new Account();
account.testConfigurable();
}
}
Account.java
@Configurable(autowire = Autowire.BY_TYPE, dependencyCheck = true)
public class Account {
@Autowired
private SpringService service;
public void testConfigurable() {
System.out.println(service.returnHello());
}
}
SpringService.java
@Service
public class SpringService {
public String returnHello() {
return "Hello";
}
}
Ugly pom.xml
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<showWeaveInfo>true</showWeaveInfo>
<source>1.8</source>
<target>1.8</target>
<Xlint>ignore</Xlint>
<complianceLevel>1.8</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>1.8.5</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.8.5</version>
</dependency>
</dependencies>
</plugin>
Below are the links I link to.
Since I am not an expert in AOP, I am not sure if you are facing the problem of configuring AOP above in the usual way. Discussion here . If you require load times in time, you should go ahead and use that as described in the answer.
source to share