Make a member of a bean available for autowiring - java

#Component
class MultiProvider {
public Foo getFoo();
public Bar getBar();
}
#Component
class FooConsumer {
FooConsumer(Foo f);
}
Can I have MultiProvider.getFoo() autowired into the FooConsumer constructor..
without making Foo a bean itself (for example, because Spring should not destroy it, since that is MultiProviders responsibility)
and without introducing a dependency from FooConsumer to MultiProvider (or any other class)?

You can achieve this simply by annotating getFoo() method in MultiProvider by #Bean
#Component
class MultiProvider {
#Bean(destroyMethodName="cleanup") // HERE IS THE TRICK
public Foo getFoo();
public Bar getBar();
}
#Component
class FooConsumer {
FooConsumer(Foo f);
}
if the problem comes from the point that spring can not properly destroy it you can include the logic inside cleanup method declared while annotating by #Bean
public class Foo {
public void cleanup() {
// destruction logic
}
}
Note that #component and #configurable are more or less the same with
some subtle differences but in your case you can use #component if you don't
want to change it. More Info

Spring can only autowire declared beans, a possible workaround can be something like the following:
#Component
class FooConsumer {
private final Foo foo;
FooConsumer(MultiProvider multiProvider) {
// MultiProvider will be autowired by spring - constructor injection
this.foo = multiProvider.getFoo();
}
}

You can include them in your Configuration.
#Configuration
class MyConfig {
#Bean
public MultiProvider getMP() {
return new MultiProvider() ;
}
#Bean
public Foo getFoo() {
return getMP(). getFoo();
}
}
Not sure if that violates your 'not a Bean itself' rule.

Related

Why do we use a qualifier when we can have a name for the bean?

