Multiple Constructor injection using Java Annotations - spring

The SPRING doc says the following
Spring Framework 4.3, an #Autowired annotation on such a constructor
is no longer necessary if the target bean only defines one constructor
to begin with. However, if several constructors are available, at
least one must be annotated to teach the container which one to use.
As i understand if there are multiple constructors and we have not annotated any of them then i will get an error . I ran the following code
#Component // this is bean id
public class TennisCoach implements Coach {
private FortuneService fortuneservice;
public TennisCoach(FortuneService thefortuneservice) {
System.out.println(" inside 1 arg constructter");
fortuneservice = thefortuneservice;
}
public TennisCoach() {
System.out.println(" inside 0 arg constructter");
}
I call that using the below code
TennisCoach theCoach = myapp.getBean("tennisCoach", TennisCoach.class);
But i didn't get the error .I got the O/P as
inside 0 arg constructter
Why?

It looks like the text you've quoted from the Spring docs doesn't apply to a case where one of the constructors is a no-args (default) constructor. You can see this very easily if you try and add an bean reference parameter to it.
Spring attempts to determine the candidate constructors using AutowiredAnnotationBeanPostProcessor and in your scenario, will not find any single or autowired constructor, so it will record the no-args one and instantiate it in SimpleInstantiationStrategy.

Related

Spring Boot Bean: value passed in constructor is not null in constructor, but null in CGLIB proxy

I have the following repository implementation in Kotlin:
#Repository
class SapArticleRepository(jooqDsl: DSLContext, jooqConfiguration: DefaultConfiguration) :
AbstractSapRepository<TrmSapArticleRecord, TrmSapArticlePojo>(jooqDsl, jooqConfiguration)
with the following base class:
abstract class AbstractSapRepository<R : UpdatableRecord<R?>, TPojo>(
var dao: DAOImpl<R, TPojo, Long>,
jooqDsl: DSLContext,
jooqConfiguration: DefaultConfiguration,
) {
fun findById(id: Long) = dao.findById(id)
}
When running an Integration test, I get an exception, because dao is null. I checked (by debugging): dao is not null when the constructor is called, but it indeed is when findById is called. I noticed that the object references are not the same (because of CGLIB, the Spring proxy), but I don't know what happens between the constructor call and the time the proxied bean is created.
I tried an abstract function getDaoImpl() an which is implemented in SapArticleRepository (returning the object instance) and the calling that method (instead of accessing dao), but that seems overly complicated. There must be a way to pass the constructor argument/field in a way so it is still present by the time the Bean is used.
Note. TrmSapArticleDao is just a Jooq-generated class.
Edit: I already found Constructor-Injected field is null in Spring CGLIB enhanced bean, but the accepted answer does not seem to answer my question.
As always, I found the solution 5 minutes after posting the question...
The problem was that fields passed in the constructor are final in Kotlin by default. CGLIB can't intercept final fields/method and therefore leaves them initialized with null (see this answer on GitHub.
The solution is to make the constructor field open. So above code works when defining the constructor of the abstract class as follows:
abstract class AbstractSapRepository<R : UpdatableRecord<R?>, TPojo>(
open var dao: DAOImpl<R, TPojo, Long>,
jooqDsl: DSLContext,
jooqConfiguration: DefaultConfiguration,
)

spring boot component with string parameters

i have a component that reads a configuration value from application.properties and accepts a string parameter in its constructor as such..
#Component
public class Person
{
#Value("${greeting}")
String greeting;
String name;
public Person(String name)
{
this.name = name;
onGreet( greeting + ", " + name );
}
public void onGreet(String message)
{
}
}
I need to instantiate this component as follows and override its "onGreet" event in the calling code as follows:
Person jack = new Person("jack")
{
public void onGreet(String message)
{
System.out.println( message );
}
};
However I end up getting this..
Parameter 0 of constructor in demo11.Person required a bean of type 'java.lang.String' that could not be found.
My application.properties is as follows:
greeting=hello
What am I missing here? Thank you.
It is literally telling you that the only constructor that you have requires a parameter that Spring knows nothing about.
Add a #Value to that String name in the constructor (right before the parameter) like so public Person(#Value("${name}") String name) if you want Spring to initalize it or remove that constructor
EDIT: some more explanation:
Spring is a dependency injection container. Meaning you define beans and let Spring create and inject them for you. Defining beans can be done in several ways (Java configuration, annotations or xml) here you are using annotation way via #Component.
Now that you have defined your bean (aka component) for Spring it will create it. For it to create it it needs to call a constructor. For that you need to provide it with all information necessary for constructor call - meaning all parameters. If parameters are other classes they need to be defined as beans as well (For example via #Component) if they are simple types like String you need to provide #Value for them.
Lastly if you ever use new ... to define Spring managed beans then the whole Spring magic disappears since Spring doesnt know about this bean instantiation anymore and will not autowire anything into it. For all intenses and purposes Spring is not aware of any objects you create with new.

Spring return dynamic instance based of String value

Java Spring question:
I have a interface MyInterface with one method
void exec (String str);
I have many implementation of MyInterface, say Oneimpl, anotherimpl yetanotherimpl...and so on and can keep adding new implementations.
how do I obtain an instance of a specific implementation using just the name of the implementing class passed as a STRING value , say "someRandomImpl"
The code should be dynamic and can provide a instance of new implementations without code change.
implements ApplicationContextAware
it will autowired ApplicationContext object
use the object like
context.getBean(beanName)
then you get the bean

Spring #Transactional value param with SpEL (Spring expression language)

In one of my service classes I have some methods annotated as such :
#Transactional(value="foodb")
public Bar getMeSomething(){
}
I recently learned about #Value with the power of Spring EL to get some values stored in a properties file.
such as
#Value("${my.db.name}")
which works like a charm.
Now I'm trying to do the same with
#Transactional(value="${my.db.name}")
with no success ...
I get the following exception :
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named '${my.db.name}' is defined: No matching PlatformTransactionManager bean found for qualifier '${my.db.name}' - neither qualifier match nor bean name match!
Is what I am trying to do even supported by Spring ?
What can I do to get the my.db.name value inside that #Transactional annotation
Thanks
Nope, it's not supported.
Here's an excerpt from org.springframework.transaction.annotation.SpringTransactionAnnotationParser
public TransactionAttribute parseTransactionAnnotation(Transactional ann) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
rbta.setPropagationBehavior(ann.propagation().value());
rbta.setIsolationLevel(ann.isolation().value());
rbta.setTimeout(ann.timeout());
rbta.setReadOnly(ann.readOnly());
rbta.setQualifier(ann.value()); // <<--- this is where the magic would be
// if it was there, but it isn't

Find annotation in Spring proxy bean

I have created my own annotation for classes: #MyAnnotation, and have annotated two classes with it.
I have also annotated a few methods in these classes with Spring's #Transactional. According to the Spring documentation for Transaction Management, the bean factory actually wraps my class into a proxy.
Last, I use the following code to retrieve the annotated beans.
Method getBeansWithAnnotation correctly returns my declared beans. Good.
The class of the bean is actually a proxy class generated by Spring. Good, this means the #Transactional attribute is found and works.
Method findAnnotation does not find MyAnnotation in the bean. Bad. I wish I could read this annotation from the actual classes or proxies seamlessly.
If a bean is a proxy, how can I find the annotations on the actual class ?
What should I be using instead of AnnotationUtils.findAnnotation() for the desired result ?
Map<String,Object> beans = ctx.getBeansWithAnnotation(MyAnnotation.class);
System.out.println(beans.size());
// prints 2. ok !
for (Object bean: services.values()) {
System.out.println(bean.getClass());
// $Proxy
MyAnnotation annotation = AnnotationUtils.findAnnotation(svc.getClass(), MyAnnotation.class);
//
// Problem ! annotation is null !
//
}
You can find the real class of the proxied bean by calling AopProxyUtils.ultimateTargetClass.
Determine
the ultimate target class of the given bean instance, traversing not
only a top-level proxy but any number of nested proxies as well - as
long as possible without side effects, that is, just for singleton
targets.
The solution is not to work on the bean itself, but to ask the application context instead.
Use method ApplicationContext#findAnnotationOnBean(String,Class).
Map<String,Object> beans = ctx.getBeansWithAnnotation(MyAnnotation.class);
System.out.println(beans.size());
// prints 2. ok !
for (Object bean: services.values()) {
System.out.println(bean.getClass());
// $Proxy
/* MyAnnotation annotation = AnnotationUtils.findAnnotation(svc.getClass(), MyAnnotation.class);
// Problem ! annotation is null !
*/
MyAnnotation annotation = ctx.findAnnotationOnBean(beanName, MyAnnotation.class);
// Yay ! Correct !
}

Resources