I am trying to understand the differences between these two annotations and how they affect injection in Spring. Consider the following piece of code -
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface ExternalPropertiesHolder {}
When I mark a class with this annotation -
#ExternalPropertiesHolder
public class SomeProperties {}
and then this dependency is injected using #Inject, it works perfectly -
#Service
public class SomeService {
private SomeProperties someProperties;
#Inject
public SomeService(SomeProperties someProperties) {
this.someProperties = someProperties;
}
}
However, when I replace #Component with #Named -
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Named // --> Here!
public #interface ExternalPropertiesHolder {}
Then the injection fails with the usual bean not found exception -
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type [com.hogehoge.SomeProperties] found for
dependency: expected at least 1 bean which qualifies as autowire
candidate for this dependency. Dependency annotations: {}
I searched the Spring reference documentation, and all it has to say about the difference is this -
JSR-330 does not provide a composable model, just a way to identify
named components.
What does that mean? Does it mean that I cannot use #Named to compose a custom marker like this? Or is there something else?
P.S.: Of course by #Component I am referring to org.springframework.stereotype.Component and by #Named I am referring to javax.inject.Named.
So I got the answer directly from Juergen Hoeller. According to him, this line -
JSR-330 does not provide a composable model, just a way to identify
named components.
means that the javax.inject.Named can only be declared directly on a given bean class. The composable annotation story just works with Spring's own annotations, which is exactly what I suspected.
Correct, javax.inject.Named and javax.anotations.ManagedBean doesn't provide a composable model. Thus, can't be used with the same intent as org.springframework.stereotype.Component.
But i could see from the documentation, we could use javax.inject.Qualifier in this use-case, as it's meant to be used for defining custom annotations.
Did you gave #Qualifier from javax a try ?
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Qualifier // --> from javax.inject
public #interface ExternalPropertiesHolder {}
Even the #Named is defined using #Qualifier.
#Qualifier
#Documented
#Retention(RUNTIME)
public #interface Named {
/** The name. */
String value() default "";
}
Related
I am wondering, why Spring's implementation of the #Component Annotation has RetentionPolicy.RUNTIME.
Here's the official implementation from github
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Indexed
public #interface Component {
String value() default "";
}
In my thought, the scanning for all Spring-Components annotated with #Component would be done at compile-time and all the results (annotated classes) would somehow be stored inside the ApplicationContext. I guess I am wrong, but why does this annotation need to be RetentionPolicy.Runtime?
The component scanning is done at the startup, not at the compilation.
Also, the aspects (AOP) are created at the runtime
The idea is to create annotations hierarchy (similar to #Service, #Component etc) using #AliasFor annotation. This should give me the possibility to define aspect, that would execute on parent annotation, and every alias of it. But somehow it doesn't work for me.
#ComponentScan is fine, #EnableAspectJAutoProxy is set.
Example:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.METHOD})
public #interface ParentAnnotation {
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#ParentAnnotation
public #interface ChildAnnotation {
#AliasFor(annotation = ParentAnnotation.class)
String value() default "";
}
#Aspect
#Component
public class EventRecorderAspect {
#Around("#annotation(com.example.ParentAnnotation)")
public void exampleMethod(ProceedingJoinPoint joinPoint) throws Throwable {
// This should be executed for both #ParentAnnotation and #ChildAnnotation
}
}
#RestController
public class ExampleController {
#ChildAnnotation // This should result in executing aspect for every implementation
String controllerMethod();
}
UPDATE:
I've updated code, as #M.Deinum suggested in a comment below. But it still doesnt work.
AspectJ pointcut matching syntax, a subset of which is used by Spring AOP, is ignorant of meta annotations, even though withing the Spring framework as such there is support for it in other places. I think the closest you can get to specifying meta annotations in your pointcut is to do it explicitly up to the nesting level you require. See this answer for examples showing the syntax variants for both
class-level, e.g. within(#(#com.example.ParentAnnotation *) *),
method-level, e.g. execution(#(#com.example.ParentAnnotation *) * *(..))
annotations.
Update: #Ariel Grabijas asked in a follow-up comment:
So is there a way to somehow inherit annotations from interface method to class method?
Not in Java and also not by means of Spring AOP. But there is a workaround using native AspectJ inter-type definitions (ITD).
I followed the doc (https://docs.jboss.org/weld/reference/latest/en-US/html/injection.html) to create qualifiers, now I'm getting a deployment error in wildfly-10.1.0.Final, I red a lot of similar questions on the internet but still no clue.
The code could compile and injections work well for other classes.
Here's the error:
"{
\"WFLYCTL0080: Failed services\" => {\"jboss.deployment.unit.\\\"test.war\\\".WeldStartService\" => \"org.jboss.msc.service.StartException in service jboss.deployment.unit.\\\"test.war\\\".WeldStartService: Failed to start service
Caused by: org.jboss.weld.exceptions.DeploymentException: Exception List with 2 exceptions:
Exception 0 :
org.jboss.weld.exceptions.DeploymentException: WELD-001409: Ambiguous dependencies for type MessageSender with qualifiers #Default
at injection point [BackedAnnotatedField] #Inject #B private test.AccessService.messageSenderB
at test.AccessService.MessageSenderB(AccessService.java:0)
Possible dependencies:
- Managed Bean [class test.messagesender.impl.MessageSenderBImpl] with qualifiers [#Any #Default],
- Managed Bean [class test.messagesender.impl.MessageSenderAImpl] with qualifiers [#Any #Default]
Here are the classes:
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ TYPE, METHOD, PARAMETER, FIELD })
#Documented
public #interface A{}
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ TYPE, METHOD, PARAMETER, FIELD })
#Documented
public #interface B{}
public interface MessageSender {}
#Singleton
#A
public class MessageSenderAImpl implements MessageSender {}
#Singleton
#B
public class MessageSenderBImpl implements MessageSender {}
#Singleton
public class AccessService {
#Inject
#A
private MessageSender messageSenderA;
#Inject
#B
private MessageSender messageSenderB;
Seems the first injection worked, the second one failed. Any idea?
I finally found it.
The stupid IDE auto imported the Qualifier class from spring framework
import org.springframework.beans.factory.annotation.Qualifier;
thought the correct one to use is
import javax.inject.Qualifier;
Some times interfaces are annotated with #Component annotation. Then my obvious reasoning was that classes that implement such interface will be treated as components as well. But if I am right that is not the case.
So what is the purpose of #Component annotation on interfaces.
Annotating an interface with #Component is common for Spring classes, particularly for some Spring stereotype annotations :
package org.springframework.stereotype;
...
#Component
public #interface Service {...}
or :
package org.springframework.boot.test.context;
...
#Component
public #interface TestComponent {...}
#Component is not declared as an inherited annotation :
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Component {...}
But whatever, during loading of the context, Spring discovers beans by considering the hierarchy of the annotation declared in the candidate class.
In the org.springframework.boot.BeanDefinitionLoader class (included in the Spring Boot dependency) that loads bean definitions from underlying sources, you can see an example of
org.springframework.core.annotation.AnnotationUtils.findAnnotation() that Spring uses to retrieve annotations in the whole hierarchy of the annotation:
class BeanDefinitionLoader {
...
private boolean isComponent(Class<?> type) {
// This has to be a bit of a guess. The only way to be sure that this type is
// eligible is to make a bean definition out of it and try to instantiate it.
if (AnnotationUtils.findAnnotation(type, Component.class) != null) {
return true;
}
// Nested anonymous classes are not eligible for registration, nor are groovy
// closures
if (type.getName().matches(".*\\$_.*closure.*") || type.isAnonymousClass()
|| type.getConstructors() == null || type.getConstructors().length == 0) {
return false;
}
return true;
}
...
}
Concretely, it means as the #Service annotation is itself annotated with #Component, Spring will consider a candidate class annotated with #Service as a bean to instantiate.
So, your guesswork is right :
Classes that implement such interface will be treated as components as
well.
But this works only for interfaces (such as #Service) that are Java annotations and not for plain interfaces.
For Spring classes, this way of doing makes sense (enriching actual stereotype for example) but for your own beans, using #Component for the interface rather than the implementation will not work and would bring more drawbacks than advantages :
it defeats in a same way the purpose of an interface that is above all a contract. It couples it to Spring and it supposes that you will always have a single implementation of the class. In this case, why using an interface ?
it scatters the reading of the class at two places while the interface doesn't need to have any Spring stereotype.
That is not the case there is no need to adding #component on an interface because it is not a bean as we can't create reference for it.
The main part is actually #autowired where you injection the dependecy.
For example
public interface SortAlog();
public class BubbleSortAlgo();
No we are following the dynamic binding and creating the object of interface but implementation is on the run time.
So #autowired is the one that will create the object internally and we have #component for bubbleSortAlgo and the only candidate for the injection, so it will get reference from there.
I hope I was able to make a point here.
I have a #SessionScoped #Named bean with a #Producer method for a user object:
#Named #SessionScoped
public class UserBean implements Serializable
{
//...
#Named #Produces #LoggedIn #SessionScoped
public MyUser getCurrentUser() {return user;}
}
This works fine in my setup (JBoss-7.1.1-Final) and it's no problem to access the user fields from JSF pages with #{currentUser.name}. The qualifier is org.jboss.seam.security.annotations.LoggedIn. Now I want to #Inject this user in a field in another #Named Bean:
#Named
public class FavBean implements Serializable
{
private #Inject #LoggedIn MyUser currentUser;
}
This gives me the error:
org.jboss.weld.exceptions.DeploymentException:
WELD-001409 Ambiguous dependencies for type [MyUser] with qualifiers [#Default] at
injection point [[field] #Inject #LoggedIn test.FavBean.currentUser].
Possible dependencies [[Managed Bean [class test.ejb.MyUser] with qualifiers
[#Any #Default],
Producer Method [MyUser] with qualifiers [#Any #Default] declared as [[method]
#Named #Produces #LoggedIn #SessionScoped public test.UserBean.getCurrentUser()]]]
I don't understand the first dependency Managed Bean [class test.ejb.MyUser] This class is a simple #Entity and deployed in an ebb.jar in a EAR. As a workaround I'm currently injecting the UserBean get the user from there.
This is because CDI searches for beans by type and your entity and the producer method return the same type. That's why it is ambiguous.
You need to define a new qualifier and annotate it with your producer method.
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER, TYPE})
public #interface CurrentUser {
}
Add this annotation to your producer method:
#Named #Produces #CurrentUser #LoggedIn #SessionScoped
public MyUser getCurrentUser() {return user;}
I had very similar problem, and I got some offline help. My problem was that where my service was, it was included in a deployed ear AND in my web project as well. It was an accidental duplication, drop one out, and it will work if it is your case as well.
here on the following picture I had the esb_khr inside the esb_khr_web, I removed. In eclipse: go to properties and deployment assembly.
I'm not an expert but I had a similar problem, and I fixed it in simpler way, by annotating the bean itself with #Alternative, so that the Producer is favored. Could be I'm missing some side effects but this worked as far as I could see / needed.
Please double check that you do not have multiple instances of beans.xml in your context. In my case I had beans.xml in WEB-INF and in META-INF. After removing beans.xml from META-INF, the similar issue got resolved.