Retrieve a bean in Spring using java.lang.annotation in runtime - java

I have a list of annotations which I have retrieved using
java.lang.reflect.Method.getDeclaredAnnotations()
Using these annotations, how can I retrieve spring beans which are tagged
with these annotations?
Pseudo code:
public interface Work{...}
#Component
#A1
public class SpringBean1 implements Work{}
#Component
#A2
public class SpringBean2 implements Work{}
public class Test
{
public void doSomething()
{
Annotation[] annotations = getAnnotationsListUsingReflection();
for(Annotation annotation: annotations)
{
Work work = /* Retrieve bean using annotation, how to do this? */
work.xyz();
......
}
}
}

Assuming your annotations are annotating the bean types and not their methods, you can use ListableBeanFactory#getBeanNamesForAnnotation(Class).
Find all names of beans whose Class has the supplied Annotation type,
without creating any bean instances yet.
AnnotationConfigApplicationContext is one implementation of that interface.
Given such an instance
AnnotationConfigApplicationContext ctx = ...;
String[] beanNames = ctx.getBeanNamesForAnnotation(Annotation.class);
Then iterate through the bean names and get each bean
for (String beanName : beanNames) {
Object bean = ctx.getBean(beanName);
// use it
}
Alternatively, ListableBeanFactory#getBeansWithAnnotation(Class) can do the work for you:
Find all beans whose Class has the supplied Annotation type, returning
a Map of bean names with corresponding bean instances.

Related

Unexpected error in spring boot constructor

