NoUniqueBeanDefinitionException for Named classes - java

I am Using Spring 3.2.2 and was wondering if there was a way to inject beans by class type without explicitly giving them a string name. Ex:
#Named
public MyClass{
}
#Named
public MyOtherClass extends MyClass{
}
#Named
public class Foo{
public void blah(){
MyClass myClass = context.getBean(MyClass.class);
}
}
This will generate:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [MyClass] is defined: expected single matching bean but found 2: myClass,myOtherClass
Is there a way to say "Use the one that matches the class name exactly" without using String names?
In other words I don't want to do:
#Named("MyClass")...
#Named("MyOtherClass")...
MyClass myClass = context.getBean("MyClass");
Thanks!

This is what § 5.4.5 of Spring manual suggest whenever you encounter non-unique dependency bean definition:
Abandon autowiring in favor of explicit wiring.
Avoid autowiring for a bean definition by setting its autowire-candidate attributes to false as described in the next section.
Designate a single bean definition as the primary candidate by setting the primary attribute of its element to true.
If you are using Java 5 or later, implement the more fine-grained control available with annotation-based configuration, as described in Section 5.9, “Annotation-based container configuration”.

For the example, you could do something like:
#Named
public class Foo{
public void blah(){
MyClass myClass = getBean(MyClass.class);
}
private <T> T getBean(Class<T> type) {
return context.getBean(Introspector.decapitalize(type.getSimpleName()), type);
}
}
But this will not work when using #Inject or #Autowire.
To force strict class matching when autowiring, you could replace the default AutowireCandidateResolver on the BeanFactory with a BeanFactoryPostprocessor, but don't seem a good idea as #Resource or #Qualify can solve the NUBDE problem.
For example: (Not tested)
public class StrictClassAutowireCandidateResolver implements AutowireCandidateResolver {
#Override
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
if (!bdHolder.getBeanDefinition().isAutowireCandidate()) {
return false;
}
if (descriptor == null) {
return true;
}
String className = null;
if (descriptor.getField() != null) {
className = descriptor.getField().getType().getName();
}
else if (descriptor.getMethodParameter() != null) {
className = descriptor.getMethodParameter().getParameterType().getName();
}
Class<?> clazz = null;
try {
clazz = Class.forName(className);
}
catch (Exception e) {
return false;
}
if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) {
// have no chances to be strict, let BeanFactory to find implementations.
return true;
}
return bdHolder.getBeanDefinition().getBeanClassName().equals(className);
}
#Override
public Object getSuggestedValue(DependencyDescriptor descriptor) {
return null;
}
}

Try using #Component (Service, Repository, Controller) on your classes, and #Autowired when you're injecting a bean.
EDIT: my bad, I didn't read the question too well. The problem is that you're actually having 2 instances of MyClass (since MyOtherClass extends from MyClass). Hence there's no other way than giving the classes names, or you'll always end up with NoUniqueBeanDefinitionExceptions.

Related

Receive annotation value from annotated field

Is it possible to receive annotation value inside a field, that was annotated?
Imagine that I have this interface:
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
String value();
}
And I have such implementation:
class SomeClass {
#MyAnnotation("Annotation")
private MyClass myClass;
}
What I want to understand is: is it possible to receive value of MyAnnotation inside MyClass? I want to implement a method inside class MyClass, which will return a value of assigned annotation. So, that myClass.getAssignedAnnotationValue() will return "Annotation".
If it is not possible, please inform me.
is it possible to know annotation value inside annotated field
It's not possible.
You may have 2 different classes
class SomeClass {
#MyAnnotation("Annotation")
private MyClass myClass;
public SomeClass(MyClass myClass) {
this.myClass=myClass;
}
}
and
class SomeClassNo Annotation {
private MyClass myClass;
public SomeClassNo(MyClass myClass) {
this.myClass=myClass;
}
}
Then you create an instance of MyClass
MyClass instance = new MyClass();
then 2 classes instances
new SomeClass(instance) and new SomeClassNo(instance) both have reference to the same instance. So the instance does not know whether the reference field annotated or not.
The only case when it is possible is to pass somehow the container reference to MyClass.
There is no straight forward way of implementing what you are asking.
WorkAround:
Limitations:
This workaround doesn't enforce any kind of compile time check and it is completely your responsibility to handle it.
This only works if MyClass is going to be a spring bean.
class MyClass {
public String annotatedValue;
}
You can write a Spring BeanPostProcessor the following way.
public class SampleBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
if (field instanceof MyClass && field.isAnnotationPresent(MyAnnotation.class)) {
String value = field.getDeclaredAnnotation(MyAnnotation.class).value();
((MyClass) field).annotatedValue = value;
return bean;
}
}
return bean;
}
}
The above BeanPostProcessor will be called for every bean during the app start up. It will check all the fields of a given bean to see if the field is of type MyClass. If it is, it will extract the value from the annotation and set it in the annotatedValue field.
The problem with this approach is that you can use MyAnnotation on any property in any class. You cannot enforce the annotation to be used only on MyClass.

