java 在非组件对象上使用 Spring @Value

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31926575/
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 19:21:57  来源:igfitidea点击:

Use Spring @Value on non-component object

javaspringspring-mvc

提问by jscherman

I've had this issue that i didn't know how to resolve. I made my Restful API using Spring Boot, and i am implementing the DTO-Domain-Entity pattern, so on this particular case i have this controller's method

我遇到了这个问题,我不知道如何解决。我使用 Spring Boot 制作了我的 Restful API,我正在实现 DTO-Domain-Entity 模式,所以在这个特殊情况下我有这个控制器的方法

@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<UserResponseDTO> createUser(@RequestBody UserRequestDTO data) {
    UserDomain user = this.mapper.map(data, UserDomain.class);
    UserDomain createdUser = this.service.createUser(user);
    UserResponseDTO createdUserDTO = this.mapper.map(createdUser, UserResponseDTO.class);
    return new ResponseEntity<UserResponseDTO>(createdUserDTO, HttpStatus.CREATED);
}

public class UserDomain {

    private Long id;

    private Date createdDate;

    private Date updatedDate;

    private String username;

    private String password;

    @Value("${default.user.enabled:true}") // I have default-values.properties being loaded in another configuration file
    private Boolean enabled;
}

I am transforming UserRequestDTO object to UserDomain. As i understand, UserRequestDTO is a bean that is being injected. Then i am transforming this to UserDomain, the problem here is that UserDomain object is not a component, so enabled attribute will not take the default value.

我正在将 UserRequestDTO 对象转换为 UserDomain。据我了解, UserRequestDTO 是一个被注入的 bean。然后我将其转换为 UserDomain,这里的问题是 UserDomain 对象不是组件,因此启用的属性不会采用默认值。

In the case i wouldn't want to handle UserDomain as a bean, how could i make spring to load default values (just enabled attribute in this case)?

在我不想将 UserDomain 作为 bean 处理的情况下,我如何让 spring 加载默认值(在这种情况下只是启用属性)?



EDIT

编辑

It's not the same answer, since my goal is get it done using @Value annotations.

这不是相同的答案,因为我的目标是使用 @Value 注释完成它。

Anyways, Would it be a better way doing something like this instead Constantine suggested?

无论如何,用康斯坦丁建议的方式做这样的事情会更好吗?

public class UserDomain {

    @Autowired
    private Environment environment;

    private Boolean enabled;

    UserDomain(){
         this.enabled = environment.getProperty("default.user.enabled");
         // and all the other ones
    }

}

采纳答案by Constantine

If your mapper has a method that takes already prepared instance instead of Class, then you can add the prototype-scoped UserDomainbean and call context.getBean()from the controller method.

如果您的映射器有一个使用已经准备好的实例而不是 的方法Class,那么您可以添加原型范围的UserDomainbean 并context.getBean()从控制器方法调用。

Controller

控制器

...

@Autowired
private WebApplicationContext context;

@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<UserResponseDTO> createUser(@RequestBody UserRequestDTO data) {
    UserDomain user = this.mapper.map(data, getUserDomain());
    UserDomain createdUser = this.service.createUser(user);
    UserResponseDTO createdUserDTO = this.mapper.map(createdUser, UserResponseDTO.class);
    return new ResponseEntity<UserResponseDTO>(createdUserDTO, HttpStatus.CREATED);
}

private UserDomain getUserDomain() {
    return context.getBean(UserDomain.class);
}

...

Spring configuration

弹簧配置

@Configuration
public class Config {

    @Bean
    public static PropertySourcesPlaceholderConfigurer properties() {
        PropertySourcesPlaceholderConfigurer propConfigurer = new PropertySourcesPlaceholderConfigurer();
        propConfigurer.setLocation(new ClassPathResource("application.properties"));
        return propConfigurer;
    }

    @Bean
    @Scope("prototype")
    public UserDomain userDomain() {
        return new UserDomain();
    }

    ...
}


Otherwise, you can use @Configurableand AspectJ compile-time weaving. But you have to decide if it is worth to introduce weaving in your project, since you have other ways to handle the situation.

否则,您可以使用@Configurable和 AspectJ 编译时编织。但是您必须决定是否值得在您的项目中引入编织,因为您有其他方法来处理这种情况。

pom.xml

pom.xml

...

<!-- additional dependencies -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.2.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.6</version>
</dependency>

...

<!-- enable compile-time weaving with aspectj-maven-plugin -->
<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <version>1.7</version>
            <configuration>
                <complianceLevel>1.8</complianceLevel>
                <encoding>UTF-8</encoding>
                <aspectLibraries>
                    <aspectLibrary>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-aspects</artifactId>
                    </aspectLibrary>
                </aspectLibraries>
                <Xlint>warning</Xlint>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>test-compile</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

