How to autowire when constructor arguments are specified at runtime? - spring-boot

I have a domain class that i want to inject into the facade of its module, this class have constructor arguments that are specified at runtime. The definition of this class is as shown below:
#Component("area")
#Scope("prototype")
public class GeographicalCircleArea {
private Location center;
private double radius;
private final IShopsProviderAdapter shopsProviderAdapter;
#Autowired
GeographicalCircleArea(double centerLatitude, double centerLongitude, double radiusInKm, IShopsProviderAdapter shopsProviderAdapter) {
this.center = makeCenter(centerLatitude, centerLongitude);
this.radius = radiusInKm;
this.shopsProviderAdapter = shopsProviderAdapter;
}
List<Shop> getShopsWithin() {
return shopsProviderAdapter.findShopsWithin(this);
}
public Location getCenter() {
return center;
}
public double getRadius() {
return radius;
}
private Location makeCenter(double latitude, double longitude) {
return new Location(latitude, longitude);
}
}
The facade where I want to inject the precedent bean is:
#Service
public class GeolocationInformationSystem {
#Autowired
private GeographicalCircleArea area;
public List<Shop> searchForNearbyShops(double centerLatitude, double centerLongitude, double radiusInKm) {
return area.getShopsWithin();
}
}
which gets the arguments to instantiate GeographicalCircleArea at runtime.
How to correctly apply the autowiring?

In the documentation they make it clear :
For each bean, its dependencies are expressed in the form of properties, constructor arguments, or arguments to the static-factory method if you are using that instead of a normal constructor. These dependencies are provided to the bean, when the bean is actually created.
That mean your application crash before running any code in runtime because of :
#Autowired
private GeographicalCircleArea area;
Simple reason you can't inject unknown values because they are unknown during container initialization process and bean creation step,
How to solve that: changing your implementation by changing the constructor
#Autowired
GeographicalCircleArea(IShopsProviderAdapter shopsProviderAdapter) {
this.shopsProviderAdapter = shopsProviderAdapter;
}
and change
List<Shop> getShopsWithin() {
return shopsProviderAdapter.findShopsWithin(this);
}
to take those arguments :
List<Shop> getShopsWithin(double centerLatitude, double centerLongitude,
double radiusInKm) {
this.center = makeCenter(centerLatitude, centerLongitude);
this.radius = radiusInKm;
return shopsProviderAdapter.findShopsWithin(this);
}

Related

How to use #ConfigProperties with a Converter class

I tried to implement a custom config type and it worked. However, when I use the custom type with a group of config using the #ConfigProperties it fails to automatically recognize the property by its name and instead treats the property as an object with a nested property.
How can I implement such a behavior correctly? (I am new to Quarkus, so please correct me if I am doing something wrong here)
Here is a code snippet that converts a custom type:
public class Percentage {
private double percentage;
public Percentage() {}
public Percentage(double percentage) {
this.percentage = percentage;
}
public void setPercentage(double percentage) {
this.percentage = percentage;
}
public double getPercentage() {
return this.percentage;
}
}
#Priority(300)
public class PercentageConverter implements Converter<Percentage> {
#Override
public Percentage convert(String value) {
int percentIndex = value.indexOf("%");
return new Percentage(Double.parseDouble(value.substring(0, percentIndex - 1)));
}
}
/// this works ------
public class Hello {
#ConfigProperty(name = "custom.vat")
Percentage vat;
public Hello () {
}
// .....
}
/// however, this fails
#ConfigProperties(prefix = "custom")
public class CustomConfig {
public Percentage vat;
public Percentage profit;
}
javax.enterprise.inject.spi.DeploymentException: No config value of type [double] exists for: custom.vat.percentage
at io.quarkus.arc.runtime.ConfigRecorder.validateConfigProperties(ConfigRecorder.java:39)
Unfortunately, I believe this does not work because Quarkus #ConfigProperties, handles these cases as if they were subgroups and try to map nested properties with the configuration (and not use the Converter).
Feel free to open up an issue in Quarkus GH if you feel this should change: https://github.com/quarkusio/quarkus/issues
Alternately, you can use SR Config #ConfigMapping: https://smallrye.io/docs/smallrye-config/mapping/mapping.html. It covers a few more cases, including direct Conversion and in the future it may replace Quarkus #ConfigProperties.

