Cannot have inject dynamic instance in quarkus when using #Dependent - java

I am using a library that is not thread safe and I want a fresh instance of that library object whenever I use it. I made a test repo here : https://github.com/lud/test-quarkus-arc
The library comes with two classes, SomeLibraryClass, which I need to use, and SomeLibraryClassDependency which is required by the former, and which is the thread-unsafe one.
I was trying to get all that working by using this factory-ish class:
#ApplicationScoped
public class MyAppFactory {
#Dependent
#Produces
SomeLibraryClassDependency getDep() {
return new SomeLibraryClassDependency();
}
#Dependent
#Produces
SomeLibraryClass getUsable(SomeLibraryClassDependency dep) {
return new SomeLibraryClass(dep);
}
#Inject
Instance<MyAppClass> myClass;
public MyAppClass getNewMyClass() {
return myClass.get(); // <-- this fails
}
}
This is some test code I would like to compile. I am calling the factory getter twice, and I verify that my class uses a different instance of the SomeLibraryClassDependency class.
#Test
public void testHelloEndpoint() {
var a = factory.getNewMyClass();
var b = factory.getNewMyClass();
assertNotEquals(a.getUsabeId(), b.getUsabeId());
}
Here is the class that should be instantiated by calling Instance<MyAppClass>#get:
#Dependent
public class MyAppClass {
#Inject
SomeLibraryClass usable;
public MyAppClass() {
}
public Integer getUsabeId() {
return usable.getId();
}
}
Finally here is the code for the library mocks:
public class SomeLibraryClass {
private SomeLibraryClassDependency dep;
public SomeLibraryClass(SomeLibraryClassDependency dep) {
this.dep = dep;
}
public Integer getId() {
return dep.getId();
}
}
public class SomeLibraryClassDependency {
private static Integer n = 0;
private Integer id;
public SomeLibraryClassDependency() {
n += 1;
this.id = n;
}
public Integer getId() {
return id;
}
}
When trying to compile that, I have the following error, and I do not understand why
[error]: Build step io.quarkus.arc.deployment.ArcProcessor#validate threw an exception: javax.enterprise.inject.spi.DeploymentException: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type io.quarkus.arc.runtime.BeanContainer$Instance<org.acme.getting.started.MyAppClass> and qualifiers [#Default]
java member: org.acme.getting.started.MyAppFactory#myClass
declared on CLASS bean [types=[org.acme.getting.started.MyAppFactory, java.lang.Object], qualifiers=[#Default, #Any], target=org.acme.getting.started.MyAppFactory]
I was thinking that since MyAppClass has the #Dependent annotation, it should be resolved.
Edit: I know I can also define a producer for my class, but my end goal is to be able to #Inject other things in that class (like a logger) and let the container do its job.

The error message says Unsatisfied dependency for type io.quarkus.arc.runtime.BeanContainer$Instance<org.acme.getting.started.MyAppClass>, which suggests that you have a wrong import for the Instance class. The correct one is javax.enterprise.inject.Instance.

Related

Autowiring any of an Interface using Spring Boot

I want autowire a class which implements in a Component. Here is a part of the interface:
#Service
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class GenericResourceHandlerService<T extends ResourceRequest, A extends ResultType, B extends ResourceService<T, A>> {
private final B service;
public Response get(String x) {
various checks(x, service.getType());
B res = service.get(x);
if (res!= null) {
return Response.status(Response.Status.OK).entity(res).build();
} else {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
}
Then, a class which implements ResourceService would look like this:
#Component
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class TypeOneService implements EntityService<CityRequest, City> {
private final Repository repository;
#Override
public ResultType getType() {
return ResultType.TYPE_ONE;
}
#Timed
public TYPE_ONE get(String resource) {
return repository.get(resource);
}
}
And the interface itself, looks like this:
public interface EntityService<T extends EntityRequest, A extends ReturnableEntity> {
ResourceType getResourceType();
A get(String resource);
}
Now, I have a set of controllers which tries to autowire GenericResourceHandlerService and call it's get method. Which looks like this:
public class TypeOneController {
private final TypeOneService typeOneService;
private final GenericResourceHandlerService<TypeOneRequest, TypeOne, TypeOneService> genericResourceHandlerService;
public Response getListItemByResource(
String resource
) {
return genericResourceHandlerService.get(resource);
}
}
Or this:
public class TypTwoController {
private final TypeTwoService typeTwoService;
private final GenericResourceHandlerService<TypeTwoRequest, TypeTwo, TypeTwoService> genericResourceHandlerService;
public Response getListItemByResource(
String resource
) {
return genericResourceHandlerService.get(resource);
}
}
This compiles but when the app starts then, then I get the following error message:
Parameter 0 of constructor in path.GenericResourceHandlerService required a single bean, but 2 were found:
- typeOneSerivce: defined in file [C:\Path\TypeOneService.class]
- typeTwoService: defined in file [C:\Path\TypeTwoService.class]
I think this is because, Spring Boot can't work out which one to service to autowire with. Is it possible what I am trying to do?
Spring tries to inject a bean to resolve the GenericResourceHandlerService.service but service has type B and B extends ResourceService. And spring found 2 beans implementing this interface so doesn't know which implementation to autowire..
You can put #Qualifier on field service but I imagine you will lost the genericity of this type GenericResourceHandlerService
Maybe the best way is to let the controller pass through the implementation in the GenericResourceHandlerService and let this last as a simple pojo..not a spring bean (so remove #Service on GenericResourceHandlerService
Like this
public class TypeOneController {
#Autowired
private final TypeOneService typeOneService;
private final GenericResourceHandlerService<TypeOneRequest, TypeOne, TypeOneService> genericResourceHandlerService = new GenericResourceHandlerService(typeOneService);
public Response getListItemByResource(
String resource
) {
return genericResourceHandlerService.get(resource);
}
}

How to get generified class from ApplicationContext?

Could you please help me and clarify how should I deal with generics in Spring?
In the following example an Exception NoUniqueBeanDefinitionException: No qualifying bean of type 'java.lang.Object' available is thrown?
public class Launcher {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
FooService<String> fooService = context.getBean(FooService.class);
fooService.printVal();
}
}
I would like to notice that if I change generic to String type it works correctly.
#Service
public final class FooService<V> {
private final V v;
#Autowired
public FooService(V v) {
this.v = v;
}
public void printVal() {
System.out.println(v);
}
}
And simple configuration class.
#Configuration
#ComponentScan("service")
public class AppConfig {
#Bean
public String string() {
return "simple string";
}
}
Could you please advice me what is the best way to force the snippet to work properly?
Since Spring 4.0 it will automatically consider generics as a form of #Qualifier, as below:
#Autowired
private Item<String> strItem; // Injects the stringItem bean
#Autowired
private Item<Integer> intItem; // Injects the integerItem bean
So you should indicate the name ( in your case "string") in your constructor method.

Why is #Required ignored by Spring?

I've read that #Required uses to make sure the property has been set.
But when I try to use it along with Spring Annotation Configuration it doesn't work.
Below you can familiarize with my code base.
#Configuration
#ComponentScan
public class AppConfig {
#Bean(initMethod = "initMethod")
public SimpleClass simpleClass(){
return new SimpleClass();
}
}
public class SimpleClass implements InitializingBean {
private int n;
public SimpleClass() {
System.out.println("constructor");
}
public int getN() {
return n;
}
#Required
public void setN(int n) {
System.out.println("setter");
this.n = n;
}
void initMethod(){
}
#Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet()");
}
}
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
SimpleClass simpleClass = context.getBean(SimpleClass.class);
}
}
Why does Spring application context create the SimpleClass Bean and don't complain about the absence of injection via setter?
UPD:
When I try to do the same using XML configuration and add then I receive a "Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Property 'n' is required for bean 'simple'"
#Required documentation states (emphasis is mine) :
Marks a method (typically a JavaBean setter method) as being
'required': that is, the setter method must be configured to be
dependency-injected with a value.
With Spring in order to configure a method as dependency injected you have to specify it (#Autowired is the standard way).
But specifying both #Autowired and #Required on a method seems clumsy today :
#Autowired
#Required
public void setN(int n) {
System.out.println("setter");
this.n = n;
}
Instead, to configure the setter to be both dependency-injected and required I advise to use only #Autowired that by default is required as you can notice :
public #interface Autowired {
/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {#code true}.
*/
boolean required() default true;
}
So it is enough :
#Autowired
public void setN(int n) {
System.out.println("setter");
this.n = n;
}
As a side note, the setter injection will probably fail as the int n will probably not be resolved as a dependency. The #Value annotation on the parameter could probably help you.
You need to use it along with #Autowired (or #Value if you inject simple values) annotation.
Referring to this article:
Without a RequiredAnnotationBeanPostProcessor bean, Spring will not complain.
So ... add another bean of type RequiredAnnotationBeanPostProcessor (using #Autowired) to make it complain.
Also, #Required annotation is to be used for Bean setter. You are trying to do #Required check on a primitive type. As far as I understand, all Spring Beans (or J2EE beans) are to be objects.
For primitive types, you can use a #Value injection.

Inject value from properties in Spring Boot

I have a Rest Controller in which I initialise a service like this :
class Config {
#Value(${"number.of.books"})
private final static String numberOfBooks;
}
class MyController {
private final Service myService = new ServiceImplementation(Config.numberOfBooks)
public ResponseEntity methodA() { ... }
}
The numberOfBooks field has a initialisation value but when it's passed in the ServiceImplementation constructor it comes null.
I'm thinking I'm missing something obvious over here.
What is the mistake and which would be the best practice to inject a value from a property file into a constructor?
I recommend you to directly inject numberOfBooks in your ServiceImplementation, as follows:
public class ServiceImplementation implements Service {
#Value("${number.of.books}")
private String numberOfBooks;
}
Otherwise use setter injection for static variables, as follows:
#Component
class Config {
public static String numberOfBooks;
#Value("${number.of.books}")
public void setNumberOfBooks(String numberOfBooks) {
numberOfBooks = numberOfBooks;
}
}
After studying a little I've found out that the dependency injection happens after the constructor has been called. This being said the approach used was to use Autowired on my services constructor.
class ServiceImplementation implements Service {
private final String numberOfBooks;
#Autowired
private ServiceImplementation(Config config) {
this.numberOfBooks = config.getNumberOfBooks();
}
}
In this way Spring creates the dependency tree and makes sure that Config is not null when injected.

How to use Singleton object in Spring?

I am newbie to Spring Framework.I have tried following example in spring.
#Path("/XZY")
#Service
#Transactional
public class XZY {
#Autowired
SampleDAO sampleDao;
#Autowired
TestDAO testDao;
#Autowired
XZYinterface xzyinterface;
#POST
#Produces("text/plain")
#Path("/checkservice")
public Response XZYservice(#FormParam("Code") String Code,
#FormParam("source") String source,
#FormParam("value") String value) {
//return xzyinterface.checkXYZService(Code,sourceName,source);
XZYinterface xyz = ServiceFactory.getXZY(999);
return xyz.checkXYZService(Code,sourceName,source);
}
}
The following code will use to create singleton object
public class Singleton {
private static sampleA sampleClassA=null;
private static SampleB sampleClassB=null;
public static XZYAbstract getXZY(long id){
if(id == 999){
if(sampleClass == null){
sampleClassA = new sampleA();
}
return sampleClass;
}
if(id == 9999){
sampleClassB = new sampleA();
}
return sampleClassB;
}
}
Interface
public interface XZYinterface {
Response XZYservice(String Code, String source,String value)
}
Abstract class and implements Interface
public class XZYAbstract implements XZYinterface {
public XZYAbstract(){
super();
}
#Autowired
SampleDAO sampleDao;
#Autowired
TestDAO testDao;
public Response checkXYZService(String Code,String source,String value){
String sample = sampleDao.getValue(code);
//..source code
}
}
The following class extends abstract class.
public class sampleA extends XZYAbstract {
//some methods.
}
If i run the application it throws following errors
SEVERE [com.sun.jersey.spi.container.ContainerResponse] The RuntimeException could not be mapped to a response, re-throwing to the HTTP container: java.lang.NullPointerException
at com.test.xyz.XZYAbstract.checkXYZService(XZYAbstract.java:112) [:]
at com.test.XYZ.XZYservice(XZY.java:140) [:]
If i call directly without singleton object, values are initialized properly using Auto wired (//return xzyinterface.checkXYZService(Code,sourceName,source);) and it's working fine.
Throw from singleton object, values(sampleDAo,testDao) are not initialized properly.
How to resolve this error?
The reason is quite trivial: it's because Spring is just a library, and not a change to the Java language. Spring doesn't instrument nor enhance constructors, so the only way to get initialized Spring bean is to get it from the Spring context.
If you call new Bean(), you becomes Bean instance untouched by Spring.
For the question how to use singleton bean: do nothing. Spring beans are Singletons by default. You can specify other scope via #org.springframework.beans.factory.config.Scope annotation. See for example #Scope("prototype") bean scope not creating new bean, how it works.

Categories

Resources