问题
I have a JPA domain class that is non managed. It is instantiated via the new
operator.
UserAccount account = new UserAccount();
userRepository.save(account)
In my UserAccount
class, I have a beforeSave()
method which is dependent on my SecurityService
to hash encode a password.
My questions is "How do I get spring DI to inject the security service into my entity?". Seems that AspectJ and LoadTimeWeaving is what I need. I've tried an array for configurations, but I can't seem to get any of them to work. I always get a NullPointerException
when trying to call a method on the injected object.
UserAccount.java (This is the JPA Entity)
@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 indicate to spring to use AspectJ:
NitroApp.java (The 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 (Configuration)
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'
}
回答1:
You can inject Spring applicationContext in the class used to instanciate UserAccount.
@Autowired
private ApplicationContext applicationContext;
Then, create your UserAccount bean this way :
UserAccount userAccount = applicationContext.getBean(UserAccount.class);
This way, you can inject your required dependencies in the UserAccount class.
回答2:
From your configuration, I am assuming that you are somehow expecting Spring to manage AOP for you. However since you are looking to @Autowired
on a non managed bean you will have to do the weaving yourself either through load time weaving or compile team weaving. Spring will only support method level aspects by default.
Because Load time weaving involves the use of javaagent as explained in 9.8.4(not always practical in a production scenario) I have gone ahead and used compile time weaving. Following code and config works for me.
Boot Config
@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>
Following are the links I refered to.
- 9.8.1 in spring doc
- Maven Config from here.
Since I am no expert on AOP, I am not sure of the knock on effect of configuring AOP the above way on ordinary aspect. A discussion here. If load time weaving is an option for you, you should go ahead and use this as discussed in the answer.
来源:https://stackoverflow.com/questions/30890380/dependency-injection-into-spring-non-managed-beans