Spring: Mixing of wiring in JavaConfig with wiring using #Autowired - java

I am totally confused about mixing of "wiring in JavaConfig" and "wiring using #Autowired". I will tell you my problems in 4 scenarios:
(I am ok with mixing of #Autowired and stereotype annotations and I don't have any question about that. my problem is Javaconfig and #autowired)
Scenario 1:
My CDPlayer Class:
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
public CDPlayer() {
cd = new CompactDisc() {
#Override
public void play() {
System.out.println("123456");
}
};
}
#Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
}
My JavaConfig File:
#Configuration
public class CDPlayerConfig {
#Bean
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
#Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer();
}
}
For Example in this scenario, I see that #Autowired is effectless and cannot make Spring to invoke and use the parameterized constructor and no-arg constructor will be executed (because it is invoked in the #Bean method) and the output is the text "123456".
=================================================================
SCENARIO 2:
My JavaConfig File:
#Configuration
public class CDPlayerConfig {
#Bean
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
#Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer(compactDisc);
}
}
My CDPlayer Class:
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
}
we wired those two beans in the config file. and I know that we do not need #Autowired at all.
=================================================================
SCENARIO 3:
My JavaConfig File:
#Configuration
public class CDPlayerConfig {
#Bean()
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
#Bean
public CDPlayer cdPlayer() {
return new CDPlayer();
}
}
My CDPlayer Class:
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
#Autowired
public void setCd(CompactDisc cd) {
this.cd = cd;
}
}
I know that if #Autowired is above of parameterized constructor, that constructor will not be executed but now that is above of setCd(), this method will be executed.
=================================================================
SCENARIO 4:
My JavaConfig File:
#Configuration
public class CDPlayerConfig {
#Bean
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
#Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer(compactDisc);
}
}
My CDPlayer Class:
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
public CDPlayer() {
}
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
#Autowired
public void doSomething(CompactDisc cd) {
this.cd = new CompactDisc() {
#Override
public void play() {
System.out.println("AAAAA");
}
};
}
}
and in this scenario, Although that we wired those two beans together, but #Autowired makes spring to execute the doSomething()method.
What is happening?! I can't see the Big Picture. I can't understand the pattern that is going on.
sometimes #Autowired works and sometimes doesn't work. what is the general pattern? do we need #Autowired at all when we wire beans together in JavaConfig file?

An autowired constructor is invoked if spring invokes the constructor by reflection, typically because you declare the bean using component scanning or XML config. If you manually invoke a constructor in a #Bean method, that constructor executes, and #Autowired has no effect.
An autowired method is invoked after the bean has been created, irrespective of how the bean was created.
The reason is that, in Java, each constructor call creates a new object, making it impossible to call two constructors for the same object. That's why Spring can't call a second constructor if you have already called a different one. In contrast, it is possible to call many methods on the same object, so Spring does support method autowiring just fine.
To conclude, you can use autowiring with JavaConfig, but you should autowire fields or methods rather than constructors. Or you can do without autowiring, and pass everything explicitly in your #Bean method. Or any mixture of the two.

The Spring Context contains all the beans you need in your program, and Spring do the rest of the job for you. But something to understand is that your beans comes from many parts of your application :
Internal beans (POJO from your domain).
External beans (POJO from other libraries or third partie classes).
Reading this from the spring documentation, you can find all the differents sources of beans :
#SpringBootApplication is a convenience annotation that adds all of
the following:
#Configuration: Tags the class as a source of bean definitions for the
application context.
#EnableAutoConfiguration: Tells Spring Boot to start adding beans
based on classpath settings, other beans, and various property
settings. For example, if spring-webmvc is on the classpath, this
annotation flags the application as a web application and activates
key behaviors, such as setting up a DispatcherServlet.
#ComponentScan: Tells Spring to look for other components,
configurations, and services in the com/example package, letting it
find the controllers.
Follow these rules :
In your domain classes (Controller, Service) : use #Autowired in your constructor. It is the recommanded way to inject your dependencies.
You want to use external classes : implements a Java Configuration with #Configuration annotation, to instanciate your external classes as beans.
You want to create custom utilities classes : decorate it with #Component.
When you have more than on implementation, use #Qualifier and define your beans in a #Configuration class.

Related

Autowire constructor is called twice even using Qualifier in spring

