How Does Autowiring Work in Spring

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 of UserService, 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:

  1. Good practice would be to use @Inject instead of @Autowired because it is not Spring-specific and is part of the JSR-330 standard.
  2. 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 a NullPointerException 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 :

  1. If there is no constructor defined - then Spring will invoke the implicit default constructor generated by compiler.
  2. If there is a no-args constructor defined explicitly then Spring invoke this one since there is no other constructor defined.
  3. 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).
  4. 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



Leave a reply



Submit