#ConditionalOnProperty for lists or arrays?

I'm using Spring Boot 1.4.3 #AutoConfiguration where I create beans automatically based on properties user specifies. User can specify an array of services, where name and version are required fields:
service[0].name=myServiceA
service[0].version=1.0
service[1].name=myServiceB
service[1].version=1.2
...
If the user forgets to specify a required field on even just one service, I want to back-off and not create any beans. Can I accomplish this with #ConditionalOnProperty? I want something like:
#Configuration
#ConditionalOnProperty({"service[i].name", "service[i].version"})
class AutoConfigureServices {
....
}
This is the custom Condition I created. It needs some polishing to be more generic (ie not hardcoding strings), but worked great for me.
To use, I annotated my Configuration class with #Conditional(RequiredRepeatablePropertiesCondition.class)
public class RequiredRepeatablePropertiesCondition extends SpringBootCondition {
private static final Logger LOGGER = LoggerFactory.getLogger(RequiredRepeatablePropertiesCondition.class.getName());
public static final String[] REQUIRED_KEYS = {
"my.services[i].version",
"my.services[i].name"
};
#Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
List<String> missingProperties = new ArrayList<>();
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(context.getEnvironment());
Map<String, Object> services = resolver.getSubProperties("my.services");
if (services.size() == 0) {
missingProperties.addAll(Arrays.asList(REQUIRED_KEYS));
return getConditionOutcome(missingProperties);
}
//gather indexes to check: [0], [1], [3], etc
Pattern p = Pattern.compile("\\[(\\d+)\\]");
Set<String> uniqueIndexes = new HashSet<String>();
for (String key : services.keySet()) {
Matcher m = p.matcher(key);
if (m.find()) {
uniqueIndexes.add(m.group(1));
}
}
//loop each index and check required props
uniqueIndexes.forEach(index -> {
for (String genericKey : REQUIRED_KEYS) {
String multiServiceKey = genericKey.replace("[i]", "[" + index + "]");
if (!resolver.containsProperty(multiServiceKey)) {
missingProperties.add(multiServiceKey);
}
}
});
return getConditionOutcome(missingProperties);
}
private ConditionOutcome getConditionOutcome(List<String> missingProperties) {
if (missingProperties.isEmpty()) {
return ConditionOutcome.match(ConditionMessage.forCondition(RequiredRepeatablePropertiesCondition.class.getCanonicalName())
.found("property", "properties")
.items(Arrays.asList(REQUIRED_KEYS)));
}
return ConditionOutcome.noMatch(
ConditionMessage.forCondition(RequiredRepeatablePropertiesCondition.class.getCanonicalName())
.didNotFind("property", "properties")
.items(missingProperties)
);
}
}
Old question, but I hope my answer will help for Spring2.x:
Thanks to #Brian, I checked migration guide, where I was inspired by example code. This code works for me:
final List<String> services = Binder.get(context.getEnvironment()).bind("my.services", List.class).orElse(null);
I did try to get List of POJO (as AutoConfigureService) but my class differs from AutoConfigureServices. For that purpose, I used:
final Services services = Binder.get(context.getEnvironment()).bind("my.services", Services.class).orElse(null);
Well, keep playing :-D
Here's my take on this issue with the use of custom conditions in Spring autoconfiguration. Somewhat similar to what #Strumbels proposed but more reusable.
#Conditional annotations are executed very early in during the application startup. Properties sources are already loaded but ConfgurationProperties beans are not yet created. However we can work around that issue by binding properties to Java POJO ourselves.
First I introduce a functional interface which will enable us to define any custom logic checking if properties are in fact present or not. In your case this method will take care of checking if the property List is empty/null and if all items within are valid.
public interface OptionalProperties {
boolean isPresent();
}
Now let's create an annotation which will be metannotated with Spring #Conditional and allow us to define custom parameters. prefix represents the property namespace and targetClass represents the configuration properties model class to which properties should be mapped.
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Conditional(OnConfigurationPropertiesCondition.class)
public #interface ConditionalOnConfigurationProperties {
String prefix();
Class<? extends OptionalProperties> targetClass();
}
And now the main part. The custom condition implementation.
public class OnConfigurationPropertiesCondition extends SpringBootCondition {
#Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
MergedAnnotation<ConditionalOnConfigurationProperties> mergedAnnotation = metadata.getAnnotations().get(ConditionalOnConfigurationProperties.class);
String prefix = mergedAnnotation.getString("prefix");
Class<?> targetClass = mergedAnnotation.getClass("targetClass");
// type precondition
if (!OptionalProperties.class.isAssignableFrom(targetClass)) {
return ConditionOutcome.noMatch("Target type does not implement the OptionalProperties interface.");
}
// the crux of this solution, binding properties to Java POJO
Object bean = Binder.get(context.getEnvironment()).bind(prefix, targetClass).orElse(null);
// if properties are not present at all return no match
if (bean == null) {
return ConditionOutcome.noMatch("Binding properties to target type resulted in null value.");
}
OptionalProperties props = (OptionalProperties) bean;
// execute method from OptionalProperties interface
// to check if condition should be matched or not
// can include any custom logic using property values in a type safe manner
if (props.isPresent()) {
return ConditionOutcome.match();
} else {
return ConditionOutcome.noMatch("Properties are not present.");
}
}
}
Now you should create your own configuration properties class implementing OptionalProperties interface.
#ConfigurationProperties("your.property.prefix")
#ConstructorBinding
public class YourConfigurationProperties implements OptionalProperties {
// Service is your POJO representing the name and version subproperties
private final List<Service> services;
#Override
public boolean isPresent() {
return services != null && services.stream().all(Service::isValid);
}
}
And then in Spring #Configuration class.
#Configuration
#ConditionalOnConfigurationProperties(prefix = "", targetClass = YourConfigurationProperties.class)
class AutoConfigureServices {
....
}
There are two downsides to this solution:
Property prefix must be specified in two locations: on #ConfigurationProperties annotation and on #ConditionalOnConfigurationProperties annotation. This can partially be alleviated by defining a public static final String PREFIX = "namespace" in your configuration properties POJO.
Property binding process is executed separately for each use of our custom conditional annotation and then once again to create the configuration properties bean itself. It happens only during app startup so it shouldn't be an issue but it still is an inefficiency.
You can leverage the org.springframework.boot.autoconfigure.condition.OnPropertyListCondition class. For example, given you want to check for the service property having at least one value:
class MyListCondition extends OnPropertyListCondition {
MyListCondition() {
super("service", () -> ConditionMessage.forCondition("service"));
}
}
#Configuration
#Condition(MyListCondition.class)
class AutoConfigureServices {
}
See the org.springframework.boot.autoconfigure.webservices.OnWsdlLocationsCondition used on org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration#wsdlDefinitionBeanFactoryPostProcessor for an example within Spring itself.