Why do we use qualifiers with #Bean when we can have different names for different beans of the same type (class)?
#Bean
#Qualifier("fooConfig")
public Baz method1() {
}
Isn't the following code more clean?
#Bean("fooConfig")
public Baz method1() {
}
If I create two beans of the same type with different names (using #Bean annotation), then can we inject them specifically using the #Qualifier annotation(can be added on field/constructor parameter/setter) in another bean?
#Bean("fooConfig")
public Baz method1(){
}
#Bean("barConfig")
public Baz method2(){
}
// constructor parameter of a different bean
final #Qualifier("fooConfig") Baz myConfig
If the above is true, then where do we use #Qualifier (with #Bean or #Component) instead of giving the bean a name as shown below?
#Bean
#Qualifier("fooConfig")
public Baz method1(){
}
#Bean
#Qualifier("barConfig")
public Baz method2(){
}
// constructor parameter of a different bean
final #Qualifier("fooConfig") Baz myConfig
Beans have names. They don't have qualifiers. #Qualifier is annotation, with which you tell Spring the name of Bean to be injected.
No.
Default Qualifier is the only implementation of the interface(example is below, 4th question) or the only method with a particular return type. You don't need to specify the #Qualifier in that case. Spring is smart enough to find itself.
For example:
#Configuration
public class MyConfiguration {
#Bean
public MyCustomComponent myComponent() {
return new MyCustomComponent();
}
}
If you will try to inject myComponent somewhere, Spring is smart enough to find the bean above. Becaude there is only one Bean with return type MyCustomComponent. But if there was a couple of methods, that would return MyCustomComponent, then you would have to tell Spring which one to inject with #Qualifier annotation.
SIDENOTE: #Bean annotation by default Spring uses the method name as a bean name. You can also assign other name like #Bean("otherComponent").
You have one Interface, and a couple of Classes implementing it. You inject bean of your interface. How can Spring know which Class should be used?
This is you interface:
public interface TestRepository{}
This is your implementation 1:
#Repository
public class Test1Repository implements TestRepository{}
Your implementation 2:
#Repository
public class Test2Repository implements TestRepository{}
Now you are injecting it like:
private final TestRepository testRepository;
public TestServiceImpl(TestRepository testRepository) {
this.testRepository= testRepository;
}
QUESTION! How is Spring supposed to know which class to inject? Test1 or Test2? That's why you tell it with #Qualifier which class.
private final TestRepository testRepository;
public TestServiceImpl(#Qualifier("test1Repository") TestRepository testRepository) {
this.testRepository= testRepository;
}
I Prefer different method to not using #Qualifier
Create common Interface
public interface CommonFooBar{
public String commonFoo();
public String commonBar();
}
Extends to each service
public interface FooService extends CommonFooBar {
}
public interface BarService extends CommonFooBar {
}
Then using it to your class
#Autowired
FooService fooService;
or
#Autowired
BarService barService;
so, we can defined the single responsibility to each interface and This kind of segregation is more readable to every junior.
I quite like a different way of working. Surely if you provide a unique name for your bean, then that is all you need?
Given the example below, its easy to see that Spring will name the beans based on the method name used to create the beans. In other words, if you give your beans sensible names, then the code should become self-explanatory. This also works when injecting beans into other classes.
The end result of this is:
Spring will name your beans based on the method used to create them.
If you import a bean, Spring will try to match on the bean name.
If you try to import a bean that does not match the name, Spring will attempt to match the class.
If your injected field name does not match the bean name and there are more than one instance of your bean, Spring will throw an exception on startup as it won't know which one to inject.
Lets not over-complicate Spring.
#Bean
mqConnectionFactory() {
ConnectionFactory connectionFactory = new MQXAConnectionFactory();
return connectionFactory;
}
#Bean
public ConnectionFactory pooledConnectionFactory(ConnectionFactory mqconnectionFactory) {
JmsPoolConnectionFactory connectionFactory = new JmsPoolConnectionFactory();
connectionFactory.setConnectionFactory(mqConnectionFactory);
return connectionFactory;
}
#Bean
public ConnectionFactory cachingConnectionFactory(ConnectionFactory mqConnectionFactory) {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setTargetConnectionFactory(mqConnectionFactory);
return connectionFactory;
}
#Bean
public JmsTemplate jmsTemplate(ConnectionFactory cachingConnectionFactory) {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(cachingConnectionFactory);
return jmsTemplate;
}
#Bean
public DefaultMessageListenerContainer messageListenerContainer(ConnectionFactory pooledConnectionFactory) {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(pooledConnectionFactory);
...
return container;
}

How to #MockBean when there are 2 of the same type?

Going to try to provide simplified example (hopefully I don't mess up somewhere)
Let's say I have 2 #Beans of the same type, but one of them is annotated
#Configuration
public class FooProvider {
#Bean
public Foo foo1() {...}
#Bean
#SpecialFoo
public Foo foo2() {...}
}
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
public #interface SpecialFoo {}
In my application, Spring seems to be ok with having one annotated bean and one un-annotated bean. The annotation is a #Qualifier
#Configuration
public class BarProvider {
#Bean
public Bar bar1(Foo foo1) {...}
#Bean
public Bar bar2(#SpecialFoo Foo foo2) {...}
}
All of that works. Program loads up and Spring doesn't complain.
But the issue arises when I try to test it.
#ExtendWith(SpringExtension.class)
#SpringBootTest(webEnvironment = WebEnvironment.MOCK)
#AutoConfigureWebTestClient
public class MyTest {
#MockBean
Foo foo1;
#MockBean #SpecialFoo
Foo foo2;
}
At this point, I get an error like
No qualifying bean of type 'Foo' available: expected single matching bean but found 2: foo1, foo2
What am I doing wrong?
Special request: I would prefer not to have to annotate foo1 also or add a #Qualifier just because it's a lot of places I need to change. I'd rather the default bean be the un-annotated one.
This is what I found works
#ExtendWith(SpringExtension.class)
#SpringBootTest(webEnvironment = WebEnvironment.MOCK)
#AutoConfigureWebTestClient
public class MyTest {
#MockBean(name = "foo1")
Foo foo1;
#MockBean #SpecialFoo
Foo foo2;
}
I needed to tell Spring which Foo #Bean I was replacing with this mock by giving it the name of the bean it's replacing. If the MockBean has a #Qualifier or #SpecialFoo Qualifier annotation, then it doesn't need this naming. So only un-annotated bean requires this.
To answer my question in the comment, it seems these 2 are equivalent:
#MockBean(name = "foo1")
and
#MockBean #Qualifier("foo1")
Hope this helps others
Like this
#Configuration
public class FooProvider {
#Bean("foo1")
public Foo foo1() {...}
#Bean("foo2")
public Foo foo2() {...}
}
...
#Autowire
#Qualifier("foo1")
Foo foo1;
#Autowire
#Qualifier("foo2")
Foo foo2;
Try adding like:
#Primary
public Foo foo1() {
...
}

How to implement OR logic for spring qualifiers?

I have following configuration:
#Qualifier1
#Qualifier2
#Bean
public MyBean bean1(){...}
#Qualifier2
#Qualifier3
#Bean
public MyBean bean2(){...}
#Qualifier1
#Qualifier2
#Qualifier3
#Bean
public MyBean bean3(){...}
#Qualifier3
#Bean
public MyBean bean4(){...}
#Qualifier1
#Bean
public MyBean bean5(){...}
And it is the injection place:
#Qualifier2
#Qualifier3
#Autowired:
private List<MyBean> beans;
By default spring uses AND logic for each #Qualifier
So bean2 and bean3 will be injected.
But I want to have OR logic for that stuff so I expect beans bean1 bean2 bean3 and bean4 to be injected
How can I achieve it?
P.S.
#Qualifier annotation is not repeatable so I have to create meta annotation for each annotation:
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface Qualifier1 {
}
What if you used marker interfaces instead of qualifiers? For example:
public class MyBean1 extends MyBean implements Marker1 {}
public class MyBean2 extends MyBean implements Marker2 {}
public class MyBean12 extends MyBean implements Marker1, Marker2 {}
Then using this:
#Bean
public MyBean1 myBean1() {
//...
}
#Bean
public MyBean2 myBean2() {
//...
}
#Bean
public MyBean12 myBean12() {
//...
}
and this:
#Autowired private List<Marker1> myBeans;
You would get a list of myBean1 and myBean12 beans.
And for this:
#Autowired private List<Marker2> myBeans;
You would get a list of myBean2 and myBean12 beans.
Will this work?
UPDATE I
Custom FactoryBean
I implemented TagsFactoryBean class and #Tags annotation which you can use to solve your task (I hope :)).
First, mark your beans with #Tags annotation:
#Tags({"greeting", "2letters"})
#Bean
public Supplier<String> hi() {
return () -> "hi";
}
#Tags({"parting", "2letters"})
#Bean
public Supplier<String> by() {
return () -> "by";
}
#Tags("greeting")
#Bean
public Supplier<String> hello() {
return () -> "hello";
}
#Tags("parting")
#Bean
public Supplier<String> goodbye() {
return () -> "goodbye";
}
#Tags("other")
#Bean
public Supplier<String> other() {
return () -> "other";
}
Then prepare TagsFactoryBean:
#Bean
public TagsFactoryBean words() {
return TagsFactoryBean.<Supplier>builder()
.tags("greeting", "other")
.type(Supplier.class)
.generics(String.class)
.build();
}
Here tags is an array of desired tags whose beans should be selected, type is a selected beans type, and generics is an array of generic types of the beans. The last parameter is optional and should be used only if your beans are generic.
Then you can use it with #Qualifier annotation (otherwise Spring injects all beans of Supplier<String> type):
#Autowired
#Qualifier("words")
private Map<String, Supplier<String>> beans;
The Map beans will contain three beans: hi, hello and other (their name are keys of the Map and their instances are its values).
More usage examples you can find in tests.
UPDATE II
Custom AutowireCandidateResolver
Thanks to #bhosleviraj recommendation, I implemented TaggedAutowireCandidateResolver that simplifies the process of autowiring the desired beans. Just mark your beans and the autowired collection with the same tags and you will get them injected into the collection:
#Autowired
#Tags({"greeting", "other"})
private Map<String, Supplier<String>> greetingOrOther;
#Configuration
static class Beans {
#Tags({"greeting", "2symbols", "even"})
#Bean
public Supplier<String> hi() {
return () -> "hi";
}
#Tags({"parting", "2symbols", "even"})
#Bean
public Supplier<String> by() {
return () -> "by";
}
#Tags({"greeting", "5symbols", "odd"})
#Bean
public Supplier<String> hello() {
return () -> "hello";
}
#Tags({"parting", "7symbols", "odd"})
#Bean
public Supplier<String> goodbye() {
return () -> "goodbye";
}
#Tags({"other", "5symbols", "odd"})
#Bean
public Supplier<String> other() {
return () -> "other";
}
}
You can use not only the Map for injecting beans but also other Collections.
To make it work you have to register a CustomAutowireConfigurer bean in your application and provide it with TaggedAutowireCandidateResolver:
#Configuration
public class AutowireConfig {
#Bean
public CustomAutowireConfigurer autowireConfigurer(DefaultListableBeanFactory beanFactory) {
CustomAutowireConfigurer configurer = new CustomAutowireConfigurer();
beanFactory.setAutowireCandidateResolver(new TaggedAutowireCandidateResolver());
configurer.postProcessBeanFactory(beanFactory);
return configurer;
}
}
More usage examples see in this Test.
Answer requires deep understanding of how autowiring resolution is implemented in Spring, so we can extend it.
I couldn't come up with any solution yet, but I can give you some pointers.
Possible candidate to extend is QualifierAnnotationAutowireCandidateResolver , override method that resolves to a qualified bean. And pass the custom autowire resolver to the bean factory.
You can clone source code and correct version branch from here:
https://github.com/spring-projects/spring-framework
There is a CustomAutowireConfigurerTests in spring-beans module, that might help you understand few things.
I guess you can't do it by using annotation.
What I'd use is the org.springframework.context.ApplicationContextAware Maybe you need to write some extra code but in this way you can solve your issue.
I'd implement a class like this:
#Component
public class SpringContextAware implements ApplicationContextAware {
public static ApplicationContext ctx;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ctx = applicationContext;
}
public static synchronized ApplicationContext getCtx() {
return ctx;
}
}
Then in all beans where you need the OR logic you want you can do something like this:
#Autowired
private SpringContextAware ctxAware;
#PostConstruct
public void init() {
//Here you can do your OR logic
ctxAware.getCtx().getBean("qualifier1") or ctxAware.getCtx().getBean("qualifier2")
}
Will this solve your issue?
Angelo

Changing a class annotated #Component to #Bean annotated method

I have a class that is annotated #Component that was then #Autowired into another class. However, I need to remove this #Component annotation and instead, create it with an #Bean annotated method in the class where its was previously autowired.
Where previously the classes looked like:
#Component
public class MyClass implements IMyClass
{
// Stuff
}
#Configuration
public class MyUsingClass
{
#Autowired
private IMyClass myClass;
private void methodUsingMyClass()
{
myClass.doStuff();
}
}
So now I have removed the #Component annotation and written a #Bean annotated method like this:
public class MyClass implements IMyClass
{
// Stuff
}
#Configuration
public class MyUsingClass
{
#Bean
public IMyClass getMyClass()
{
return new MyClass();
}
....
}
My question is around replacing the previous call of myClass.doStuff() to use the new bean. Do I now pass in a parameter of type MyClass to the private method:
private void methodUsingMyClass(final MyClass myClass)
{
myClass.doStuff();
}
... or do I call this method directly (doesn't seem the correct way to me):
private void methodUsingMyClass()
{
getMyClass().doStuff();
}
... or are neither of these correct?
I think you misunderstand the #Bean annotation. It can be used to create a Bean. So basically spring will scan all classes, will find your #Bean and create a Bean, not more. You can now use this bean, like if you would use one created with <bean></bean>. To actually use the bean you need to either get it from ApplicationContext or #Autowire it. Of course you can still use that function like any other function in your code, to create a new instance of that object, but that would contradict to what you want to achieve with beans
Using Annotations that solutions
public class MyClass implements IMyClass{
private OtherClassInjection otherClassInjection;
private OtherClassInjection2 otherClassInjection2;
MyClass(OtherClassInjection otherClassInjection, OtherClassInjection2 otherClassInjection2){
this.otherClassInjection=otherClassInjection;
this.otherClassInjection2=otherClassInjection2;
}
public void useObject(){
otherClassInjection.user();
}
}
#Bean(name = "myClass")
#Autowired
#Scope("prototype") //Define scope as needed
public MyClass getMyClass(#Qualifier("otherClassInjection") OtherClassInjection otherClassInjection,
OtherClassInjection2 otherClassInjection2) throws Exception
{
return new MyClass(otherClassInjection, otherClassInjection2);
}
that logical, it's work injection #Autowired when create a Bean if context are know that bean, that you will to want inject.
I'm use that way.

Inject collection of beans to constructor using annotations

I am working on spring based project where we use constructor injection and configure context using #Configuration.
Here is the simplified example that shows my problem.
I have bean MyMainBean that refers to collection of Foo beans:
public class MyMainBean {
private Collection<Foo> foos;
public MyMainBean(Collection<Foo> foos) {
this.foos = foos;
}
}
Here is the bean Foo:
public class Foo {
private final String name;
public Foo(String name) {
this.name = name;
}
public void foo(String arg) {
System.out.println("foo (" + name + "): " + arg);
}
}
Here is how configuration class looks like:
#Configuration
public class AppConfig {
#Bean
public MyMainBean myMain(Collection<Foo> foos) {
return new MyMainBean(foos);
}
#Bean
public Collection<Foo> foos() {
System.out.println("foos");
return Arrays.asList(new Foo("colletion1"), new Foo("colletion2"));
}
}
When I run this I get exception message:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.hello.impl.Foo] found for dependency [collection of com.hello.impl.Foo]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
The message is pretty clear, so although it is not what I need I added the following methods to AppConfig:
#Bean
public Foo foo1() {
System.out.println("foo1");
return new Foo("single1");
}
and similar foo2()
Now the context runs and beans are wired. But although foo1(), foo2() and foos() are called the MyAppBean receives in its constructor collection that contains 2 elements created by foo1() and foo2().
I want to get foos() working because in my real code similar method retrieves the list of Foo dynamically using configuration. I believe that some magic annotations are missing here because I can create list of beans using context.xml, but I have to use programmatically created context here.
As a workaround I can create FooFactory bean that will expose method getFoos() and wire this factory to MyMain, however this looks ugly. Is there better solution?
Remarks
Attempts to add #Qualifier did not help
Attempts to work with #Autowire and #Resource instead of constructor injection did not help too.
Since both #Bean are declared in the same AppConfig you can fix your issue like:
#Bean
public MyMainBean myMain() {
return new MyMainBean(foos());
}
In case of different #Configuration classes the #Resource comes to the rescue:
#Resource(name="foos")
private Collection<Foo> foos;
The #Autowire doesn't help in this case even with #Qualifier.
A bit late but here we are :
#Configuration
public class AppConfig {
// Answer 1 : Inject the "foos" collection bean using #Value instead of #Qualifier
#Bean
public MyMainBean myMain1(#Value("#{foos}") Collection<Foo> foos) {
return new MyMainBean(foos);
}
// Answer 2 : call directly foos() since #Configuration bean is proxified,
// "foos" collection bean will only be called and instanciated once (at first call)
#Bean
public MyMainBean myMain2() {
return new MyMainBean(foos());
}
#Bean
public Collection<Foo> foos() {
System.out.println("foos");
return Arrays.asList(new Foo("colletion1"), new Foo("colletion2"));
}
}

Categories

Resources