preface

Recently, when reviewing other people’s code, I saw some different uses of @autowired and found them interesting. I spent some time studying them and gained a lot. Now I share with you.

Maybe @autowired is more powerful than you think.

1. Default assembly for @autowired

We all know that the @AutoWired annotation in Spring is used to automatically assemble objects. Usually, we use this in 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() {
    }
}
Copy the code

Yes, this will work because by default spring assembles byType, which is what we call byType.

Additionally, the @AutoWired annotation’s required parameter defaults to true, indicating that autoroing is enabled. In some cases we don’t want to use autoroing, so we can set this parameter to false.

2. If there is more than one object of the same type

The preceding byType method applies to only one object of the same type. In this case, the object type is unique and the correct object can be found.

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

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

package com.sue.cache.service.test;

import org.springframework.stereotype.Service;

@Service
public class TestService1 {

    public void test1() {
    }
}
Copy the code

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]
Copy the code

The result is wrong. The name of the reported class conflicts, which directly leads to the failure of the project.

Note that this situation is not caused by having two objects of the same type in Autowired, which is very confusing. This is because Spring’s @service method does not allow identical class names because spring converts the first letter of the class name to lowercase as the bean name, such as testService1, and by default the bean name must be unique.

Let’s see how we can 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(); }}Copy the code

Create a TestService1 instance manually in the TestConfig class and remove the @Service annotation from the TestService1 class.

Restart the project:

TestService1 is a singleton, but two objects are found.

There is also a case where two beans of the same type are produced:

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; }Copy the code

When the project is restarted:

TestService1 is a singleton, but found two objects.

The second scenario is more common in real projects, and we’ll focus on the second scenario in the following examples.

3. @ the Qualifier and @ Primary

Obviously in Spring, Autowired’s default assembly method: byType cannot solve the above problem, so you can use byName assembly instead.

Just add the @qualifier annotation to the code:

@Service
public class UserService {

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

After adjustment, the project can start normally.

A Qualifier is a Qualifier. Used in conjunction with an Autowired, you need to specify the name of a bean from which you can find the bean to be assembled.

In addition to the @qualifier annotation above, you can use the @primary annotation to solve the above problems. Add the @primary annotation to User1:

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

Remove @qualifier annotation from UserService:

@Service
public class UserService {

    @Autowired
    private IUser user;
}
Copy the code

Restart the project and it will work just as well.

When we assemble a Bean using auto-configuration, if the Bean has multiple candidates, if one of the candidates has the @primary annotation, that candidate will be selected as the auto-configuration value.

4. Scope of use of @autowired

In the example above, the @AutoWired annotation is used on member variables, but the power of @Autowired is far from that.

Let’s start with the @autowired annotation definition:

As you can see from the figure, this annotation can be used on five target types, summarized in the following figure:

4.1 Member Variables

Use Autowired annotations on member variables:

@Service
public class UserService {

    @Autowired
    private IUser user;
}
Copy the code

This is probably the most commonly used method.

4.2 the constructor

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); }}Copy the code

Note that by adding an Autowired annotation to the constructor, you are actually using the Autowired assembly, not the constructor assembly.

4.3 methods

Add Autowired annotations to normal methods:

@Service public class UserService { @Autowired public void test(IUser user) { user.say(); }}Copy the code

Spring automatically calls the @AutoWired annotated method during project startup, where we can do some initialization.

You can also Autowired annotations on setter methods:

@Service public class UserService { private IUser user; @Autowired public void setUser(IUser user) { this.user = user; }}Copy the code

4.4 parameter

We can add an Autowired annotation to the constructor’s input parameter:

@Service public class UserService { private IUser user; public UserService(@Autowired IUser user) { this.user = user; System.out.println("user:" + user); }}Copy the code

You can also add an Autowired annotation to the input parameter of a non-static method:

@Service public class UserService { public void test(@Autowired IUser user) { user.say(); }}Copy the code

4.5 annotations

This is actually not used very much, so I won’t tell you more about it.

5. High-end gameplay from @autowired

In fact, all of the examples above are using @AutoWired to auto-assemble a single instance, but I’ll show you that it can also auto-assemble multiple instances. What’s going on here?

Adjust the UserService method to receive arguments of type IUser with a List collection:

@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); }}Copy the code

Add a controller:

@RequestMapping("/u") @RestController public class UController { @Autowired private UserService userService; @RequestMapping("/test") public String test() { userService.test(); return "success"; }}Copy the code