DeltaSpike custom ConfigSource with CDI

I am trying to define a custom DeltaSpike ConfigSource. The custom config source will have the highest priority and check the database for the config parameter.
I have a ConfigParameter entity, that simply has a key and a value.
#Entity
#Cacheable
public class ConfigParameter ... {
private String key;
private String value;
}
I have a #Dependent DAO that finds all config parameters.
What I am trying to do now, is define a custom ConfigSource, that is able to get the config parameter from the database. Therefore, I want to inject my DAO in the ConfigSource. So basically something like
#ApplicationScoped
public class DatabaseConfigSource implements ConfigSource {
#Inject
private ConfigParameterDao configParameterDao;
....
}
However, when registering the ConfigSource via META-INF/services/org.apache.deltaspike.core.spi.config.ConfigSource, the class will be instantiated and CDI will not work.
Is there any way to get CDI working in this case?
Thanks in advance, if you need any further information, please let me know.
The main problem is, that the ConfigSource gets instantiated very early on when the BeanManager is not available yet. Even the JNDI lookup does not work at that point in time. Thus, I need to delay the injection/lookup.
What I did now, is add a static boolean to my config source, that I set manually. We have a InitializerService that makes sure that the system is setup properly. At the end of the initialization process, I call allowInitialization() in order to tell the config source, that the bean is injectable now. Next time the ConfigSource is asked, it will be able to inject the bean using BeanProvider.injectFields.
public class DatabaseConfigSource implements ConfigSource {
private static boolean allowInit;
#Inject
private ConfigParameterProvider configParameterProvider;
#Override
public int getOrdinal() {
return 500;
}
#Override
public String getPropertyValue(String key) {
initIfNecessary();
if (configParameterProvider == null) {
return null;
}
return configParameterProvider.getProperty(key);
}
public static void allowInitialization() {
allowInit = true;
}
private void initIfNecessary() {
if (allowInit) {
BeanProvider.injectFields(this);
}
}
}
I have a request-scoped bean that holds all my config variables for type-safe access.
#RequestScoped
public class Configuration {
#Inject
#ConfigProperty(name = "myProperty")
private String myProperty;
#Inject
#ConfigProperty(name = "myProperty2")
private String myProperty2;
....
}
When injecting the Configuration class in a different bean, each ConfigProperty will be resolved. Since my custom DatabaseConfigSource has the highest ordinal (500), it will be used for property resolution first. If the property is not found, it will delegate the resolution to the next ConfigSource.
For each ConfigProperty the getPropertyValue function from the DatabaseConfigSource is called. Since I do not want to retreive the parameters from the database for each config property, I moved the config property resolution to a request-scoped bean.
#RequestScoped
public class ConfigParameterProvider {
#Inject
private ConfigParameterDao configParameterDao;
private Map<String, String> configParameters = new HashMap<>();
#PostConstruct
public void init() {
List<ConfigParameter> configParams = configParameterDao.findAll();
configParameters = configParams.stream()
.collect(toMap(ConfigParameter::getId, ConfigParameter::getValue));
}
public String getProperty(String key) {
return configParameters.get(key);
}
}
I could sure change the request-scoped ConfigParameterProvider to ApplicationScoped. However, we have a multi-tenant setup and the parameters need to be resolved per request.
As you can see, this is a bit hacky, because we need to explicitly tell the ConfigSource, when it is allowed to be instantiated properly (inject the bean).
I would prefer a standarized solution from DeltaSpike for using CDI in a ConfigSource. If you have any idea on how to properly realise this, please let me know.
Even though this post has been answered already I'd like to suggest another possible solution for this problem.
I managed to load properties from my db service by creating an #Signleton #Startup EJB which extends the org.apache.deltaspike.core.impl.config.BaseConfigSource and injects my DAO as delegate which I then registered into the org.apache.deltaspike.core.api.config.ConfigResolver.
#Startup
#Singleton
public class DatabaseConfigSourceBean extends BaseConfigSource {
private static final Logger logger = LoggerFactory.getLogger(DatabaseConfigSourceBean.class);
private #Inject PropertyService delegateService;
#PostConstruct
public void onStartup() {
ConfigResolver.addConfigSources(Collections.singletonList(this));
logger.info("Registered the DatabaseConfigSourceBean in the ConfigSourceProvider ...");
}
#Override
public Map<String, String> getProperties() {
return delegateService.getProperties();
}
#Override
public String getPropertyValue(String key) {
return delegateService.getPropertyValue(key);
}
#Override
public String getConfigName() {
return DatabaseConfigSourceBean.class.getSimpleName();
}
#Override
public boolean isScannable() {
return true;
}
}
I know that creating an EJB for this purpose basically produces a way too big overhead, but I think it's a bit of a cleaner solution instead of handling this problem by some marker booleans with static accessors ...
DS is using the java se spi mechanism for this which is not CD'Injectable'. One solution would be to use the BeanProvider to get hold of your DatabaseConfigSource and delegate operations to it.

How to programmatically lookup and inject a CDI managed bean where the qualifier contains the name of a class

I am trying to programmatically lookup and inject a CDI managed bean where the qualifier contains the name of a class (not the class I want to inject), however the problem I've got is that the code I'm using to lookup the correct bean always returns with null.
The beans I want to inject are annotated with a custom annotation called #CQRSCommandHandler which contains the name of a class being used as a qualifier and the beans also implement an interface called CommandHandler. The classes I'm using qualifier implement the interface Command.
Based on my somewhat limited knowledge of CDI, I believe that in order to programmatically lookup the correct bean which has been qualified with the #CQRSCommandHandler annotation, I need to extend AnnotationLiteral and I can then use Instance to select the bean.
The code for the #CQRSCommandHandler annotation is as follows:
#Qualifier
#Documented
#Retention(value= RetentionPolicy.RUNTIME)
public #interface CQRSCommandHandler {
Class<? extends Command> command();
}
The code for extending AnnotationLiteral is as follows:
public class CQRSCommandHandlerQualifier extends AnnotationLiteral<CQRSCommandHandler> implements CQRSCommandHandler {
private static final long serialVersionUID = 1L;
private final Class<? extends Command> command;
public CQRSCommandHandlerQualifier(Class<? extends Command> command) {
this.command = command;
}
#Override
public Class<? extends Command> command() {
return command;
}
}
The code I'm using to lookup the correct bean using CDI is as follows:
#Inject
#Any
private Instance<CommandHandler> commandHandlerInstance;
private CommandHandler findCommandHandlerFor(Command command) {
CommandHandler commandHandler = commandHandlerInstance.select(new CQRSCommandHandlerQualifier(command.getClass())).get(); //This always returns null
return commandHandler;
}
Despite many hours of google searching I can't work out why commandHandlerInstance.select(new CQRSCommandHandlerQualifier(command.getClass())).get(); does not return an instance of a bean which has been annotated with #CQRSCommandHandler (command = MyCommand.class) where the bean implements the CommandHandler interface and MyCommand.class implements the interface Command.
Is this the correct way to programmatically lookup and inject a CDI managed bean where the qualifier contains the name of a class? If so, where am I going wrong with the above code? If not, what is the best way to achieve the same end result?
Update
The following code is an example implementation of a bean that I'm trying to lookup:
#CQRSCommandHandler(command = CreateToDoItemCommand.class)
public class CreateToDoItemCommandHandler implements CommandHandler {
#Override
public <R> Object handle(Command command) {
System.out.println("This is the CreateToDoItemCommandHandler");
return null;
}
}
The following code is the interface for CommandHandler:
public interface CommandHandler {
public <R> Object handle(Command command);
}
The following code is an example of the class I'm using as a parameter in the qualifier:
public class CreateToDoItemCommand implements Command {
private String todoId;
private String description;
public CreateToDoItemCommand(String todoId, String description) {
this.todoId = todoId;
this.description = description;
}
public String getTodoId() {
return todoId;
}
public String getDescription() {
return description;
}
}
I've stepped through the code in Eclipse and it seems that the Instance object of commandHandlerInstance is null.
Update 2
As suggested by #redge I've separate each step of the instantiation onto a separate line as follows:
private CommandHandler findCommandHandlerFor(Command command) {
CQRSCommandHandlerQualifier qualifier = new CQRSCommandHandlerQualifier(command.getClass());
Instance<CommandHandler> instance = commandHandlerInstance.select(qualifier);
CommandHandler commandHandler = instance.get();
return commandHandler;
}
The issue seems to be with this line of code Instance<CommandHandler> instance = commandHandlerInstance.select(qualifier); where NullPointerException is thrown presumably because the Instance object commandHandlerInstance is null
I'm running this code on GlashFish 4 which ships with Weld 2.0.0 SP1, but I've also just tried running the same code on GlashFish 4.1 and have installed Weld version 2.2.10.SP1 which is the latest from Maven Central but the same issue occurs.
You have GlassFish 4.1. I doubt you have a beans.xml file, which if you do should be marked as bean-discovery-mode="all" based on your current setup. If you don't, or you use bean-discovery-mode="annotated" then you'll need to add a bean defining annotation to each of your commands, e.g. #ApplicationScoped for each command so that they can be resolved.

