在非组件对象上使用弹簧@Value

2022-09-04 04:05:24

我遇到了这个问题,我不知道如何解决。我使用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;
}

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

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


编辑

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

无论如何,做这样的事情会是一个更好的方法,而不是康斯坦丁的建议?

public class UserDomain {

    @Autowired
    private Environment environment;

    private Boolean enabled;

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

}

答案 1

如果您的映射器具有采用已准备好的实例而不是 的方法,则可以添加原型范围的 Bean 并从控制器方法进行调用。ClassUserDomaincontext.getBean()

控制器

...

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

...

弹簧配置

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

    ...
}

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

啪.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>

...

用户域.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;

    ...
}

弹簧配置

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

    ...
}

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


编辑

关于您的编辑。

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

仍然可以使用环境应用程序上下文Aware来制定更好的解决方案,而无需将域对象绑定到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;
    }
}

用户域.java

public class UserDomain {

    private Boolean enabled;

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

    ...
}

弹簧配置

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

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

    ...
}

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


答案 2

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

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

我只是注意到从DTO到域类的转换正在控制器中进行。

定义

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

在控制器内部,然后

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

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


推荐