【问题标题】:Dependency Injection into Spring non-managed beans依赖注入到 Spring 非托管 bean
【发布时间】:2015-06-17 11:34:05
【问题描述】:

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

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

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

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

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);  
        }
    }
}

试图指示spring使用AspectJ:

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(配置)

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

【问题讨论】:

标签: java spring spring-boot aspectj spring-aspects


【解决方案1】:

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

@Autowired
private ApplicationContext applicationContext;

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

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

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

【讨论】:

  • 感谢您的回答,您是对的!话虽如此,我想继续使用new 运算符,因为有成千上万个实例我将实例化这些对象,我宁愿以直观的方式进行。我特别在寻找一种 AOP 方法。
【解决方案2】:

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

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

启动配置

@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";
    }

}

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

以下是我引用的链接。

  1. 9.8.1 在春季文档中
  2. 来自 here 的 Maven 配置。

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

【讨论】:

  • 我仍然在passwordHash = securityService.hashPassword(password); 上获得 NPE,请您详细说明一下。
  • 由于您正在寻找使用新运算符,请忽略我之前的回答。事实上,我重写了答案,并在使用 new 运算符时设法使依赖注入工作。
  • 我可以使用 aspectj 插件查看您的 gradle 脚本。那是为了编译时编织吗?万一答案似乎解释得太多了..我只想写下我是如何得出答案的,以便更清楚..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-14
  • 2012-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-23
  • 2014-06-28
相关资源
最近更新 更多