The @Autowired annotation is not recommended by Spring or IDEA, why are there so many people using it?

  1. Default assembly for @Autowired

We all know that the @Autowired annotation in spring is used to automatically assemble objects. Usually, we use it like this in our projects:

package com.sue.cache.service;

import org.springframework.stereotype.Service;

@Service
public class TestService1 {
    public void test1() {
    }
}
package com.sue.cache.service;

import org.springframework.stereotype.Service;

@Service
public class TestService2 {

    @Autowired
    private TestService1 testService1;

    public void test2() {
    }
}

That’s right, this can be assembled successfully, because by default spring is assembled according to type, which is what we call byType.

In addition, the required parameter of the @Autowired annotation is true by default, which means that automatic assembly is enabled. Sometimes we do not want to use the automatic assembly function, so we can set this parameter to false.

  1. When there is more than one object of the same type, the above byType method is mainly for the case where there is only one object of the same type. At this time, the object type is unique, and the correct object can be found.

But what happens if there is more than one object of the same type?

In the test directory of the project, a class TestService1 with the same name is built:

package com.sue.cache.service.test;

import org.springframework.stereotype.Service;

@Service
public class TestService1 {

    public void test1() {
    }
}

When restarting the project:

Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'testService1' for bean class [com.sue.cache.service.test.TestService1] conflicts with existing, non-compatible bean definition of same name and class [com.sue.cache.service.TestService1]

As a result, an error was reported, and there was a conflict in the name of the reported class, which directly caused the project to fail to start.

Note that this situation is not caused by two objects of the same type being Autowired, which is very easy to cause confusion. This is because spring’s @Service method does not allow the same class name, because spring will convert the first letter of the class name to lowercase as the bean name, such as: testService1, and by default the bean name must be only.

Let’s see how to generate two beans of the same type:

public class TestService1 {

    public void test1() {
    }
}
@Service
public class TestService2 {

    @Autowired
    private TestService1 testService1;

    public void test2() {
    }
}
@Configuration
public class TestConfig {

    @Bean("test1")
    public TestService1 test1() {
        return new TestService1();
    }

    @Bean("test2")
    public TestService1 test2() {
        return new TestService1();
    }
}

Manually create a TestService1 instance in the TestConfig class, and remove the original @Service annotation on the TestService1 class.

Restart the project:
![Insert picture description here](https://img-blog.csdnimg.cn/c586e48c4647445b8fc3c374fc0dc5b3.png#pic_center

Sure enough, an error was reported, indicating that testService1 is a singleton, but two objects were found.

In fact, there is another situation that produces two beans of the same type:

public interface IUser {
    void say();
}
@Service
public class User1 implements IUser{
    @Override
    public void say() {
    }
}
@Service
public class User2 implements IUser{
    @Override
    public void say() {
    }
}
@Service
public class UserService {

    @Autowired
    private IUser user;
}

When the project restarts:

An error is reported, and the prompt is the same as above, testService1 is a singleton, but two objects are found.

The second situation occurs more often in actual projects. In the following examples, we mainly focus on the second situation.

  1. @Qualifier and @Primary
    Obviously, in spring, according to Autowired’s default assembly method: byType, the above problem cannot be solved. At this time, you can switch to assembly by name: byName.

Just add the @Qualifier annotation to the code:

@Service
public class UserService {

    @Autowired
    @Qualifier("user1")
    private IUser user;
}

After this adjustment, the project can start normally.

Qualifier means a qualified person. It is generally used in conjunction with Autowired. You need to specify a bean name, and you can find the bean that needs to be assembled through the bean name.

In addition to the @Qualifier annotation above, the @Primary annotation can also be used to solve the above problems. Add the @Primary annotation on User1:

@Primary
@Service
public class User1 implements IUser{
    @Override
    public void say() {
    }
}

Remove the @Qualifier annotation on UserService:

@Service
public class UserService {

    @Autowired
    private IUser user;
}

Restart the project and it will work normally.

When we use the automatic configuration method to assemble a bean, if the bean has multiple candidates, if one of the candidates has @Primary annotation modification, the candidate will be selected as the value of the automatic configuration.

  1. Scope of use of @Autowired
    The @Autowired annotation in the above example is used on member variables, but the power of @Autowired is far from that.

First look at the definition of the @Autowired annotation:

It can be seen from the figure that this annotation can be used on 5 target types. Let’s summarize it with a picture:

The annotation of the picture may be on member variables where we usually use it the most.

Next, let’s focus on how to use it in other places?

4.1 Member variables
Use Autowired annotations on member variables:

@Service
public class UserService {

    @Autowired
    private IUser user;
}

This method is probably the most commonly used.

4.2 Constructors
Use the Autowired annotation on the constructor:

@Service
public class UserService {

    private IUser user;

    @Autowired
    public UserService(IUser user) {
        this. user = user;
        System.out.println("user:" + user);
    }
}

Note that adding the Autowired annotation to the constructor actually uses the Autowired assembly method, not the constructor assembly.

4.3 Method
Add Autowired annotations to common methods:

@Service
public class UserService {

    @Autowired
    public void test(IUser user) {
       user.say();
    }
}

Spring will automatically call the method annotated with @Autowired once during the project startup process, and we can do some initialization work in this method.

You can also annotate Autowired on the setter method:

@Service
public class UserService {

    private IUser user;

    @Autowired
    public void setUser(IUser user) {
        this. user = user;
    }
}

4.4 Parameters
Autowired annotations can be added to the input parameters of the constructor:

@Service
public class UserService {

    private IUser user;

    public UserService(@Autowired IUser user) {
        this. user = user;
        System.out.println("user:" + user);
    }
}

You can also add Autowired annotations to the input parameters of non-static methods:

@Service
public class UserService {

    public void test(@Autowired IUser user) {
       user.say();
    }
}

4.5 Notes
This method is actually not very practical, so I won’t introduce it too much.

  1. High end play with @Autowired
    In fact, the above examples are all automatically assembled with a single instance through @Autowired, but here I will tell you that it can also automatically assemble multiple instances, what’s going on?

Adjust the UserService method to use a List collection to receive parameters of type IUser:

@Service
public class UserService {

    @Autowired
    private List<IUser> userList;

    @Autowired
    private Set<IUser> userSet;

    @Autowired
    private Map<String, IUser> userMap;

    public void test() {
        System.out.println("userList:" + userList);
        System.out.println("userSet:" + userSet);
        System.out.println("userMap:" + userMap);
    }
}

Add a controller:

@RequestMapping("/u")
@RestController
public class UController {

    @Autowired
    private UserService userService;

    @RequestMapping("/test")
    public String test() {
        userService. test();
        return "success";
    }
}

After calling this interface:

It can be seen from the above figure: userList, userSet and userMap all print out two elements, indicating that @Autowired will automatically collect the same type of IUser objects into the collection.

Is it a surprise, is it a surprise?

  1. @Autowired must be able to assemble successfully?
    I introduced so many awesome features of the @Autowired annotation. In fact, in some cases, even if the object assembled with @Autowired is still null, what is the reason?

6.1 No @Service annotation added
If you forget to add @Controller, @Service, @Component, @Repository and other annotations on the class, spring will not be able to complete the automatic assembly function, for example:

public class UserService {

    @Autowired
    private IUser user;

    public void test() {
        user.say();
    }
}

This situation should be the most common mistake, and you won’t make such low-level mistakes just because you are handsome.

6.2 Inject Filter or Listener
The order in which web applications are started is: listener->filter->servlet.

Next look at this case:

public class UserFilter implements Filter {

    @Autowired
    private IUser user;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        user.say();
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

    }

    @Override
    public void destroy() {
    }
}
@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new UserFilter());
        bean.addUrlPatterns("/*");
        return bean;
    }
}