I wrote a sample program like below.
#Configuration
public class MyclassName{
private final List<String> tableIds;
public MyclassName(
List<String> tableIds) {
this.tableIds = tableIds;
}
}
While running i am getting the below error.
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in MyclassName required a single bean, but 4 were found:
- spring.sleuth.baggage-keys: defined by method 'baggageKeys' in class path resource [org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfiguration.class]
- spring.sleuth.local-keys: defined by method 'localKeys' in class path resource [org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfiguration.class]
- spring.sleuth.propagation-keys: defined by method 'propagationKeys' in class path resource [org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfiguration.class]
- spring.sleuth.log.slf4j.whitelisted-mdc-keys: defined by method 'whiteListedMDCKeys' in class path resource [org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfiguration.class]
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
Process finished with exit code 0
We are using sleuth for tracing, so pom.xml has that dependencies aswell. But this is a very basic example, why iam getting this error. Can anyone help on this?
The class MyclassName is known to the spring framework.
So spring will try to create an instance of MyclassName by using the constructor:
public MyclassName(List<String> tableIds)
The Parameter 0 of constructor in MyclassName is List<String> tableIds
Spring is trying to provide a value for this parameter but instead of only one available value, it founds 4. So Spring is not intelligent enought to choose one in your place.
The 4 available values are provided by Sleuth and are declared in the TraceBaggageConfiguration.class like follow:
#Bean(BAGGAGE_KEYS)
#ConfigurationProperties(BAGGAGE_KEYS)
List<String> baggageKeys() {...}
#Bean(LOCAL_KEYS)
#ConfigurationProperties(LOCAL_KEYS)
List<String> localKeys() {...}
#Bean(PROPAGATION_KEYS)
#ConfigurationProperties(PROPAGATION_KEYS)
List<String> propagationKeys() {...}
#Bean(WHITELISTED_MDC_KEYS)
#ConfigurationProperties(WHITELISTED_MDC_KEYS)
List<String> whiteListedMDCKeys() {...}
To solve the indecision you need to tell Spring which List<String> you realy want injected in your constructor by using an unique identifier.
First qualify (give an id) to the expected bean
#Bean("myExpectedTableIds")
List<String> myTableIdsProcucerMethod()
Then use the org.springframework.beans.factory.annotation.Qualifier annotation to tell Spring which bean you realy want:
public MyclassName(#Qualifier("myExpectedTableIds") List<String> tableIds)
public MyclassName(
List<String> tableIds) {
this.tableIds = tableIds;
}
This means that your MyclassNames, as a Configuration bean in Spring, needs to inject a bean of type List<String>, it happens that slueth's TraceBaggageConfiguration class defines some beans of type List<String>:
#org.springframework.context.annotation.Bean({"spring.sleuth.baggage-keys"})
#org.springframework.boot.context.properties.ConfigurationProperties("spring.sleuth.baggage-keys")
java.util.List<java.lang.String> baggageKeys() { /* compiled code */ }
#org.springframework.context.annotation.Bean({"spring.sleuth.local-keys"})
#org.springframework.boot.context.properties.ConfigurationProperties("spring.sleuth.local-keys")
java.util.List<java.lang.String> localKeys() { /* compiled code */ }
#org.springframework.context.annotation.Bean({"spring.sleuth.propagation-keys"})
#org.springframework.boot.context.properties.ConfigurationProperties("spring.sleuth.propagation-keys")
java.util.List<java.lang.String> propagationKeys() { /* compiled code */ }
So spring reported an error, did not know which bean to inject. You may need to use Qualifier to specify the injected bean. Or, have you defined the corresponding bean which hash a List<String> type need to be injected? What is List<String> tableIds in your MyclassName? You need to define a Bean like this:
public MyclassNamesss(
#Qualifier("test") List<String> tableIds) {
this.tableIds = tableIds;
}
#Bean("test")
public List<String> myString() {
return new ArrayList<>();
}
Or you can use, in this case, the tableId will be null:
public MyclassName(
#Autowired(required = false) List<String> tableIds) {
this.tableIds = tableIds;
}

How to make a singleton by generic type with class argument in Spring?

Let's suppose I have a Wrapper with generic type:
#Component
#Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class Wrapper<T> {
private final Class<T> wrappedClass;
public Wrapper(Class<T> wrappedClass) {
this.wrappedClass = wrappedClass;
}
}
And I want to use this Wrapper with many classes (for example > 100). Is it possible to make Spring create singleton of wrapper for each generic type and pass generic class as parameter to constructor? For example, Spring must always inject the same instance of Wrapper<Foo>. If it is possible, please give example with java code configuration, but not with xml.
If I understood correctly you want to add beans of wrapper dynamically based on some criteria that some beans (like Foo / Bar) adhere to and some don't.
This is a kind of advanced stuff in spring, but in a nutshell you will have to implement a Bean Factory Post Processor that will be called automatically by spring during the startup.
This is a point where you could analyze the beans by iterating over all the "accessible" beans (like Foo / Bar and others) and for beans that should be wrapped you will create a bean definition of the wrapper, despite the fact that the wrapper itself is not a bean.
I've created a simple example to illustrate this. In my sample project I've put everything under package "wrappers":
#Wrappable
public class Foo {
}
#Wrappable
public class Bar {
}
public class ShouldNotBeWrapped {
}
Note that I've put an annotation #Wrappable - a custom annotation that will serve as a "differentiator" of what should be wrapped and what not. The processing of the annotation will be done in Bean Factory Post Processor.
The annotation is nothing special really, it should be acessible in runtime (spring is a runtime framework and be put on classes):
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface Wrappable {
}
The java config will add Foo, Bar, ShouldNotBeWrapped as beans and also Bean Factory Post Processor that I'll describe below:
#Configuration
public class WrappersJavaConfig {
#Bean
public Foo foo () {
return new Foo();
}
#Bean
public Bar bar () {
return new Bar();
}
#Bean
public ShouldNotBeWrapped shouldNotBeWrapped () {
return new ShouldNotBeWrapped();
}
#Bean
public WrappersEnrichmentBFPP wrappersEnrichmentBFPP () {
return new WrappersEnrichmentBFPP();
}
}
The Wrapper class itself for the sake of example has toString but it doesn't differ much from your wrapper presented in the question:
public class Wrapper<T> {
private T wrapped;
public Wrapper(T wrapped) {
this.wrapped = wrapped;
}
#Override
public String toString() {
return "Wrapper for" + wrapped;
}
}
And the Main class will list all the loaded beans and get their classes + call toString so that we could see that the wrappers are defined correctly:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(WrappersJavaConfig.class);
String[] names = ctx.getBeanDefinitionNames();
for(String name : names) {
Object bean = ctx.getBean(name);
if(bean.getClass().getPackage().getName().startsWith("wrappers")) {
System.out.println(ctx.getBean(name).getClass() + " ==> " + ctx.getBean(name));
}
}
}
}
Sidenote, the "if" condition in the main method is because I don't want to print the beans that spring loads by itself (infra stuff, etc) - only my beans which all reside in package "wrappers" as I've mentioned above.
Now the BeanFactoryPostProcessor - is a regular bean in a sense that it gets registered in the java config and it looks like this (your implementation might be different but the idea is the same):
public class WrappersEnrichmentBFPP implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] bddNames = beanFactory.getBeanDefinitionNames();
for(String bddName : bddNames) {
Object bean = beanFactory.getBean(bddName);
if(bean.getClass().isAnnotationPresent(Wrappable.class)) {
BeanDefinition wrappedBdd = BeanDefinitionBuilder.genericBeanDefinition(Wrapper.class)
.addConstructorArgReference(bddName)
.getBeanDefinition();
((BeanDefinitionRegistry)beanFactory).registerBeanDefinition("wrapperFor" + bddName, wrappedBdd);
}
}
}
}
So I'm getting all the beans one by one in for-each loop, then I'm asking whether the bean has an annotation "wrappable" on it in the if condition. If it has - it must be wrapped.
In this case I create an "artificial" bean definition for Wrapper and add a constuctor that will reference my bean that should be wrapped.
Then I register the bean definition by adding it to the application context.
Run the code above and you'll see the output similar to mine:
class wrappers.WrappersJavaConfig$$EnhancerBySpringCGLIB$$f88f147d ==> wrappers.WrappersJavaConfig$$EnhancerBySpringCGLIB$$f88f147d#1283bb96
class wrappers.Foo ==> wrappers.Foo#74f0ea28
class wrappers.Bar ==> wrappers.Bar#f6efaab
class wrappers.ShouldNotBeWrapped ==> wrappers.ShouldNotBeWrapped#3c19aaa5
class wrappers.WrappersEnrichmentBFPP ==> wrappers.WrappersEnrichmentBFPP#3349e9bb
class wrappers.Wrapper ==> Wrapper forwrappers.Foo#74f0ea28
class wrappers.Wrapper ==> Wrapper forwrappers.Bar#f6efaab
As you see, two last lines are lines that correspond to the wrapper beans created for the same instances of Foo and Bar but nothing was created for the ShouldNotBeWrapped bean
The APIs used are somewhat obscure and look outdated, but again its pretty advanced stuff and works at the level of spring container infra itself. Having said that, there are a lot of tutorials about BeanFactoryPostProcessor-s.
Since Using BFPPs is not a usual task, and although I've provided the solution, I don't see any real usage of it, wrappers can't be used "instead" of Foo or Bar classes, do not have their APIs, etc. Maybe you could explain why do you need wrappers over some beans. Usually people use Aspects/BeanPostProcessors (not BFPP but BPP) to wrap the class into dynamic proxy (cglib / java.lang.Proxy) and add an additional behavior, stuff like #Transactional, cache handling and so forth is implemented in spring with BeanPostProcessors, so consider checking this direction as well.
It is possible and in fact a feature in spring.
Spring can inject your dependency with the correct generic type.The following example is from spring documentation.
Suppose you have an interface
public interface Store<T>{...}
and two beans. One implements Store,one implemenets Store.
#Configuration
public class MyConfiguration {
#Bean
public StringStore stringStore() {
return new StringStore();
}
#Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
}
You can declare the type with the correct type parameter and spring will inject the right bean for you.
#Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean
#Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean

