Spring DI with dynamic constructor arguments - java

Most of my classes wired up look something like:
#Component
public class MyClassImpl implements MyClass {
private MyService service;
#Autowired
public MyClass(MyService service) {
this.service = service;
}
}
So that makes sense to me, but if I want to do something like this:
#Component
public class MyClassImpl implements MyClass {
private MyService service;
private String id; // this is what I need
#Autowired
public MyClass(MyService service, String id) {
this.service = service;
this.id = id;
}
}
But the problem is String id is not known until runtime. Is there any way to do this? From what I can tell, Spring checks all the dependencies by default at runtime so if I try the second example, it complains about the constructor arguments.
I've seen some examples where you can use a factory to create the actual value later down the line. Or I can create getters and setters in order to set the id when I need it, but that would also mean I'd need to add those getters and setters to the interface MyClass as well. Is there a cleaner way to do this?

You can create a #Bean method for id with logic needed to calculate it.
#Configuration
public class Config {
#Bean
public String idForService() {
return calculateId();
}
}
And then your service constructor will be look like this
#Autowired
public MyClass(MyService service, #Qualifier("idForService") String id) {
this.service = service;
this.id = id;
}
But as others have already mentioned it looks like a bad practice so you'd better consider to redesign your service class.

I am afraid what you are trying to do is not in the spirit of what Spring was created for. Please do have a look at: Runtime dependency injection with Spring
Having said that, there might be a workaround to your problem.
If you have some class that calculates that ID for you, just inject that class in a constructor and bind the result of its calculation to the field id in your class.

Related

My annotation #Value return null even it being used and called into component annotated classes