When the program starts, an error will be reported:

tomcat cannot start normally.

What is the reason?

As we all know, the startup of springmvc is done in DisptachServlet, and it is executed after listener and filter. If we want to @Autowire a certain bean in the listener and filter, it will definitely not work, because when the filter is initialized, the bean has not been initialized at this time and cannot be automatically assembled.

If we really need to do this at work, how can we solve this problem?

public class UserFilter implements Filter {

    private IUser user;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());
        this.user = ((IUser)(applicationContext.getBean("user1")));
        user.say();
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

    }

    @Override
    public void destroy() {

    }
}

The answer is to use WebApplicationContextUtils.getWebApplicationContext to get the current ApplicationContext, and then get the bean instance through it.

6.3 Annotations are not scanned by @ComponentScan
Usually, @Controller, @Service, @Component, @Repository, @Configuration and other annotations need to be scanned through @ComponentScan annotations to collect metadata.

However, if the @ComponentScan annotation is not added, or the path scanned by the @ComponentScan annotation is incorrect, or the path range is too small, some annotations cannot be collected, and @Autowired cannot be used to complete the automatic assembly function later.

The good news is that in the springboot project, if the @SpringBootApplication annotation is used, it has the built-in @ComponentScan annotation function.

6.4 Circular dependency problem
If A depends on B, B depends on C, and C depends on A, this forms an endless loop.

The bean of the image spring is a singleton by default. If the singleton bean is automatically assembled using @Autowired, in most cases, the circular dependency problem can be solved.

However, if the bean has multiple instances, there will be a circular dependency problem, which will cause the bean to fail to be automatically assembled.

In some cases, if a proxy object is created, even if the bean is a singleton, there will still be a circular dependency problem.

  1. Difference between @Autowired and @Resouce
    Although the @Autowired function is very powerful, it also has some shortcomings. For example: For example, it is strongly coupled with spring. If it is replaced with other frameworks such as JFinal, the function will become invalid. And @Resource is provided by JSR-250, which is a Java standard and supported by most frameworks.

In addition, some scenarios using @Autowired cannot meet the requirements, but changing to @Resource can solve the problem. Next, let’s focus on the difference between @Autowired and @Resource.

@Autowired defaults to autowiring byType, while @Resource defaults to autowiring byName.
@Autowired contains only one parameter: required, indicating whether to enable automatic admission, the default is true. And @Resource contains seven parameters, the two most important parameters are: name and type.
@Autowired If you want to use byName, you need to use @Qualifier together. And @Resource if name is specified, it will be automatically assembled with byName, and if type is specified, it will be automatically assembled with byType.
@Autowired can be used on: constructors, methods, parameters, member variables and annotations, while @Resource can be used on: classes, member variables and methods.
@Autowired is an annotation defined by spring, while @Resource is an annotation defined by JSR-250.
Also, they are assembled in a different order.

The assembly sequence for @Autowired is as follows:

The assembly order of @Resource is as follows:

If both name and type are specified:

If name is specified:

If type is specified:

If neither name nor type is specified: