How does autowiring work in Spring?
First, and most important - all Spring beans are managed - they "live" inside a container, called "application context".
Second, each application has an entry point to that context. Web applications have a Servlet, JSF uses a el-resolver, etc. Also, there is a place where the application context is bootstrapped and all beans - autowired. In web applications this can be a startup listener.
Autowiring happens by placing an instance of one bean into the desired field in an instance of another bean. Both classes should be beans, i.e. they should be defined to live in the application context.
What is "living" in the application context? This means that the context instantiates the objects, not you. I.e. - you never make new UserServiceImpl()
- the container finds each injection point and sets an instance there.
In your controllers, you just have the following:
@Controller // Defines that this class is a spring bean
@RequestMapping("/users")
public class SomeController {
// Tells the application context to inject an instance of UserService here
@Autowired
private UserService userService;
@RequestMapping("/login")
public void login(@RequestParam("username") String username,
@RequestParam("password") String password) {
// The UserServiceImpl is already injected and you can use it
userService.login(username, password);
}
}
A few notes:
- In your
applicationContext.xml
you should enable the<context:component-scan>
so that classes are scanned for the@Controller
,@Service
, etc. annotations. - The entry point for a Spring-MVC application is the DispatcherServlet, but it is hidden from you, and hence the direct interaction and bootstrapping of the application context happens behind the scene.
UserServiceImpl
should also be defined as bean - either using<bean id=".." class="..">
or using the@Service
annotation. Since it will be the only implementor ofUserService
, it will be injected.- Apart from the
@Autowired
annotation, Spring can use XML-configurable autowiring. In that case all fields that have a name or type that matches with an existing bean automatically get a bean injected. In fact, that was the initial idea of autowiring - to have fields injected with dependencies without any configuration. Other annotations like@Inject
,@Resource
can also be used.
why @Autowired work without @Component when inside @Configuration
In your code, the @Component
annotation is not required because you've created the UserRealm
object as a spring bean in the ShiroConfig
class. Since it's a spring bean, spring will manage the object and perform the dependency injections specified by the @Autowired
annotation.
If you didn't create the UserRealm
object as a spring bean in the ShiroConfig
class, you would then need the @Component
annotation on the UserRealm
class. The @Component
annotation would cause spring to automatically create an instance of the UserRealm
class as a spring bean, assuming component scanning is enabled.
So you either don't use a @Component
annotation and manually create spring beans in your configuration class, or use the @Component
annotation and let spring automatically create the spring bean. The result is the same.
Understanding Spring @Autowired usage
TL;DR
The @Autowired
annotation spares you the need to do the wiring by yourself in the XML file (or any other way) and just finds for you what needs to be injected where and does that for you.
Full explanation
The @Autowired
annotation allows you to skip configurations elsewhere of what to inject and just does it for you. Assuming your package is com.mycompany.movies
you have to put this tag in your XML (application context file):
<context:component-scan base-package="com.mycompany.movies" />
This tag will do an auto-scanning. Assuming each class that has to become a bean is annotated with a correct annotation like @Component
(for simple bean) or @Controller
(for a servlet control) or @Repository
(for DAO
classes) and these classes are somewhere under the package com.mycompany.movies
, Spring will find all of these and create a bean for each one. This is done in 2 scans of the classes - the first time it just searches for classes that need to become a bean and maps the injections it needs to be doing, and on the second scan it injects the beans. Of course, you can define your beans in the more traditional XML file or with an @Configuration
class (or any combination of the three).
The @Autowired
annotation tells Spring where an injection needs to occur. If you put it on a method setMovieFinder
it understands (by the prefix set
+ the @Autowired
annotation) that a bean needs to be injected. In the second scan, Spring searches for a bean of type MovieFinder
, and if it finds such bean, it injects it to this method. If it finds two such beans you will get an Exception
. To avoid the Exception
, you can use the @Qualifier
annotation and tell it which of the two beans to inject in the following manner:
@Qualifier("redBean")
class Red implements Color {
// Class code here
}
@Qualifier("blueBean")
class Blue implements Color {
// Class code here
}
Or if you prefer to declare the beans in your XML, it would look something like this:
<bean id="redBean" class="com.mycompany.movies.Red"/>
<bean id="blueBean" class="com.mycompany.movies.Blue"/>
In the @Autowired
declaration, you need to also add the @Qualifier
to tell which of the two color beans to inject:
@Autowired
@Qualifier("redBean")
public void setColor(Color color) {
this.color = color;
}
If you don't want to use two annotations (the @Autowired
and @Qualifier
) you can use @Resource
to combine these two:
@Resource(name="redBean")
public void setColor(Color color) {
this.color = color;
}
The @Resource
(you can read some extra data about it in the first comment on this answer) spares you the use of two annotations and instead, you only use one.
I'll just add two more comments:
- Good practice would be to use
@Inject
instead of@Autowired
because it is not Spring-specific and is part of theJSR-330
standard. - Another good practice would be to put the
@Inject
/@Autowired
on a constructor instead of a method. If you put it on a constructor, you can validate that the injected beans are not null and fail fast when you try to start the application and avoid aNullPointerException
when you need to actually use the bean.
Update: To complete the picture, I created a new question about the @Configuration
class.
How do constructor calls with @Autowired work?
TL;DR
When creating beans Spring will have to invoke constructors that the target bean class contains :
- If there is no constructor defined - then Spring will invoke the implicit default constructor generated by compiler.
- If there is a no-args constructor defined explicitly then Spring invoke this one since there is no other constructor defined.
- If there is a constructor defined, which requires some dependencies then Spring will have to invoke this one and provide dependencies for it. (Since Spring 4.3 you do not even have to mark this constructor with
@Autowired
). - If there are multiple args constructor defined then you will have to resolve ambiguity, since Spring will not know which one to choose. (Then you can mark one of them with
@Autowired
or use configuration class to define your beans).
Side notes
Spring IOC container (application context) is responsible for holding beans and return them whenever it is asked to do so. To create a context you have to tell Spring where to look for bean definitions : you can provide xml file, java configuration or enable auto-scanning of components in given packages. When Spring context is being created it has to create beans. It will try to invoke constructors and provide any dependencies for beans that require them.
In your example when instance of MyClass
will be created for the context, it will invoke default constructor of MyClass
class and then set it's dependency via reflection.
However field injection is typically a bad idea as you might have problems with testing such components. Constructor or setter injection is a better choice.
If you changed your MyClass
to :
public class MyClass {
private MyService service;
@Autowired
public MyClass(MyService service) {
this.service = service;
}
}
here you provide your own constructor - note that there will be no default constructor generated in this case. So Spring will have to invoke constructor you provided and satisfy dependency for it. If there is no dependency that can be injected - an exception will be thrown.
Notice that you can use your classes even without Spring :
MyService myService = new MyService();
MyClass myclass = new MyClass(myService);
By marking your classes with Spring stereotypes and by using @Autowired
you just enable spring support for context creation and dependency injection (in case of automated package scanning)
How does Spring annotation @Autowired work?
Java allows access controls on a field or method to be turned off (yes, there's a security check to pass first) via the AccessibleObject.setAccessible()
method which is part of the reflection framework (both Field
and Method
inherit from AccessibleObject
). Once the field can be discovered and written to, it's pretty trivial to do the rest of it; merely a Simple Matter Of Programming.
Spring Boot: Why does @Autowired not work for this case?
From the Vaadin website:
The only difference between using the router in a standard application and a Spring application is that, in Spring, you can use dependency injection in components annotated with @Route. These components are instantiated by Spring and become Spring-initialized beans. In particular, this means you can autowire other Spring-managed beans.
One way to access Spring components from your regular Vaadin components is by creating static get methods to retrieve your Spring components. Here is one way that can be done... but not the only.
@Component
public class StaticHelper {
private static StaticHelper instance;
@Autowired
private ApplicationContext applicationContext;
@PostConstruct
public void registerInstance() {
instance = this;
}
public static <T> T getBean(Class<T> class) {
return instance.applicationContext.getBean(class);
}
}
So in your example...
private TaskService taskService = StaticHelper.getBean(TaskService.class);
Spring boot autowiring an interface with multiple implementations
Use @Qualifier
annotation is used to differentiate beans of the same interface
Take look at Spring Boot documentation
Also, to inject all beans of the same interface, just autowire List
of interface
(The same way in Spring / Spring Boot / SpringBootTest)
Example below:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
public interface MyService {
void doWork();
}
@Service
@Qualifier("firstService")
public static class FirstServiceImpl implements MyService {
@Override
public void doWork() {
System.out.println("firstService work");
}
}
@Service
@Qualifier("secondService")
public static class SecondServiceImpl implements MyService {
@Override
public void doWork() {
System.out.println("secondService work");
}
}
@Component
public static class FirstManager {
private final MyService myService;
@Autowired // inject FirstServiceImpl
public FirstManager(@Qualifier("firstService") MyService myService) {
this.myService = myService;
}
@PostConstruct
public void startWork() {
System.out.println("firstManager start work");
myService.doWork();
}
}
@Component
public static class SecondManager {
private final List<MyService> myServices;
@Autowired // inject MyService all implementations
public SecondManager(List<MyService> myServices) {
this.myServices = myServices;
}
@PostConstruct
public void startWork() {
System.out.println("secondManager start work");
myServices.forEach(MyService::doWork);
}
}
}
For the second part of your question, take look at this useful answers first / second
How do I make Autowired work inside a Configuration class?
Spring will only inject the dependencies after or when a bean is instantiated (Depending if constructor injection is used or not). However , you are now accessing the dependency MyService
during the field initialisation which happens before initialising a bean .Hence , it cannot access MyService
during field initialisation as it is not injected yet.
You can simply fix it by changing to use constructor injection and initialise routingKeys
inside a constructor at the same time :
@Configuration
public class RabbitConfiguration {
private List<String> routingKeys ;
private MyService myService;
@Autowired
public RabbitConfiguration(MyService myService){
this.myService = myService
this.routingKeys = writeRoutingKeys();
}
private List<String> writeRoutingKeys() {
return myService.getRoutingKeys();
}
}
Or simply :
@Autowired
public RabbitConfiguration(MyService myService){
this.myService = myService
this.routingKeys = myService.getRoutingKeys();
}
Related Topics
How to Subtract X Days from a Date Using Java Calendar
Nosuchelementexception with Java.Util.Scanner
Jpanel in Puzzle Game Not Updating
Java Integer Compareto() - Why Use Comparison VS. Subtraction
What Is the Exact Meaning of Static Fields in Java
How to Convert Java String into Byte[]
Why Is Hibernate Open Session in View Considered a Bad Practice
Does Java Have Support for Multiline Strings
Executorservice, How to Wait for All Tasks to Finish
Why Should a Java Class Implement Comparable
Using Regex to Generate Strings Rather Than Match Them
How to Use a Custom Serializer with Jackson