I'm using Spring and need some help:
I want to set one API key using application.properties instead of hardcoding it, but it always returns null. IntelliJ evaluates it correctly to the value I've set in the file.
I've already read other questions here and almost all solutions are saying that Spring can only "inject" those value anotations in managed classes, like Components, Beans, etc. That's what (think) I did and still got null!
Everything else is working as I intended. Any direction is appreciated!
My application.properties
api.someapiservice.key=08e...f
Class that uses the properties value:
#Component
public class ApiClient implements ApiClientInterface {
#Value("${api.someapiservice.key}")
private String API_KEY;
public ApiClient () {
System.out.println(API_KEY); //Returns null after spring log info: Initialized JPA EntityManagerFactory for persistence unit 'default'
...
}
Class that uses ApiClient:
#Component
public class SomeService {
private final SomeRepository someRepository;
private final ApiClient apiClient;
public PlaylistService(SomeRepository someRepository , ApiClient apiClient ) {
this.SomeRepository = SomeRepository;
this.apiClient = ApiClient;
}
Field injection can't possibly happen until after the instance is already constructed, so your #Value (or #Autowired) fields will always be null in the constructor. Move the #Value to a constructor parameter instead.
If you want to know what is the value of your #Value field on start up. You can use #PostConstruct annotation, or you can move #Value annotation on your class constructor.
private String API_KEY;
public ApiClient(#Value("${api.test.value}") String key) {
this.API_KEY= key;
System.out.println(this.API_KEY);
}
or using #PostConstruct Annotation
#Value("${api.someapiservice.key}")
private String API_KEY;
#PostConstruct
public void init() {
System.out.println(this.API_KEY);
}

Decide which of multiple implementations to use in Spring Boot app

Given two (or more) implementations of a particular service API, what's the best way to pick which one to use at runtime in my app based on an application property?
Example API:
public interface Greeting {
String sayHello(String username);
}
Implementations:
public class FriendlyGreeting implements Greeting {
public String sayHello(String username) {
return "Hello, " + username;
}
}
public class HostileGreeting implements Greeting {
public String sayHello(String username) {
return "Go away, " + username;
}
}
I've got a separate service class with an #Autowired constructor that takes an instance of Greeting. What I want, is based upon a configuration property, to decide which greeting implementation gets injected and used. I came up with using a configuration class to make that decision:
#Configuration
public class GreetingConfiguration {
private String selection;
#Autowired
public GreetingConfiguration(#Value("${greeting.type}") String type) {
this.selection = type;
}
#Bean
public Greeting provideGreeting() {
if ("friendly".equals(selection)) {
return new FriendlyGreeting();
} else {
return new HostileGreeting();
}
}
}
Is this the right way to do what I want? I went down the road of using #Qualifier on the implementations, and ended up with a mess where Spring saw 3 instances of my Greeting API, and I needed a configuration anyway to pick which implementation to use and return it with a unique qualifier name on it, and that feels worse than what I settled on.
You can mark both Greeting as #Service and select the chosen one with #Qualifier("yourServiceHere") like this:
#Autowired
#Qualifier("friendlyGreeting")
private Greeting greeting;
Another way you can do it is with profile. You can mark your FriendlyGreeting service with #Service and #Profile("friendly") and the HostileGreeting service with #Service and #Profile("hostileGreeting") and just put in the application.properties the following:
spring.profiles.active=friendly
Answering my own question.
#Compass and #user268396 were correct - using Profiles got this working as expected.
I created both implementations, annotated with #Service and #Profile("friendly") or #Profile("hostile"), and could change the property spring.profiles.active to dev,friendly for example, and get what I wanted.
You can use #Conditional annotations described at https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Conditional.html and https://reflectoring.io/spring-boot-conditionals/
#Profile annotations mentioned above are based upon #Conditional(from Spring Framework); see also Spring Boot: org.springframework.boot.autoconfigure.condition
Here is a full solution using ideas mentioned by David and Vitor above with #Profile and #Qualifer annotations.
Two beans with same name but Only one is activated based on which profile is defined.
#Profile("profile1")
#Bean("greeting")
public class FriendlyGreeting implements Greeting {
---
#Profile("profile2")
#Bean("greeting")
public class HostileGreeting implements Greeting {
---
#Configuration
public class GreetingConfiguration {
private Greeting greeting;
#Autowired
public GreetingConfiguration(#Qualifier("greeting") Greeting greeting) {
this.greeting = greeting;
}
}
Notes:
you can remove the intermediate class GreetingConfiguration and stick the "greeting" bean wherever you need
i prefer the #Autowired on the constructor instead of the class member to make it easier for unit testing.

When and how to instantiate a Spring Bean in my Rest Api

First of all, I'm a relative noob to Spring Boot, so keep that in mind.
I've got a REST api in which I'm trying to minimize database calls for the same object and I've determined that using a Spring Bean scoped to the Request is what I want. Assuming that is correct, here is what I'm trying to do:
1) Controller takes in a validated PhotoImportCommandDto command
PhotoCommandController
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> importPhoto(#Valid #RequestBody PhotoImportCommandDto command){
...
}
2) PhotoImportCommandDto is validated. Note the custom #UserExistsConstraint which validates that the user exists in the database by calling a service method.
PhotoImportCommandDto
#Component
public class PhotoImportCommandDto extends BaseCommand {
#NotNull(message = "userId must not be null!")
#UserExistsConstraint
private Long userId;
...
}
What I would like to do is somehow set a Spring Bean of the user that is validated in the #UserExistsConstraint and reference it in various methods that might be called throughout a Http request, but I'm not really sure how to do that. Since I've never really created my own Spring Beans, I don't know how to proceed. I've read various guides like this, but am still lost in how to implement it in my code.
Any help/examples would be much appreciated.
You can use the #Bean annotation.
#Configuration
public class MyConfiguration {
#Bean({"validUser"})
public User validUser() {
User user;
//instantiate user either from DB or anywhere else
return user;
}
then you can obtain the validUser.
#Component
public class PhotoImportCommandDto extends BaseCommand {
#Autowired
#Qualifier("validUser")
private User validUser;
...
}
I don't really know how to make annotations in Java. Anyway, in Spring, checking where the User exists in the DataBase or not is one line of code:
userRepository.findOne(user) == null
That is accomplished by the Spring Data JPA project:
Create a JPA Entity User.
Set the spring.datasource.url and login/password in the
resources/application.properties.
Create this interface:
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
}
Note, Spring implements it behind the scences.
Inject this interface into your RestController (or any other Spring bean):
private UserRepository userRepository ;
**constructor**(UserRepository ur){
userRepository = ur;
}
Note, a Spring Bean is any class annotated #Component (this includes stereotype annotations like Controller, Repository - just look up the contents of an annotation, it may use #Component internally) or returned from a method which is annotated #Bean (can only be on the Component or Configuration class). A Component is injected by searching the classpath, Bean is injected more naturally.
Also note, injecting is specifying #Autowired annotation on field or constructor, on a factory method, or on a setter. The documentation recommends that you inject required dependencies into constructor and non-required into the setter.
Also note, if you're injecting into a constructor and it is clean by the arguments, you may omit #Autowired annotation, Spring will figure it out.
Call its method findOne.
So, you can do one of the following:
Inject the userRepository into the #RestController constructor (as shown above). I would do that.
Inject the userRepository into the #Service (internally #Component) class that will do this sorts of thing for you. Maybe you can play with it to create an annotation.
p.s. Use #PostMapping instead of #RequestMapping(method = RequestMethod.POST)
p.p.s. If ever in doubt, go to the official documentation page and just press CTRL-F: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/ Note the current word, that will always take you to the latest version.
p.p.p.s Each Spring project has its own .io webpage as well as quick Get Started Guides where you can quickly see the sample project with explanations expecting you to know nothing.
Hope that helps! :)
Don't forget to mark the answer as accepted if you wish
Using Jose's input, I took a bit of a different route.
Here's what I did:
I created a ValidatedUser class:
#RequestScope
#Component
public class ValidatedUser {
private UserEntity user;
public UserEntity getUser() {
return user;
}
public void setUser(UserEntity user) {
this.user = user;
}
}
and I also created a wrapper class HttpRequestScopeConfig to capture all variables to use over the course of an Http Request to the api.
#Component
public class HttpRequestScopeConfig {
#Autowired
private ValidatedUser validatedUser;
...
public UserEntity getValidatedUser() {
return validatedUser.getUser();
}
public void setValidatedUser(UserEntity validatedUser) {
this.validatedUser.setUser(validatedUser);
}
...
}
In my UserExistsConstraintValidator (which is the impl of #UserExistsConstraint, I set the validatedUser in the httpRequestScopeConfig:
public class UserExistsConstraintValidator implements ConstraintValidator<UserExistsConstraint, Long> {
//private Log log = LogFactory.getLog(EmailExistsConstraintValidator.class);
#Autowired
private UserCommandService svc;
#Autowired
private HttpRequestScopeConfig httpRequestScope;
#Override
public void initialize(UserExistsConstraint userId) {
}
#Override
public boolean isValid(Long userIdField, ConstraintValidatorContext context) {
try {
UserEntity user = svc.findUserOfAnyStatus((Long) userIdField);
if (user != null) {
httpRequestScope.setValidatedUser(user);
return true;
}
} catch (Exception e) {
//log.error(e);
}
return false;
}
}
Now, I can access these variables throughout the rest of my service layers by autowiring HttpRequestScopeConfig where necessary.

What is the best approach to get injected beans with same interface in factory using Spring?

I created one factory to decide what best implementation should be returned, based in some conditional check.
// Factory
#Component
public class StoreServiceFactory {
#Autowired
private List<StoreService> storeServices;
public StoreService getService(){
if(isActiveSale){
return storeServices.get("PublicStoreService")
}
return storeServices.get("PrivateStoreService")
}
}
//Service Implementations
#Service
#Qualifier("PublicStoreService")
public class PublicStoreService implements StoreService {
public getStoreBalanceScore(){
Do Stuff....
}
}
#Service
#Qualifier("PrivateStoreService")
public class PrivateStoreService implements StoreService {
public getStoreBalanceScore(){
Do Stuff....
}
}
// Controller
#Autowired
StoreServiceFactory storeServiceFactory;
#Override
public StoreData getStoreBalance(String storeId) {
StoreService storeService = storeServiceFactory.getService();
return simulationService.simulate(sellerId, simulation);
}
Is this approach good? If yes, how can i get my service from an elegant way?
I would like to use only annotations, without configurations.
You should use a map instead of a List and pass a string parameter to the getService method.
public class StoreServiceFactory {
#Autowired
private Map<String,StoreService> storeServices = new HashMap<>();
public StoreService getService(String serviceName){
if(some condition...){
// want to return specific implementation on storeServices map, but using #Qualifier os something else
storeServices.get(serviceName)
}
}
}
You can prepopulate the map with supported implementations. You can then get an appropriate service instance as follows :
// Controller
#Autowired
StoreServiceFactory storeServiceFactory;
#Override
public StoreData getStoreBalance(String storeId) {
StoreService storeService = storeServiceFactory.getService("private");//not sure but you could pass storeId as a parameter to getService
return simulationService.simulate(sellerId, simulation);
}
If you don't like using Strings, you can define an enum for the supported implementations and use that as the key for your map.
You don't need to create a list or map on your code. You can retrieve it directly from Spring context using GenericBeanFactoryAccessor. This has various method to retrieve a specific bean like based on name, annotation etc. You can take a look at javadoc here. This avoids unnecessary complexity.
http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/beans/factory/generic/GenericBeanFactoryAccessor.html

JAX-RS subresources and Guice injections

In a JAX-RS web-app we make use of subresources:
#Path("/some/things")
public class ThingsListResource {
#Inject
SomeStorage store;
#GET
public List<Thing> getAllThings() {
return store.getAllThings();
}
#Path("{id}")
public ThingResource getThingResource(#PathParam("id") String id) {
return new ThingResource(id); // PROBLEMATIC
}
}
public class ThingResource {
#Inject
SomeOtherDependecy dep;
#Inject
SomeStorage store;
private final String id;
public ThingResource(String id) {
this.id = id;
}
#GET
public Thing getThisThing() {
return store.getThing(id);
}
#DELETE
public void removeThisThing() {
store.removeThing(id);
}
// following is a list of methods useful enough
// to make ThingResource a useable subresource
}
As you noticed, there are injections which are made with Guice and its GuiceResteasyBootstrapServletContextListener. The dependencies of root resources are injected without problems. The problematic line above is marked with PROBLEM: the subresource is created by-hand, which omits all Guice injections.
What is the elegeant way to inject dependencies with Guice into subresources? I can think of a few options here:
Inject the injector into the root resource and use it to create a subresource, probably with some #Assisted magic in the subresource:
#Inject
Injector injector
// ...
return injector.getInstance(ThingResource.class); // via some provider accepting id?
but I can't wrap my head arround injecting the id of the "thing" into the provider or #Assisted injections. Also, it looks like a lot of Guice boilerplate is required here.
Forget about Guice management of the subresource and pass every dependency to its constructor by hand from the root resource. Simple, yet very "dependency injection by hand"-like.
Make the subresource and inner class of ThingsListResource and thus let it have an access to the outer class (injected) fields. Not very extensible (e.g. if one want to have different subresources implementing the common interface), but simple...
Forget about subresources and "upgrade" them to root resources. It's a bit breaking of DRY rule, as you will specify full URL paths to each resource.
Is there any other way to go or simpler way to realize the above ideas?
This is an ideal use-case for assisted inject. You need to define a factory interface:
public interface ThingResourceFactory {
public ThingResource create(String id);
}
Then you bind it in one of your modules:
install(new FactoryModuleBuilder().build(ThingResourceFactory.class));
Then you modify ThingResource constructor:
private final String id;
#Inject
public ThingResource(#Assisted String id) {
this.id = id;
}
(BTW, I would use constructor injection instead of field injection if I were you, but this is just a side note)
Then you inject ThingResourceFactory into your ThingsListResource and use it in your resource method:
#Path("/some/things")
public class ThingsListResource {
#Inject
SomeStorage store;
#Inject
ThingResourceFactory thingResourceFactory;
#GET
public List<Thing> getAllThings() {
return store.getAllThings();
}
#Path("{id}")
public ThingResource getThingResource(#PathParam("id") String id) {
return thingResourceFactory.create(id);
}
}
See, almost no boilerplate and very easy to use! Guice will automatically create ThingResourceFactory instance for you which will pass create() arguments directly to #Assisted parameters in class constructor, injecting other parameters/fields/methods/etc with usual means.

Categories

Resources