After calling this interface:

As you can see from the figure above, userList, userSet, and userMap all print out two elements, indicating that @AutoWired will automatically collect IUser objects of the same type into the collection.

Are you surprised? Are you surprised?

6. @autowired can be assembled successfully?

In some cases, the @Autowired assembly object is still null, even though the @Autowired assembly object is null.

6.1 No @service annotation

Spring can’t auto-assemble a class without adding @Controller, @service, @Component, @repository, etc.

public class UserService { @Autowired private IUser user; public void test() { user.say(); }}Copy the code

This should be the most common mistake, just because you are handsome doesn’t mean you won’t make this stupid mistake.

6.2 Injecting a Filter or Listener

Start web applications in the following sequence: Listener ->filter-> Servlet.

Take a 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; }}Copy the code

Program startup error:

Tomcat cannot start properly.

What’s the reason?

As we all know, SpringMVC is started in DisptachServlet, which is executed after listener and filter. If we want to @autowired a bean in the listener and filter, we cannot do this because the bean is not initialized when the filter is initialized.

If this is really necessary at work, how can we solve the 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() {

    }
}
Copy the code

The answer is to use WebApplicationContextUtils. GetWebApplicationContext for current ApplicationContext, then access to the bean instance through it.

6.3 Annotations are not scanned by @ComponentScan

Normally, @Controller, @Service, @Component, @Repository, and @Configuration annotations need to be scanned by @ComponentScan to collect metadata.

However, if the @ComponentScan annotation is not added, or the path of the @ComponentScan annotation is not correct, or the path range is too small, some annotations can not be collected, and the function of automatic assembly can not be completed by using @AutoWired.

The good news is that in springBoot projects, if you use the @SpringBootApplication annotation, it has the @ComponentScan annotation built in.

6.4 Cyclic dependencies

If A depends on B, and B depends on C, and C depends on A, an endless loop is formed.

Spring’s beans are singletons by default, and if singletons use the @AutoWired assembly, for the most part, you can solve the loop dependency problem.

However, if the bean is multi-instance, the problem of loop dependencies can occur, causing the bean to fail to auto-assemble.

There are also cases where circular dependencies can occur if a proxy object is created, even if the bean is singleton.

If you are interested in circular dependencies, you can also take a look at my other feature, Spring: How DO I Solve Circular dependencies? “, which is very detailed.

7. The difference between @autowired and @resouce

The @autowired feature, while very powerful, has some drawbacks. For example, it is strongly coupled to Spring, and if you switch to another framework such as JFinal, the functionality will not work. The @Resource is provided by JSR-250, which is a Java standard supported by most frameworks.

In addition, there are scenarios where @Autowired doesn’t meet the requirements, but changing to @Resource does. Next, let’s focus on the difference between @autowired and @Resource.

  • @AutoWired defaults to byType auto-assembly, while @Resource defaults to byName auto-assembly.

  • @autoWired contains only one parameter: required, indicating whether automatic admission is enabled, and the default is true. The @resource contains seven parameters, the most important of which are name and type.

  • @autoWired Needs to use @qualifier in conjunction with byName. @resource is automatically assembled with byName if name is specified, and byType if type is specified.

  • @AutoWired can be used for: constructors, methods, parameters, member variables and annotations, while @Resource can be used for: classes, member variables and methods.

  • @AutoWired is a Spring-defined annotation, while @Resource is a JSR-250 defined annotation.

In addition, they are assembled in a different order.

The assembly order of @autowired is as follows:

The assembly sequence of @Resource is as follows:

  1. If both name and type are specified:

  2. If name is specified:

  3. If type is specified:

  4. If neither name nor type is specified:

Afterword.

I was going to write @Autowired theory analysis and source code interpretation next, but it’s too long to put together, so I’m going to write a special topic later. If interested friends, you can continue to pay attention to my follow-up article, I believe you will read some harvest.

One last word (attention, don’t fuck me for nothing)

If this article is of any help or inspiration to you, please scan the QR code and pay attention to it. Your support is the biggest motivation for me to keep writing.

Ask for a key three even: like, forward, look.

Pay attention to the public account: [Su SAN said technology], in the public account reply: interview, code artifact, development manual, time management have excellent fan welfare, in addition reply: add group, can communicate with a lot of BAT big factory seniors and learn.