Good day, guys. I have a question about autowiring services into my classes when using Springboot. All of the examples I have seen on the Internet as well as in the Springboot specification do something of the like (taking an excerpt from the Springboot version 1.5.7 specification):
package com.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class DatabaseAccountService implements AccountService {
private final RiskAssessor riskAssessor;
#Autowired
public DatabaseAccountService(RiskAssessor riskAssessor) {
this.riskAssessor = riskAssessor;
}
// ...
}
This is a class that injects a property through its constructor, by means of #Autowiring the constructor. Another form is to #Autowire the property like this:
#Autowired
private final RiskAssessor riskAssessor
But, where I work, for these two methods to work, I have been told that I need to use this method:
applicationContext.getAutowireCapableBeanFactory().autowireBean(Object.class)
They have told me that I need this in order for the #Autowired annotation to work.
Now my question to you is: why is there no simple annotation that allows the #Autowire to function correctly? (Something like #AutowiredClass). The above method is too verbose and hard to remember, so surely there must be a better way to make #Autowired work on classes in order to inject services, just like we do in Grails where we just say def someService and it is automatically injected.
If you want properly use #Autowired in your spring-boot application, you must do next steps:
Add #SpringBootApplicationto your main class
Add #Service or #Component annotation to class you want inject
Use one of two ways that you describe in question, to autowire
If you don't have any wiered package structure and the main class package includes all other classes you want spring to instantiate (directly or in the subpackages) a simple annotation #ComponentScan on your main class will help you save all those boiler plate code. Then spring will do the magic, it will go and scan the package(and subpackages) and look for classes annotated with #Service, #Component etc and instantiate it.
Even better, use #SpringBootApplication in your main class, this will cover #Configuration as well. If it is a green field project , I would encourage to start from start.spring.io - a template generation/scaffolding tool for spring
Now my question to you is: why is there no simple annotation that allows the #Autowire to function correctly?
There is: #SpringBootApplication
If you put this at the root of your application (file that contains the main class) and as long as your services are at the same package or a sub-package, Spring will auto-discover, instantiate, and inject the proper classes.
There's an example in this walk-through: REST Service with Spring Boot
As described in that page:
#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.
#ComponentScan tells Spring to look for other components, configurations, and services in the hello package, allowing it to find the controllers.
You need to annotate the implementation of RestService as a #Service or #Component so Spring would pick it up.
#Service
public class MyRiskAssessorImpl implements RiskAssessor {
///
}
#Autowired almost works out of the box. Just do your component scanning of the class you want to autowire and you are done. Just make sure your main class (or main configuration class) uses #ComponentScan("{com.example.app}") or #SpringBootApplication (main class). The docs explain this stuff pretty good
Related
I'm trying to do a getmapping method using rest-api which will return as a JSON on localhost: 8080. When I do this from the default Application class it works great but when I move this functionality to another class nothing happens. Could someone help me with this, please?
The issue is that your package structure prevents BookController from being component scanned.
When you have your #SpringBootApplication defined in package com.xenon.myspringbootapp, anything in that package and in nested packages is eligible for component scanning, which will register a bean out of #(Rest)Controller and other stereotypes.
Because BookController is defined in book, it is outside of the component scanned packages and will therefore not be component scanned by your #SpringBootApplication annotation.
See the reference docs for more best practices on package structure with Spring Boot applications.
To resolve this, there are two choices to get your class component scanned.
The first (and the way I would recommend) is just to restructure your packages so that the #SpringBootApplication class is at the "base" of your application packages. For example, you could move book.BookController to be at com.xenon.myspringbootapp.book.BookController.
The second is to change the component scanning configuration to include your book package. You can do this either on the #SpringBootApplication annotation itself:
#SpringBootApplication(scanBasePackages = {
"com.xenon.myspringbootapp",
"book"
})
Or, define a different #ComponentScan. Note that the configuration class annotated with #ComponentScan must still be component scanned itself.
#SpringBootApplication
public class MySpringBootAppApplication {
#Configuration
#ComponentScan("book")
public static class MyBookComponentScanConfiguration {}
public static void main(String[] args) {
SpringApplication.run(MySpringBootAppApplication.class);
}
}
Reason 1: Default Spring's component scanning can't fount your #RestController component, so you can try to do #ComponentScan directly.
Reason 2: You need to specify an endpoint for your #GetMapping by using #RequestMapping/#GetMapping("url") or both of them.
I have a jar file that contains a java class HelloService with #Service annotation. I would like to Autowire it into my a #Component class GoodByeComponent that I am writing (autowire into the constructor).
So, the skeleton for HelloService could look something like this:
#Service
public class HelloService
{
...
}
And the GoodByeComponent would look like:
import from.some.jar.HelloService
#Component
public class GoodByeComponent
{
private final HelloService helloService;
#Autowired
public GoodByeComponent(HelloService helloService)
{
this.helloService = helloService;
}
}
Understandably, I get an error that says Could not autowire. No beans of 'HelloService' type found.
So. I have some idea that I might need to create a bean somewhere that returns HelloService? How would I even instantiate the service...? It also needs to autowire other things. Is this possible, or is it too much of a headache and I should probably just copy it into my jar?
Your question does not have enough information, but most likely, your application have a #ComponentScan annotation somewhere in your program. That annotation is responsible for finding your #Component, #Service and initializing them.
By default, #ComponentScan only scan for the its own package. So let say it you have a package structure similar to this:
your.own.package ---- ConfigurationClass
|
--- GoodByeComponent
Then spring naturally, will only discover the GoodByeComponent and cannot find the HelloService.
You have to supply additional location for it like:
#ComponentScan({ "your.own.package", "from.some.jar" })
That would allow spring to discover the beans inside your own application, as well as the external dependency you rely on.
See: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/ComponentScan.html
In a real project, I found out that #Component may be omitted in the following code:
// no #Component !!!!!
public class MovieRecommender {
private final CustomerPreference customerPreference;
#Autowired
public MovieRecommender(CustomerPreference customerPreference) {
this.customerPreference = customerPreference;
}
// ...
}
#Component
public class CustomerPreference {...}
(The example is taken from the official Spring docs https://docs.spring.io/spring-framework/docs/4.3.x/spring-framework-reference/htmlsingle/#beans-autowired-annotation , and the docs show no #Component at all, which may mean either that it is not needed, or that it is just not shown.)
The project where I work does not use any XML bean declarations, but it uses frameworks other than just Spring, so it is possible that something declares the class as a bean. Or it may be a feature of the version of Spring that we use, and if that feature is not documented, it may be dropped later.
Question:
Must the class that uses #Autowired be annotated with #Component (well, be a bean)? Is there any official documentation about that?
UPD Folks, there is no #Configuration and no XML configs in the project, I know that such declarations make a bean from a class, but the question is not about them. I even wrote "(well, be a bean)" in the question above to cover that. Does #Autowired work in a class that is not a bean? Or maybe it declares the class that uses it as a bean?
there are several ways to instantiate a bean in Spring.
One of them indeed is with the #Component annotations, with that, Spring will scan all the packages defined for component-scan and initialize all annotated classes (either with #Component or one of the annotations that uses it - Controller, Service, etc.).
Other way to initialise beans is using a configuration class (annotated with #Configuration) that includes methods annotated with #Bean. each of these methods will create a bean.
There's also an option to create the beans using xml configurations, but this is becoming less and less common, as the annotation-based approach is more convinient
According to https://stackoverflow.com/a/3813725/755804 , with autowireBean() it is possible to autowire a bean from a class not declared as a bean.
#Autowired
private AutowireCapableBeanFactory beanFactory;
public void sayHello(){
System.out.println("Hello World");
Bar bar = new Bar();
beanFactory.autowireBean(bar);
bar.sayHello();
}
and
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
public class Bar {
#Autowired
private Foo foo;
public void sayHello(){
System.out.println("Bar: Hello World! foo="+foo);
}
}
On the other hand, by default the latest Spring does not assume that classes that use #Autowire are #Component-s.
UPD
As to the mentioned real project, the stack trace shows that the constructor is called from createBean(). That is, the framework creates beans from classes declared in the framework's configs.
I and my friend were discussed about #ComponentScan and #Import. Which one is better?
We have 2 different ideas.
#ComponentScan: Easy to use, import all beans from the component
scan.
#Import: You need to know what component you want to use, no need to scan all.
How about your idea? Which one is better for you to use?
Thanks!
#Import is used to import Java configuration classes marked with #Configuration/#Component typically. So if you have a bean inside this component, Spring will load it into Application Context. You can just put the name of the component or class and Spring will pull it up for you.
However, by using #ComponentScan, you tell the application which packages to scan for java classes are annotated with #Configuration/#Component (or any of #Component's sub-annotations like #Service or #Repository etc) and load all of them up in Application Context so they can be autowired when required. If there are inner instances that need to be populated, Spring will take care of it.
You can read more about #Import and #ComponentScan on their respective doc pages.
This page explains pretty well the difference.
#ComponentScan scans and searches for any beans inside packages/classes specified under basePackageClasses or basePackages options, whichever is configured.
This option also allows you to filter some classes that you do not want to be included in search.
#Import is like clubbing one java configuration into another.
eg:
#Configuration
#ComponentScan(basePackages="com.stackoverflow")
public class Dbconfig {
#Bean
public Datasource dSource(){
return new Datasource()
}
}
#Configuration
#Import(Dbconfig.class)
#ComponentScan(basePackages="org.hellospring")
public class AppConfig {
...// beans
}
So here, if we check AppConfig class,
it will include all beans registered in Dbconfig configuration class including inside of package com.stackoverflow
+
It will include all beans inside AppConfig class and beans under package org.hellospring
I have a package named com.example.service, and in my Spring Configuration class I have the annotation #ComponentScan({"com.example.service"},{"com.example.controller"}).
When I try to #Autowire a service, code compilation fails with a NoSuchBeanDefinitionException. The MyService interface is annotated with #Service.
Currently I use a quite ugly workaround and declare every single service bean in my ExampleConfig.java like
#Bean
public MyService myService() {
return new MyServiceImpl();
}
Generally the #ComponentScan seems to work, if I remove the controller package, the controllers are not found. What did I understand wrong? Please let me know, if I missed out any relevant information.
The MyService interface is annotated with #Service
You must annotate the implementation of your interface. Not the interface itself.
Try using below code for ComponentScan annotation for scanning multiple packages:
#ComponentScan({"com.example.service","com.example.controller"})
instead of
#ComponentScan({"com.example.service"},{"com.example.controller"})
#ComponentScan uses string array for scanning multiple base packages.