Is DI to static field not good?

I have an issue with component bean that has static field in it.
And I did DI to static field to implement static method so that other class can call this method without DI to itself
Is this unnatural stuff? Just someone has doubt on this so..
-Edit
I attached some code that shows what I want
#Component
public class CompWithStatic {
private static InjectedObj injectedObj ;
#Autowired
CompWithStatic(InjectedObj injectedObj ) {
CompWithStatic.injectedObj = injectedObj;
}
public static String doStaticA(String str){
//do something with injectedObj
return str + " method A ";
}
public static String doStaticB(String str){
//do something with injectedObj
return str + " method B ";
}
}
public enum EnumType {
ENUMA(str-> CompWithStatic.doStaticA(str)),
ENUMB(str-> CompWithStatic.doStaticB(str));
private Function<String,String> expression;
EnumType(Function expression) {
this.expression = expression;
}
public String doExpress(String str){
return expression.apply(str);
}
}
what I intended was each Enum case has dynamic method
so you can use it as like this
EnumType.ENUMA.doExpress("str");
Please let me know if it's unnatural way!
Static fields are okay to some extend, typically constants within bean are defined as static final constants.
But static methods within bean are not at all recommended as this there is no participation of static methods in object which will not have any place in DI. (Although it shouldn't be an issue to have static methods within bean as these references will be replaced by Class during compilation time it self).

Can we mock the instance if it declared with new key word that is inside the method we are testing?

I'm new to Mockito and Junit, I'm working with Spring Boot
I want to know that can we mock the instance if it declared with a new keyword that is inside the method we are testing?
For example:
#Service
class A {
#Autowired
X x;
#Autowired
Y y;
public void testMe(){
imCommunicatingWithSomeRestClient();
}
private void imCommunicatingWithSomeRestClient(){
String body="";
MyRestClient client=new MyRestClient(iTakeUrlNeedsToHit); //no arg constructor not exist and suppose this is the method of some Core jar project
client.callDataRest(HTTP.GET,body)
}
}
Although I wanted to mock it, I've tried all #Spy #Mock, #InjectMocks to check if it'll behave differently but none of these worked for me as it always creates a new object and calls the real method.
So I change approach slightly and did it with using BeanFactory and instead of new I replace that with :
MyRestClient client=beanFactory.getBean(MyRestClient.class,jobDataRestUrl);
so I have these questions:
Already asked above (if we mock the instance if it declared with new keyword that is inside the method we are testing).
If my current project is Spring Boot project and MyRestClient is inside the jar written in the core. Is the standard say I should not create it by Bean Factory because I think I should do it by that way and let the Spring handles that
I even tried with reflection but it seems it is also not working with the instance created with new keyword inside a method and not on the class level.
Your current setting is not efficiently testable. You may still do it with lots of weird workarounds, but still, not recommended. Here's what you can do; firstly, you should not have any kind of dependency initialization inside your classes (like new MyRestClient(...)). So, move the REST client to the property level and have it injected through constructor.
#Service
class A {
private final X x;
private final Y y;
private final MyRestClient restClient;
public A (X x, Y y, MyRestClient restClient) {
this.x = x;
this.y = y;
this.restClient = restClient;
}
public void testMe() {
imCommunicatingWithSomeRestClient();
}
private void imCommunicatingWithSomeRestClient() {
String body = "";
restClient.callDataRest(GET, body);
}
}
Since you are using Spring, you can create a bean of the REST client and move the endpoint URL to an external property.
class Config {
#Bean
public MyRestClient myRestClient(#Value("${property.name}") String url) {
return new MyRestClient(url);
}
}
Finally, you can easily mock the behavior of that REST client.
#ExtendWith(MockitoExtension.class)
class TestA {
#Mock
private X x;
#Mock
private Y y;
#Mock
private MyRestClient restClient;
#InjectMocks
private A a;
// your tests...
}

Use another bean as parameter for constructor injection

I there any posibility to inject bean member into constructor of another bean member? Something like code below:
class Config {
#Resource("${root-service}")
IServiceRoot root;
#Resource("${child-service}")
IServiceChild child;
}
#Component("root1")
class ServiceRoot1 implements IServiceRoot {
}
#Component("root2")
class ServiceRoot2 implements IServiceRoot {
}
#Component("child1")
class ServiceChild1 implements IServiceChild {
public ServiceChild1(IServiceRoot r) {
root = r;
}
}
#Component("child2")
class ServiceChild2 implements IServiceChild {
public ServiceChild2(IServiceRoot r) {
root = r;
}
}
child-service and root-service props are set to "child1/child2" and "root1/root2" respectively. Now I want to be able to construct child member using root member.
#Resource(${child-service})
#ConstructorArg(member="root")
ServiceChild child;
So after constructing root by spring it will be used to construct child. How can I achieve behaviour like #ConstructorArg?

Spring - Qualify injection candidates by designated environment

Edit:
Perhaps a more concise way to ask this question is: Does Spring provide a way for me to resolve ambiguous candidates at injection time by providing my own listener/factory/decision logic?
In fact, arguably the #Environmental qualifier on the member field below is unnecessary: if an #Inject-ion is ambiguous... let me help? In fact, #ResolveWith(EnvironmentalResolver.class) would be alright too..
When Spring attempts to inject a dependency (using annotations) I understand that I need to #Qualifier an #Inject point if I am to have multiple components that implement that interface.
What I'd like to do is something like this:
class MyFoo implements Foo {
#Inject
#Environmental
private Bar bar;
}
#Environmental(Environment.Production)
class ProductionBar implements Bar {
}
#Environmental({Environment.Dev, Environment.Test})
class DevAndTestBar implements Bar {
}
I would expect that I need to create some kind of ambiguity resolver which would look something (vaguely) like this:
class EnvironmentalBeanAmbiguityResolver {
// set from configuration, read as a system environment variable, etc.
private Environment currentEnvironment;
public boolean canResolve(Object beanDefinition) {
// true if definition has the #Environmental annotation on it
}
public Object resolve(Collection<Object> beans) {
for (Object bean : beans) {
// return bean if bean #Environmental.values[] contains currentEnvironment
}
throw new RuntimeException(...);
}
}
One example of where this would be useful is we have a service that contacts end-users. Right now I just have a hacked together AOP aspect that before the method call to the "MailSender', checks for a "Production" environment flag and if it is not set, it sends the email to us instead of the users email. I'd like to instead of wrapping this in an AOP aspect specific to mail sending, instead be able to differentiate services based on the current environment. Sometime's it is just a matter of "production" or "not production" as I've demonstrated above, but a per-environment definition works too.
I think this can be reused for region too... e.g. #Regional and #Regional(Region.UnitedStates) and so on and so forth.
I'd imagine #Environmental would actually be a #Qualifier that way if you wanted to depend directly on something environmental you could (an #Environmental(Production) bean would likely depend directly on an #Environmental(Production) collaborator - so no ambiguity for lower level items --- same a #Regional(US) item would depend on other #Regional(US) items expiclitly and would bypass my yet-to-be-understood BeanAmbiguityResolver)
Thanks.
I think I solved this!
Consider the following:
public interface Ambiguity {
public boolean isSatisfiedBy(BeanDefinitionHolder holder);
}
#Target({ METHOD, CONSTRUCTOR, FIELD })
#Retention(RUNTIME)
public #interface Ambiguous {
Class<? extends Ambiguity> value();
}
#Target(TYPE)
#Retention(RUNTIME)
public #interface Environmental {
public static enum Environment {
Development, Testing, Production
};
Environment[] value() default {};
}
#Named
public class EnvironmentalAmbiguity implements Ambiguity {
/* This can be set via a property in applicationContext.xml, which Spring
can use place holder, environment variable, etc. */
Environment env = Environment.Development;
#Override
public boolean isSatisfiedBy(BeanDefinitionHolder holder) {
BeanDefinition bd = holder.getBeanDefinition();
RootBeanDefinition rbd = (RootBeanDefinition) bd;
Class<?> bc = rbd.getBeanClass();
Environmental env = bc.getAnnotation(Environmental.class);
return (env == null) ? false : hasCorrectValue(env);
}
private boolean hasCorrectValue(Environmental e) {
for (Environment env : e.value()) {
if (env.equals(this.env)) {
return true;
}
}
return false;
}
}
#Named
public class MySuperDuperBeanFactoryPostProcessor implements
BeanFactoryPostProcessor, AutowireCandidateResolver {
private DefaultListableBeanFactory beanFactory;
private AutowireCandidateResolver defaultResolver;
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg)
throws BeansException {
if (arg instanceof DefaultListableBeanFactory) {
beanFactory = (DefaultListableBeanFactory) arg;
defaultResolver = beanFactory.getAutowireCandidateResolver();
beanFactory.setAutowireCandidateResolver(this);
return;
}
throw new FatalBeanException(
"BeanFactory was not a DefaultListableBeanFactory");
}
#Override
public Object getSuggestedValue(DependencyDescriptor descriptor) {
return defaultResolver.getSuggestedValue(descriptor);
}
#Override
public boolean isAutowireCandidate(BeanDefinitionHolder holder,
DependencyDescriptor descriptor) {
Ambiguity ambiguity = getAmbiguity(descriptor);
if (ambiguity == null) {
return defaultResolver.isAutowireCandidate(holder, descriptor);
}
return ambiguity.isSatisfiedBy(holder);
}
private Ambiguity getAmbiguity(DependencyDescriptor descriptor) {
Ambiguous ambiguous = getAmbiguousAnnotation(descriptor);
if (ambiguous == null) {
return null;
}
Class<? extends Ambiguity> ambiguityClass = ambiguous.value();
return beanFactory.getBean(ambiguityClass);
}
private Ambiguous getAmbiguousAnnotation(DependencyDescriptor descriptor) {
Field field = descriptor.getField();
if (field == null) {
MethodParameter methodParameter = descriptor.getMethodParameter();
if (methodParameter == null) {
return null;
}
return methodParameter.getParameterAnnotation(Ambiguous.class);
}
return field.getAnnotation(Ambiguous.class);
}
}
Now if I have an interface MyInterface and two classes that implement it MyFooInterface and MyBarInterface like this:
public interface MyInterface {
public String getMessage();
}
#Named
#Environmental({ Environment.Testing, Environment.Production })
public class MyTestProdInterface implements MyInterface {
#Override
public String getMessage() {
return "I don't always test my code, but when I do, I do it in production!";
}
}
#Named
#Environmental(Environment.Development)
public class DevelopmentMyInterface implements MyInterface {
#Override
public String getMessage() {
return "Developers, developers, developers, developers!";
}
}
If I want to #Inject MyInterface I would get the same multiple bean definition error that one would expect. But I can add #Ambiguous(EnvironmentalAmbiguity.class) and then the EnvironmentalAmbiguity will tell which bean definition it is satisfied by.
Another approach would have been to use a List and go through them all seeing if they are satisfied by a given bean definition, this would mean that the dependnecy wouldn't need the #Ambiguous annotation. That might be more "IoC-ish" but I also thought it might perform poorly. I have not tested that.

Resources