How to #Autowired a List<Integer> in spring framework

I have a configuration class as below:
#Configuration
public class ListConfiguration {
#Bean
public List<Integer> list() {
List<Integer> ints = new ArrayList<>();
ints.add(1);
ints.add(2);
ints.add(3);
return ints;
}
#Bean
public int number() {
return 4;
}
}
I also have a test class as below
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = ListConfiguration.class)
public class ListTest {
#Autowired
List<Integer> ints;
#Test
public void print() {
System.out.println(ints.size());
System.out.println(ints);
}
}
But the output of the print method is 1 and [4], why not 3 and [1,2,3]? Thank you very much for any help!
You've got a bean of type Integer and a bean of type List<Integer> in your application context.
Now obviously the bean you want to autowire is of type List<Integer>, which does qualify as a candidate for autowiring. To discover how Spring actually autowires fields I had to dive deep into the AutowiredAnnotationBeanPostProcessor class.
TL;DR of my investigation is that Spring will prefer to autowire objects in the following order:
Default Value using #Value
Multiple beans using a type parameter.
Individual beans that match the field type.
That means that if you're autowiring a List<Integer> Spring will attempt to autowire multiple Integer beans into the list before it will attempt to autowire a single List<Integer> bean.
You can see this behaviour in the DefaultListableBeanFactory class.
Relevant snippet below:
public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
Class<?> type = descriptor.getDependencyType();
//Searches for an #Value annotation and
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
//Handle finding, building and returning default value
}
/*
* Check for multiple beans of given type. Because a bean is returned here,
* Spring autowires the Integer bean instance.
*/
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
// Do more stuff here to try and narrow down to a single instance to autowire.
}
}
Hopefully this explains why you do need to use an #Qualifer annotation when trying to autowire a list of a type when you've got individual beans of that type in your application context.
EDIT:
It's worth noting that this is not good practice. Creating a collection of primitives or primitive wrappers and registering it as a bean is going to cause issues. The best way to do this is with #Value and define your list of primitives in a properties file, that Spring picks up.
Example:
application.properties file
list=1,2,3,4
In your config class declare the following bean:
#Bean
public ConversionService conversionService() {
return new DefaultConversionService();
}
The default conversion service is used to convert comma separated values declared in a properties file into a collection of objects with type safety.
Class to use it:
#Value("${list}")
private List<Integer> anotherList;
anotherList will contain 1,2,3 & 4 as elements in the list.
May be Spring is injecting all the Integer type beans into a List instead of Autowiring List<Integer> bean that you declared.
Probably if you add #Qualifier("list") at your injection point in your Test then it will provide the behavior you are expecting.

