简体   繁体   English

Java Spring-如何将@Value注入数据对象?

[英]Java Spring - how to inject @Value to a data object?

I have a data object (with getters\\setter only) that needs to be aware of the Spring profile, ie 我有一个数据对象(仅带有getters \\ setter),需要了解Spring概要文件,即

@Value("${spring.profiles.active}")
private String profile;

I added a logic to one of it's 'set' method that checks the profile, ie 我在检查配置文件的“设置”方法之一中添加了逻辑,即

public void setItem(Item msg) {
    if (environmentProperties.isDevMode()) {
        this.msg= msg;
    }
}

since this class is often marshal\\unmarhsalled externally, so, of course the @Value isn't being populated - sine I didn't use spring Autowire to create the class instance... I tried defined the class as component, and autowire to an external class that holds the profile @Value - but it doesn't work I use spring 3.2 - with no XML definition. 由于此类通常是从外部编组的,所以@Value当然不会被填充-正弦,我没有使用spring Autowire来创建类实例...我试图将类定义为组件,并自动将其定义为组件一个拥有配置文件@Value的外部类-但我使用spring 3.2不能正常工作-没有XML定义。

any suggestions? 有什么建议么?

btw that data-objects often wrapped inside an exception class - so when it's created the profile should also be known to the data-object... 顺便说一句,数据对象通常包裹在一个异常类中-因此,在创建它时,数据对象也应该知道概要文件...

thanks! 谢谢!

EDITED: 编辑:

  • using ApplicationContextAware doesn't work - I get null the 'setApplicationContext' method is never invoked. 使用ApplicationContextAware不起作用-我得到null,永远不会调用'setApplicationContext'方法。
  • also trying to get context directly doesn't work - get null instead when using: 'ApplicationContext ctx = ContextLoader.getCurrentWebApplicationContext();' 也尝试直接获取上下文不起作用-使用以下方法获取null:'ApplicationContext ctx = ContextLoader.getCurrentWebApplicationContext();'

FIXED: I've eventually found an example how to access the context staticly from an external class: 修正:我最终找到了一个示例,该示例如何从外部类静态访问上下文:

@Configuration
public class ApplicationContextContainer implements ApplicationContextAware {

    private static ApplicationContext CONTEXT;

    /**
     * This method is called from within the ApplicationContext once it is
     * done starting up, it will stick a reference to itself into this bean.
     *
     * @param context a reference to the ApplicationContext.
     */
    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        CONTEXT = context;
    }

    /**
     * This is about the same as context.getBean("beanName"), except it has its
     * own static handle to the Spring context, so calling this method statically
     * will give access to the beans by name in the Spring application context.
     * As in the context.getBean("beanName") call, the caller must cast to the
     * appropriate target class. If the bean does not exist, then a Runtime error
     * will be thrown.
     *
     * @param beanName the name of the bean to get.
     * @return an Object reference to the named bean.
     */
    public static Object getBean(String beanName) {
        return CONTEXT.getBean(beanName);
    }

If I understand you correctly you want to inject into Objects not managed by Spring, but created by some other code that internally calls new and returns objects eg a serialization framework. 如果我理解正确,那么您想注入不是由Spring管理的对象,而是由内部调用new并返回对象的其他一些代码创建的对象,例如序列化框架。

To inject unmanaged Objects you will need to configure either load-time or compile-time weaving. 要注入非托管对象,您将需要配置加载时编织或编译时编织。 Load-time weaving requires an agent argument and lib when you start your VM, some containers might do this for you. 加载时编织在启动VM时需要一个agent参数和lib,某些容器可能会为您执行此操作。

Compile-time weaving requires the use of the AspectJ compiler. 编译时编织需要使用AspectJ编译器。

Below you will find a complete example using Maven and Spring-Boot: 在下面,您将找到使用Maven和Spring-Boot的完整示例:

Eg run it with: 例如,使用以下命令运行它:

mvn spring-boot:run -Drun.arguments="--spring.profiles.active=dev"

DemoApplication.java: DemoApplication.java:

package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.aspectj.EnableSpringConfigured;
import org.springframework.stereotype.Component;