...

UserDomain.java

用户域.java

@Configurable
public class UserDomain {

    private Long id;

    private Date createdDate;

    private Date updatedDate;

    private String username;

    private String password;

    @Value("${default.user.enabled:true}")
    private Boolean enabled;

    ...
}

Spring configuration

弹簧配置

@EnableSpringConfiguredis the same as <context:spring-configured>.

@EnableSpringConfigured<context:spring-configured>.

@Configuration
@EnableSpringConfigured
public class Config {

    @Bean
    public static PropertySourcesPlaceholderConfigurer properties() {
        PropertySourcesPlaceholderConfigurer propConfigurer = new PropertySourcesPlaceholderConfigurer();
        propConfigurer.setLocation(new ClassPathResource("application.properties"));
        return propConfigurer;
    }

    ...
}

Please consult Spring documentation for more information on AspectJ and @Configurable.

有关AspectJ 和 @Configurable 的更多信息,请参阅 Spring 文档。



EDIT

编辑

Regarding your edit.

关于你的编辑。

Please note that you use @Autowiredthere. It means that UserDomaininstances have to be managed by the Spring container. The container is not aware about instances created outside of it, so @Autowired(exactly as @Value) will not be resolved for such instances, e.g. UserDomain userDomain = new UserDomain()or UserDomain.class.newInstance(). Thus, you still have to add a prototype-scoped UserDomainbean to your context. Effectively, it means that the proposed approach is similar to the @Value-associated approach, except that it ties your UserDomainto Spring Environment. Therefore, it is bad.

请注意,您@Autowired在那里使用。这意味着UserDomain实例必须由 Spring 容器管理。容器不知道在其外部创建的实例,因此@Autowired(完全一样@Value)不会为此类实例解析,例如UserDomain userDomain = new UserDomain()UserDomain.class.newInstance()。因此,您仍然必须将原型范围的UserDomainbean添加到您的上下文中。实际上,这意味着所提议的方法类似于@Value-related 方法,除了它将您UserDomain与 Spring联系起来Environment。因此,这是不好的。

It is still possible to craft a better solution using Environmentand ApplicationContextAwarewithout tying your domain objects to Spring.

仍然可以使用EnvironmentApplicationContextAware不将域对象绑定到 Spring来制定更好的解决方案。

ApplicationContextProvider.java

应用程序上下文提供程序

public class ApplicationContextProvider implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    public static <T> T getEnvironmentProperty(String key, Class<T> targetClass, T defaultValue) {
        if (key == null || targetClass == null) {
            throw new NullPointerException();
        }

        T value = null;
        if (applicationContext != null) {
            System.out.println(applicationContext.getEnvironment().getProperty(key));
            value = applicationContext.getEnvironment().getProperty(key, targetClass, defaultValue);
        }
        return value;
    }

    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
}

UserDomain.java

用户域.java

public class UserDomain {

    private Boolean enabled;

    public UserDomain() {
         this.enabled = ApplicationContextProvider.getEnvironmentProperty("default.user.enabled", Boolean.class, false);
    }

    ...
}

Spring configuration

弹簧配置

@Configuration
@PropertySource("classpath:application.properties")
public class Config {

    @Bean
    public ApplicationContextProvider applicationContextProvider() {
        return new ApplicationContextProvider();
    }

    ...
}

However, I do not like the additional complexity and sloppiness of this approach. I think it is not justified at all.

但是,我不喜欢这种方法的额外复杂性和草率。我认为这根本没有道理。

回答by Agustí Sánchez

Don't you have a service layer? Preferences, parameters, default values and so on should be injected into service classes which are the ones centralizing business logic and they should be managed by Spring.

你没有服务层吗?首选项、参数、默认值等应该注入到服务类中,这些类是集中业务逻辑的类,它们应该由 Spring 管理。

If you don't have a UserService, then load the default value into the controller.

如果您没有UserService,则将默认值加载到控制器中。

I just notice the conversion from DTO to the domain class is taking place in the controller.

我只是注意到从 DTO 到域类的转换发生在控制器中。

Define

定义

@Value("${default.user.enabled:true}")  
private Boolean defaultUserEnabled;

inside the controller and then

在控制器内部,然后

if (user.isEnabled() == null)
    user.setEnabled(defaultUserEnabled);

But, as I already said, both the declaration and the setting of the default value belong to a Spring-managed service class.

但是,正如我已经说过的,默认值的声明和设置都属于 Spring 管理的服务类。