How to Autowire Bean of generic type <T> in Spring?

I have a bean Item<T> which is required to be autowired in a #Configuration class.
#Configuration
public class AppConfig {
#Bean
public Item<String> stringItem() {
return new StringItem();
}
#Bean
public Item<Integer> integerItem() {
return new IntegerItem();
}
}
But when I try to #Autowire Item<String>, I get following exception.
"No qualifying bean of type [Item] is defined: expected single matching bean but found 2: stringItem, integerItem"
How should I Autowire generic type Item<T> in Spring?
Simple solution is to upgrade to Spring 4.0 as 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
Infact, you can even autowire nested generics when injecting into a list, as below:
// Inject all Item beans as long as they have an <Integer> generic
// Item<String> beans will not appear in this list
#Autowired
private List<Item<Integer>> intItems;
How this Works?
The new ResolvableType class provides the logic of actually working with generic types. You can use it yourself to easily navigate and resolve type information. Most methods on ResolvableType will themselves return a ResolvableType, for example:
// Assuming 'field' refers to 'intItems' above
ResolvableType t1 = ResolvableType.forField(field); // List<Item<Integer>>
ResolvableType t2 = t1.getGeneric(); // Item<Integer>
ResolvableType t3 = t2.getGeneric(); // Integer
Class<?> c = t3.resolve(); // Integer.class
// or more succinctly
Class<?> c = ResolvableType.forField(field).resolveGeneric(0, 0);
Check out the Examples & Tutorials at below links.
Spring Framework 4.0 and Java Generics
Spring and Autowiring of Generic Types
If you dont want to upgrade to Spring 4 you have to autowire by name as below :
#Autowired
#Qualifier("stringItem")
private Item<String> strItem; // Injects the stringItem bean
#Autowired
#Qualifier("integerItem")
private Item<Integer> intItem; // Injects the integerItem bean
Below is a solution I made to answer this question:
List<String> listItem= new ArrayList<>();
ResolvableType resolvableType = ResolvableType.forClassWithGenerics(List.class, String.class);
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setTargetType(resolvableType);
beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
beanDefinition.setAutowireCandidate(true);
DefaultListableBeanFactory bf = (DefaultListableBeanFactory) configurableWebApplicationContext.getBeanFactory();
bf.registerBeanDefinition("your bean name", beanDefinition);
bf.registerSingleton("your bean name", listItem);
Spring autowired strategy is defined in your configration file(application.xml).
if you don't defined, default is by Type, spring inject use JDK reflect mechanism.
so List?String? and List?Item?, the type is same List.class, so spring confused how to inject.
and as above persons response, you should be point #Qualifier to tell spring which bean should be inject.
i like spring configration file to define bean rather then Annotation.
<bean>
<property name="stringItem">
<list>
<....>
</list>
</property>
Spring 4.0 is the answer with the #Qualifier annotation usage. Hope this helps
I believe it has nothing to with generics... If you are injecting two different beans of same type then you need to provide a qualifier to help Spring identify them;
... Elsewhere
#Configuration
#Bean
public Item stringItem() {
return new StringItem();
}
#Bean
public Item integerItem() {
return new IntegerItem();
}
If you have a non-generic declarations like these then you need to add qualifier to help Spring identify them...
#Autowired
**#Qualifier("stringItem")**
private Item item1;
#Autowired
**#Qualifier("integerItem")**
private Item item2;
Of course, in versions 4 and above spring considers the Generic Types through the resolvers which is very cool...

Request scoped field in a Singleton bean