@SpringBootApplication
public class DemoApplication {
    @EnableSpringConfigured
    @ComponentScan("com.example")
    public static class AppConfiguration {
        @Value("${spring.profiles.active}")
        String profile;

        @Bean
        public String profile() {
            return profile;
        }
    }

    @Configurable
    public static class SomePojo {
        @Autowired
        private String profile;

        public void print() {
            System.out.println(this + "\t" + profile);
        }
    }

    @Component
    public static class Runner {
        public void run() {
            new SomePojo().print();
            new SomePojo().print();
        }
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args).getBean(Runner.class).run();
    }

}

pom.xml: pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.3.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.8</version>
                <configuration>
                    <complianceLevel>1.8</complianceLevel>
                    <aspectLibraries>
                        <aspectLibrary>
                            <groupId>org.springframework</groupId>
                            <artifactId>spring-aspects</artifactId>
                        </aspectLibrary>
                    </aspectLibraries>
                </configuration>
                <executions>
                    <execution>
                        <id>compile</id>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

From your description, you're trying to inject the property into POJO, which is used in marshalling. 根据您的描述,您尝试将属性注入到POJO中,以用于编组。 With this structure you may look for different workarounds with static-based/any other complex solutions. 使用这种结构,您可能会寻找基于静态/任何其他复杂解决方案的不同解决方法。

I'd suggest to separate the bean which is used as POJO from the logic which depends on the property value. 我建议将用作POJO的bean与取决于属性值的逻辑分开。 You can extract that logic to BeanService (which can be placed to Spring context) and handle it on that level, so that you separate the responsibility between Service and Data layers. 您可以将该逻辑提取到BeanService(可以将其放置在Spring上下文中)并在该级别上进行处理,以便在Service和Data层之间划分职责。

You're doing it wrong. 你这样做是错的。 Your code does not need to be aware of the profile. 您的代码不需要知道配置文件。 In your example, create a Message interface, and a number of bean implementations of this interface, one for each profile, each containing an appropriate message for that profile, and assign each one to a profile so that the bean is instantiated for that profile, and inject the instance into the class that needs the message. 在您的示例中,创建一个Message接口以及该接口的许多Bean实现,每个配置文件一个,每个都包含该配置文件的相应消息,然后将每个分配给一个配置文件,以便为该配置文件实例化Bean,并将实例注入到需要消息的类中。

So, 所以,

public interface Message { String getMessage(); }

@Profile("dev") @Component
public class DevMessage implements Message { 
  public String getMessage() { return "this is the dev message"; }
}

@Profile("prod") @Component
public class ProdMessage implements Message {
  public String getMessage() { return "this is the production message"; }
}

If you prefer to describe your beans in your @Configuration class, you can mark a whole configuration with an @Profile, and have multiple configurations. 如果您希望在@Configuration类中描述bean,则可以使用@Profile标记整个配置,并具有多个配置。

If you inject the Message instance into a class, you can call getMessage() on it. 如果将Message实例注入到类中,则可以在其上调用getMessage()。 The profile will ensure that you have the appropriate implementation for your environment. 该配置文件将确保您具有适合您的环境的实施。

Edit: I've just reread your question and realised that I've got this wrong. 编辑:我刚刚重读了您的问题,意识到我错了。 You have entity objects stored outside the application and instantiated through some code/framework. 您将实体对象存储在应用程序外部,并通过一些代码/框架进行实例化。 These aren't spring components, and so can't use the spring approach to dependency injection. 这些不是spring组件,因此不能使用spring方法进行依赖项注入。 In this case, don't use spring for them -- it doesn't work, doesn't have to work, and shouldn't work. 在这种情况下,请不要对它们使用spring-它不起作用,不必起作用,也不应该起作用。 If you haven't instantiated the object through spring, then it should have nothing to do with spring. 如果您没有通过spring实例化对象,那么它应该与spring无关。 I don't know your problem domain, but I've been using spring since it was invented and have never ever had to do this. 我不知道您的问题领域,但是自从Spring被发明以来,我就一直在使用它,并且从来没有这样做。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM