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
@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.
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)?
It's not the same answer, since my goal is get it done using @Value annotations.
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
}
}
If your mapper has a method that takes already prepared instance instead of Class
, then you can add the prototype-scoped UserDomain
bean and call context.getBean()
from the controller method.
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 @Configurable
and 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.
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
@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
@EnableSpringConfigured is the same as <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.
Regarding your edit.
Please note that you use @Autowired
there. It means that UserDomain
instances 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 UserDomain
bean to your context. Effectively, it means that the proposed approach is similar to the @Value
-associated approach, except that it ties your UserDomain
to Spring Environment
. Therefore, it is bad.
It is still possible to craft a better solution using Environment
and ApplicationContextAware
without tying your domain objects to 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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With