I am trying to learn autowire with Qualifier in spring and the autowired class constructor is called twice .
I have the following class:
MainApp:
public class MainApp {
public static void main(String[] args) {
// ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
ApplicationContext ctx =
new AnnotationConfigApplicationContext(HelloWorldConfig.class);
// TextEditor obj = (TextEditor) ctx.getBean("helloWorld");
TextEditor obj = (TextEditor)ctx.getBean(TextEditor.class);
obj.spellCheck();
}
SpellChecker:
public class SpellChecker {
public SpellChecker() {
System.out.println("Inside SpellChecker constructor.");
}
public void checkSpelling() {
System.out.println("Inside checkSpelling.");
}
TextEditor
public class TextEditor {
#Qualifier("a")
#Autowired
private SpellChecker spellChecker;
public SpellChecker getSpellChecker( ) {
return spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
I have the java based configuration which has multiple bean with same Type and want to select a single bean with Qualifier but the output shows the constructor is called twice.\
HelloWorldConfig
#Component
public class HelloWorldConfig {
#Bean
public HelloWorld helloWorld(){
return new HelloWorld();
}
#Bean
public TextEditor textEditor(){
return new TextEditor();
}
#Bean(name="a")
public SpellChecker spellChecker(){
return new SpellChecker();
}
#Bean(name="b")
public SpellChecker spellChecker1(){
return new SpellChecker();
}
}
OUTPUT:
Inside SpellChecker constructor.
Inside SpellChecker constructor.
Inside checkSpelling.
I was expecting a single SpellChecker constructor call since i used Qualifier("a") to specify the bean, however the constructor is called twice even if I used Qualifier to select a sinlge bean. Why is it called twice??
Check Out HelloWorldConfig file. You've declared 2 beans of type SpellChecker:
#Bean(name="a")
public SpellChecker spellChecker(){
return new SpellChecker();
}
#Bean(name="b")
public SpellChecker spellChecker1(){
return new SpellChecker();
}
Both beans are created by spring, although only one bean is injected into the TextEditor.
In spring its perfectly fine to have more than one bean of the same type or beans that implement the same interface (think about listeners - there can be many of them) - so spring container will create them all during the application start.
If you want to inject one of those beans however into another bean (like TextEditor in your case), spring won't have a clue which one to inject, that's why you they've added a "qualifier" feature.
Another note, you've by mistake put a #Component annotation on the configuration class, instead you should use #Configuration annotation to specify that this class contains a series of beans definitions (that you indeed declare with #Bean annotation). Although the "configuration" is also a component (managed by spring) - spring still treats it in a significantly different way than regular beans.
And yet another note: although its not directly related to your question, it seems like you're mixing 2 styles of configurations in the code snippet you've provided: the style with #Qualifier/#Autowired (this way was added in spring 2.5 if I'm not mistaken) and the style of java configurations: #Configuration class with #Bean-s in it.
You can avoid using autowire altogether and inject the dependencies via constructor called from java config like this:
#Configuration
public class HelloWorldConfig {
#Bean
public HelloWorld helloWorld(){
return new HelloWorld();
}
#Bean
public TextEditor textEditor(#Qualifier("a") SpellChecker spellChecker){ // note the dependency here
return new TextEditor(spellChecker);
}
#Bean(name="a")
public SpellChecker spellChecker(){
return new SpellChecker();
}
#Bean(name="b")
public SpellChecker spellChecker1(){
return new SpellChecker();
}
}
// the TextEditor Class should have a constructor with one argument:
public class TextEditor {
private final SpellChecker spellChecker;
public TextEditor(SpellChecker spellChecker) {
this.spellChecker = spellChecker;
}
...
}
// or if you use lombok library:
#AllArgsConstructor
public class TextEditor {
private final SpellChecker spellChecker;
...
}
Now note, that your "business classes" are not even aware of spring all the "resolution" (usage of qualifier and injection rules) is done in spring configuration which is a special class anyway.

#Bean ResourceProcessor with #Autowired

In my Spring Boot 1.5.10 application with Spring Data REST and HATEOAS, I have a ResourceProcessor bean with an #Autowired service, like:
#Bean
public ResourceProcessor<Resource<Order>> orderResourceProcessor() {
return new ResourceProcessor<Resource<Order>>() {
#Autowired
private OrderHandler orderHandler;
#Override
public Resource<Order> process(Resource<Order> resource) {
Order order = resource.getContent();
Payment payment = orderHandler.payment(order);
resource.add(makeLink(payment));
return resource;
}
private Link makelink(Payment payment) {
return new Link(/*...*/);
}
};
}
When the #Autowired service is added, the resource processor bean is no longer triggered, unfortunately; i.e., when OrderHandler is commented out, the resource processor runs as it should.
Can a ResourceProcessor use #Autowired services; and, if so, what's the right way to construct it?
This part of the #Bean annotation javadoc should interest you :
#Bean Methods in #Configuration Classes
Typically, #Bean methods are declared within #Configuration classes.
In this case, bean methods may reference other #Bean methods in the
same class by calling them directly. This ensures that references
between beans are strongly typed and navigable. Such so-called
'inter-bean references' are guaranteed to respect scoping and AOP
semantics, just like getBean() lookups would.
Example :
#Bean
public FooService fooService() {
return new FooService(fooRepository());
}
#Bean
public FooRepository fooRepository() {
return new JdbcFooRepository(dataSource());
}
It means that you have not to use #Autowired to set the dependency inside the #Bean declaration but reference another method annotated with #Bean.
But do you really need to set the dependency to create your bean ?
No at all. The OrderHandler is used only during the process() invocation.
So you can simply inject OrderHandler at the same level that the method annotated with #Bean and using it in the anonymous class :
#Autowired
private OrderHandler orderHandler; // only change
#Bean
public ResourceProcessor<Resource<Order>> orderResourceProcessor() {
return new ResourceProcessor<Resource<Order>>() {
#Override
public Resource<Order> process(Resource<Order> resource) {
Order order = resource.getContent();
Payment payment = orderHandler.payment(order);
resource.add(makeLink(payment));
return resource;
}
private Link makelink(Payment payment) {
return new Link(/*...*/);
}
};
}
I guess you can Autowire orderHandler to outer class. In your way it will not work as you create the instance of ResourceProcessor yourself.
#Autowired
private OrderHandler orderHandler;
#Bean
public ResourceProcessor<Resource<Order>> orderResourceProcessor() {
return new ResourceProcessor<Resource<Order>>() {
#Override
public Resource<Order> process(Resource<Order> resource) {
Order order = resource.getContent();
Payment payment = orderHandler.payment(order);
resource.add(makeLink(payment));
return resource;
}
private Link makelink(Payment payment) {
return new Link(/*...*/);
}
};
}

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.

How do i register AbstractMongoEventListener programmatically?

In my Spring Boot application, i have a configuration, which reads entries from a Mongo database.
After this is done, my subclass of AbstractMongoEventListener is created, even though it operates on a different table and different scope (my own custom #CustomerScope).
Here is the listener:
#CustomerScoped
#Component
public class ProjectsRepositoryListener extends AbstractMongoEventListener<Project> {
#Override
public void onAfterSave(Project source, DBObject dbo) {
System.out.println("saved");
}
}
And here the configuration:
#Configuration
public class MyConfig {
#Autowired
private CustomersRepository customers;
#PostConstruct
public void initializeCustomers() {
for (Customer customer : customers.findAll()) {
System.out.println(customer.getName());
}
}
}
I find it surprising that the listener is instantiated at all. Especially since it is instantiated well after the call to the customers repository has finished.
Is there a way to prevent this? I was thinking of programmatically registering it per table/scope, without annotation magic.
To prevent auto-instantiation, the listener must not be annotated as #Component. The configuration needs to get ahold of the ApplicationContext, which can be autowired.
Thus, my configuration class looks like this:
#Autowired
private AbstractApplicationContext context;
private void registerListeners() {
ProjectsRepositoryListener firstListener = beanFactory.createBean(ProjectsRepositoryListener.class);
context.addApplicationListener(firstListener);
MySecondListener secondListener = beanFactory.createBean(MySecondListener.class);
context.addApplicationListener(secondListener);
}
Note that this works for any ApplicationListener, not just AbstractMongoEventListener.

Spring Qualifier and property placeholder

Does anyone know if I should be able to use property placeholder as an expression in a Qualifier? I can't seem to get this working.
I am using spring 3.0.4.
#Controller
public class MyController {
#Autowired
#Qualifier("${service.class}")
Service service;
}
#Service
#Qualifier("ServiceA")
ServiceA implements Service {
public void print() {
System.out.println("printing ServiceA.print()");
}
}
#Service
#Qualifier("ServiceB")
ServiceB implements Service {
public void print() {
System.out.println("printing ServiceB.print()");
}
}
XML:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="file:/etc/config.properties"/>
</bean>
config.properties:
config.properties
service.class=serviceB
This works. You can leave off the service names if you just use the default spring bean name. serviceA vs ServiceA, etc.
#Controller
class MyController {
#Autowired(required=false)
#Qualifier("Service")
Service service;
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("app-ctx.xml", MyController.class);
for(String s:context.getBeanDefinitionNames()){
System.out.println(s);
for(String t:context.getAliases(s)){
System.out.println("\t" + t);
}
}
context.getBean(MyController.class).service.print();
}
}
public interface Service {
void print();
}
#Service(value="ServiceA")
public class ServiceA implements example.Service {
public void print() {
System.out.println("printing ServiceA.print()");
}
}
#Service(value="ServiceB")
public class ServiceB implements example.Service {
public void print() {
System.out.println("printing ServiceB.print()");
}
}
XML:
<beans>
<alias name="${service.class}" alias="Service"/>
<context:property-placeholder location="example/app.properties"/>
<context:component-scan base-package="example"/>
<beans>
Props:
service.class=ServiceB
This solution works without XML and with properties file.
Yours classes improved:
MyController.java:
#Controller
public class MyController {
#Autowired
public MyController(#Qualifier("MyServiceAlias") MyService myService) {
myService.print();
}
}
ServiceA.java:
#Service("serviceA")
public class ServiceA implements MyService {
#Override
public void print() {
System.out.println("printing ServiceA.print()");
}
}
ServiceB.java:
#Service("serviceB")
public class ServiceB implements MyService {
#Override
public void print() {
System.out.println("printing ServiceB.print()");
}
}
application.properties (here you can change which class will be loaded):
service.class=serviceA
And important configuration file AppConfig.java:
#Configuration
public class AppConfig {
#Autowired
private ApplicationContext context;
#Bean
public MyService MyServiceAlias(#Value("${service.class}") String qualifier) {
return (MyService) context.getBean(qualifier);
}
}
Additional explanations:
Use #Qualifier only for field which will be autowired. For services, to specify bean name, use #Service.
If you want standard bean name you don't need to use #Service with specyify name. For example, standard bean name for ServiceA is serviceA (not ServiceA - see big first letter), so #Service("serviceA") redundant (#Service is enough).
I based AppConfig on this answer: Spring Bean Alias in JavaConfig.
This solution is better than this Spring Qualifier and property placeholder, because you don't need XML.
Tested on Spring Boot 1.5.7.
I would venture to guess the answer is no, just based on the write ups in a few javadoc pages. For example, see the docs for #Value:
http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/beans/factory/annotation/Value.html
Notice they make special mention of using expressions in the annotation. For comparison, the docs for #Qualifier:
http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/beans/factory/annotation/Qualifier.html
Which make no mention of expressions. Obviously not a definitive answer (but spring is generally very good on documentation). Also, if expressions were supported in the #Qualifier annotation I would expect they work the same way as the #Value annotation (just based on spring being a very consistent framework).
Spring 3.1 has the new profile bean feature, which seems like it can accomplish something like what you're trying to do. Here's a write up for that:
http://blog.springsource.com/2011/02/14/spring-3-1-m1-introducing-profile/
As a workarround, you can set the desired Spring service implementation based on its name in your config.properties.
#Controller
public class MyController {
//add a String which will hold the name of the service to implement
#Value("${service.class}")
private String serviceToImplement;
Service service;
// now autowire spring service bean based on int name using setter
#Autowired
public void setService(ApplicationContext context) {
service = (Service) context.getBean(serviceToImplement);
}
}
#Service
#Qualifier("ServiceA")
ServiceA implements Service {
public void print() {
System.out.println("printing ServiceA.print()");
}
}
#Service
#Qualifier("ServiceB")
ServiceB implements Service {
public void print() {
System.out.println("printing ServiceB.print()");
}
}
config.properties
service.class=serviceB
Maybe give this a whirl:
#Controller
public class MyController {
private String serviceId;
#Value("${serviceId}")
public void setServiceId(String serviceId) {
this.serviceId = serviceId;
}
#Autowired
#Qualifier(serviceId)
Service service;
}

Categories

Resources