java 对 Spring 非托管 bean 的依赖注入

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30890380/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 17:48:09  来源:igfitidea点击:

Dependency Injection into Spring non-managed beans

javaspringspring-bootaspectjspring-aspects

提问by sparkyspider

I have a JPA domain class that is non managed. It is instantiated via the newoperator.

我有一个非托管的 JPA 域类。它是通过new操作符实例化的。

UserAccount account = new UserAccount();
userRepository.save(account)

In my UserAccountclass, I have a beforeSave()method which is dependent on my SecurityServiceto hash encode a password.

在我的UserAccount课堂上,我有一个beforeSave()方法依赖于我SecurityService对密码进行哈希编码。

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 NullPointerExceptionwhen trying to call a method on the injected object.

我的问题是“如何让 spring DI 将安全服务注入到我的实体中?”。似乎 AspectJ 和 LoadTimeWeaving 是我需要的。我已经尝试了一个用于配置的数组,但我似乎无法让它们中的任何一个工作。NullPointerException尝试在注入的对象上调用方法时,我总是得到一个。

UserAccount.java(This is the JPA Entity)

UserAccount.java(这是 JPA 实体)

@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:

试图表明 spring 使用 AspectJ:

NitroApp.java(The main class)

NitroApp.java(主类)

@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)

build.gradle(配置)

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'
}

回答by Baptiste

You can inject Spring applicationContext in the class used to instanciate UserAccount.

您可以在用于实例化 UserAccount 的类中注入 Spring applicationContext。

@Autowired
private ApplicationContext applicationContext;

Then, create your UserAccount bean this way :

然后,以这种方式创建您的 UserAccount bean:

UserAccount userAccount = applicationContext.getBean(UserAccount.class);

This way, you can inject your required dependencies in the UserAccount class.

这样,您就可以在 UserAccount 类中注入所需的依赖项。

回答by ArunM

From your configuration, I am assuming that you are somehow expecting Spring to manage AOP for you. However since you are looking to @Autowiredon 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.

根据您的配置,我假设您以某种方式期望 Spring 为您管理 AOP。但是,由于您正在寻找@Autowired非托管 bean,因此您必须通过加载时编织或编译团队编织自己进行编织。Spring 默认只支持方法级别的方面。

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.

因为加载时编织涉及使用 javaagent,如9.8.4 中所述(在生产场景中并不总是实用),所以我继续使用编译时编织。以下代码和配置对我有用。

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

账号.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

SpringService.java

@Service
public class SpringService {

    public String returnHello() {
        return "Hello";
    }

}

Ugly pom.xml

丑陋的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.

以下是我参考的链接。

  1. 9.8.1in spring doc
  2. Maven Config from here.
  1. 9.8.1在 spring 文档中
  2. Maven 配置从这里

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.

由于我不是AOP方面的专家,我不确定按照上述方式配置AOP对普通方面的影响。这里有一个讨论。如果加载时间编织是您的一个选项,您应该继续使用它,如答案中所述。