Spring: how to get an applicable generic bean instance by its type argument(s)?

I'm using Spring 4.1.2 and I have the following code:
public class Foo {
}
public class Bar {
}
public interface Service<T> {
}
#Service("fooService")
public class FooServiceImpl implements Service<Foo> {
}
#Service("barService")
public class BarServiceImpl implements Service<Bar> {
}
I know that Spring 4 can inject generic bean instances like the following:
#Autowired
private Service<Foo> service; // works fine
But I need to obtain them in a static way like the following:
Service<Foo> service = getService(getContext(), Foo.class);
...
public static <T> Service<T> getService(ApplicationContext context,
Class<T> objectClass) {
...
}
I tried to use ApplicationContext.getBeansOfType(Service.class) but it returns all available bean instances (fooService and barService). So I need to pass type arguments somehow.
Is there any way to do this? Something like this:
#SupressWarnings("unchecked")
Service<Foo> service = applicationContext.getGenericBean(
Service.class, // bean class
Foo.class // type arguments
// ...
);
Getting generic beans programmatically from the application context:
String[] beanNames = applicationContext.getBeanNamesForType(ResolvableType.forType(new ParameterizedTypeReference<String>() {}));
if (beanNames.length > 0) {
String bean = (String) applicationContext.getBean(beanNames[0]);
}
Or check this answer if you're interested in a mechanism to handle generic objects with particular handler.

Categories