I know it's possible to inject a request scoped bean into a singleton bean in Spring so I know what I'm trying to do will work, I'm just wondering if there is a way to express it more concisely without so many extra unnecessary class definitions. I'm new to Spring annotations so maybe there's an annotation I don't know about.
I have an abstract class that will be extended maybe 100 times in my application as different singleton spring beans. Take this class definition for an example:
/** The abstract class with a field that needs to be request-specific **/
public abstract class AbstractSingletonBean {
private SampleState state;
public SampleState getState() { return state; }
public void setState(SampleState state) { this.state = state; }
// Other fields that are just singleton here
}
And an example of what one of the bean definitions might look like:
#Component
public class SampleSingletonBean extends AbstractSingletonBean {
#Resource(name="sampleState")
public void setState(SampleState state) { super.setState(state); }
}
Now of course we need a bean called sampleState. So I have to create two classes: a base class to define the fields in SampleState and then a request-scoped bean definition. This is because each extension of AbstractSingletonBean will need it's own request-scoped instance of the state field.
Here might be the base class:
public class SampleState {
private String fieldOne;
public String getFieldOne() { return fieldOne }
public void setFieldOne() { this.fieldOne = fieldOne }
}
And here is this silly bean definition:
#Component ("sampleState")
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SampleStateBean extends SampleState {}
The thing that bothers me is that if I have 100 extensions of AbstractSingletonBean, I'll need 100 extensions of SampleStateBean with just boilerplate code to make it request-scoped. Is there a way to just override setState() in the extensions of AbstractSingletonBean and indicate to spring that it should create a new request scoped bean on the fly and inject it here? So my SampleSingletonBean could look like this:
#Component
public class SampleSingletonBean extends AbstractSingletonBean {
#Resource
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public void setState(SampleState state) { super.setState(state); }
}
Of course this doesn't work because #Resource needs to refer to a bean that already exists. Is there another annotation to accomplish this without creating a new class for every SampleState bean?
Spring can inject into abstract classes too. So you can move the injection of the SampleState to the abstract class, if each AbstractSingletonBean descendant needs just a SampleState (as in your example).
It doesn't look like this was available out of the box so I created an annotation I call #AnonymousRequest that I put on the field I want, and a BeanDefinitionRegistryPostProcessor to do the work of creating the bean. It basically goes like this:
for each bean in the BeanFactory
if bean class has AnonymousRequest annotation
create request scoped bean from field class
create singleton bean to be request scoped bean wrapper
set the annotated property value to the singleton wrapper
This took a lot of work to figure out how Spring registers request scoped beans. You create the bean definition you want as a request scoped bean. Then you create a singleton bean of type RootBeanDefinition that acts as a wrapper to the request scope bean and set a property on the wrapper called "targetBeanName" to whatever you named the request scoped bean ("scopedTarget." + the singleton bean name by convention).
So this could probably be improved by someone who actually knows this stuff but here's what I came up with:
public void createRequestBeanFromSetterMethod(String containingBeanName, BeanDefinition containingBean, Method method, BeanDefinitionRegistry registry)
{
String fieldName = ReflectionUtil.getFieldNameFromSetter(method.getName());
String singletonBeanName = containingBeanName+"_"+fieldName;
String requestBeanName = "scopedTarget."+singletonBeanName;
BeanDefinition requestBean = createAnonymousRequestBean(ReflectionUtil.getFieldTypeFromSetter(method), containingBean);
RootBeanDefinition singletonBean = new RootBeanDefinition();
singletonBean.setBeanClass(ScopedProxyFactoryBean.class);
singletonBean.getPropertyValues().addPropertyValue("targetBeanName", requestBeanName);
registry.registerBeanDefinition(singletonBeanName, singletonBean);
registry.registerBeanDefinition(requestBeanName, requestBean);
beanDefinition.getPropertyValues().addPropertyValue(fieldName, new RuntimeBeanReference(singletonBeanName));
}
private BeanDefinition createAnonymousRequestBean(Class<?> beanType, BeanDefinition parentBean)
{
BeanDefinition newBean = null;
if (parentBean != null)
{
newBean = new GenericBeanDefinition(parentBean);
}
else
{
newBean = new GenericBeanDefinition();
}
if (beanType != null)
{
newBean.setBeanClassName(beanType.getName());
}
newBean.setScope("request");
newBean.setAutowireCandidate(false);
// This would have come from the Proxy annotation...could add support for different values
String proxyValue = "org.springframework.aop.framework.autoproxy.AutoProxyUtils.preserveTargetClass";
BeanMetadataAttribute attr = new BeanMetadataAttribute(proxyValue, true);
newBean.setAttribute(proxyValue, attr);
return newBean;
}
It seems to work! I effectively have now a request scoped bean created just before the context initialization that is localized to this one containing bean. It's a request-scoped property, more or less.
You can try defining single SampleState request scope bean and then use spring's lookup method to inject this bean wherever you want to.That's works just fine with prototype-scope beans. Fingers crosses it would work with request scope as well.
AFAIK, there is no annotation support for lookup method as of now, so either use it's xml vis-a-vis
or have a look at javax.inject.Provider relevant question here

Categories

Resources