1. Analysis from the usage level

First, we create an interface UserService and two implementation classes UserServiceImpl1 and UserServiceImpl2.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public interface UserService {
    void save();
}

@Service
public class UserServiceImpl1 implements UserService {
    @Override
    public void save() {
        System.out.println("UserServiceImpl1 save");
    }
}

@Service
public class UserServiceImpl2 implements UserService {
    @Override
    public void save() {
        System.out.println("UserServiceImpl2 save");
    }
}

We can then perform dependency injection where we need to use UserService.

Use the @Resource annotation.

1
2
3
4
5
6
7
@Component
public class UserController {
    @Resource
    private UserService userService;

    // ...
}

Use the @Autowired annotation.

1
2
3
4
5
6
7
8
@Component
public class UserController {
    @Autowired(required = false)
    @Qualifier("userServiceImpl1")
    private UserService userService;

    // ...
}

In the above code, the @Resource annotation injects the UserService object directly into the userService property, while the @Autowired annotation needs to be combined with the @Qualifier annotation to specify the specific bean name and the required attribute to set whether or not it must be injected into the object. Note that the @Qualifier annotation can also be omitted from the @Autowired annotation, which will match the corresponding bean by type.

2. Analysis from the conceptual level

The implementation principle of dependency injection in Spring Framework is based on the Java reflection mechanism and JavaBean specification.

  1. Instantiate objects through the reflection mechanism

    The Spring framework instantiates the bean object to be injected through the reflection mechanism and stores it in a Map, where Key is the bean name and Value is the bean instance object.

  2. bean property injection via the JavaBean specification

    Next, Spring will find the property to be injected based on the JavaBean specification (i.e. the property name and the setter method of the property) and call the corresponding setter method to inject the property through the reflection mechanism.

  3. Search strategies for dependency injection

    During dependency injection, the Spring Framework typically uses two search strategies:

    • Search by type: When the type of the property to be injected matches the type of multiple beans in the container, a different search strategy is chosen depending on the annotation. For example, when using the @Autowired annotation, the default is to find the corresponding bean by type matching.

    • Search by name: When the type of the property to be injected is not unique, or the name of the bean to be injected does not match the name of the property, you can use the @Qualifier annotation to specify the name of the bean to be injected.

  4. using various annotations for dependency injection

    The Spring framework provides a variety of annotations for dependency injection, including:

    • @Autowired: injection by type;
    • @Resource: injection by name;
    • @Value: injection of properties of simple types or string types;
    • @Inject: an annotation defined by the JSR-330 standard, similar in function to @Autowired.

The Spring Framework’s implementation of dependency injection is based on the Java reflection mechanism and the JavaBean specification, and is implemented in different ways through various annotations.

Both @Resource and @Autowired are Spring dependency injection annotations, but they differ as follows:

  • Different sources: @Resource is an annotation defined by the Java EE specification, whereas @Autowired is an annotation provided by Spring.

  • Attributes are different: The @Resource annotation does not have a required attribute, but a name attribute that indicates the name of the bean to be injected, while the @Autowired annotation has a required and a name attribute, where required indicates whether the object must be injected and defaults to true, and name indicates the name of the bean to be injected.

  • Different lookup method: @Resource annotation by default is based on byName, if not found then byType, while @Autowired by default is based on byType.

  • Compatibility differences: The @Resource annotation can be used with the @Inject annotation in JSR-330; the @Autowired annotation can only be used with Spring components.

  • Different application scenarios: The @Resource annotation is mainly used in Java EE environments, while the @Autowired annotation is one of the most widely used dependency injection annotations in the Spring Framework and can be applied to different application scenarios.

The advantages and disadvantages of the @Resource and @Autowired annotations are described separately below:

Advantages of @Resource:

  1. simple to use and very easy to use in classes.
  2. Can be used in conjunction with the @Inject annotation.
  3. Supports specifying bean names for more precise dependency injection.

@Resource disadvantages:

  1. the Spring framework for @Resource annotation support is not as rich as @Autowired.
  2. only support byName and byType two injection methods, more limited than @Autowired support.

@Autowired Advantages:

  1. support for complex dependency injection configuration , you can dependency injection in a variety of ways.
  2. the Spring framework for @Autowired annotation support is very rich, is one of the most widely used annotations in Spring.
  3. you can specify whether the property must be injected, or you can specify the bean name for injection.
  4. In addition to using it in classes, you can also use the @Autowired annotation in constructors, Setter methods and any method annotated with @Bean.

Disadvantages of @Autowired:

  1. more cumbersome to configure, need to specify required and name attributes etc.
  2. need to follow Spring’s automatic scanning mechanism, only classes marked with annotations such as @Component, @Service, @Controller and @Repository will be managed by the Spring container.

The principle of dependency injection can be summarised in three simple steps:

  1. At application startup, the Spring container is responsible for creating and managing all bean instance objects.
  2. when a bean needs to be used, the Spring container injects the bean into the class or object that needs to be used through the reflection mechanism.
  3. During the bean injection process, the Spring container will use different dependency injection methods depending on the annotation (e.g. @Resource, @Autowired) to complete the dependency injection process.

Both @Resource and @Autowired are dependency injection annotations provided by Spring, each with its own advantages and disadvantages. In practice, you can choose the right annotation to use depending on the specific application scenario.

2. Analysis from the code level

At the Spring Framework source level, the fully qualified paths for the @Resource and @Autowired annotations are javax.annotation.Resource and org.springframework.beans.factory.annotation.Autowired. Let’s take a look at their implementation.

Implementation principles of @Resource annotation:

  1. if the name attribute of the @Resource annotation is not empty, the Spring container looks for the bean instance object to be injected based on the value of the attribute; if the name attribute is empty, the default is to use the field name as the bean name to look for it.
  2. The Spring container will first look for the bean instance based on the byName injection method, and if it does not find the corresponding bean instance, it will look for it based on the byType method.
  3. If the corresponding bean instance is found, the bean instance is injected into the class or object that needs to use it using the reflection mechanism.
  4. If the corresponding bean instance is not found, a NoSuchBeanDefinitionException is thrown.
1
2
3
4
5
6
7
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Resource {
    String name() default "";
    Class<?> type() default java.lang.Object.class;
    boolean shareable() default true;
}

How the @Autowired annotation is implemented:

  1. The Spring container will process the AutowiredAnnotationBeanPostProcessor class and the derived classes of the AutowiredAnnotationBeanPostProcessor class according to their derivatives, injecting all the properties marked with the @Autowired annotation into the class that needs to be used or object to be used.
  2. For the AuthorizedAnnotationBeanPostProcessor, which implements the BeanPostProcessor interface, the @Autowired annotation is intercepted during the instantiation and initialization of the bean.
  3. When the Spring container encounters an @Autowired annotation, it will automatically call the postProcessPropertyValues method of the AutowiredAnnotationBeanPostProcessor class, which will perform different dependency injection processes depending on the value of the annotated property.
  4. When handling @Autowired annotations, the Spring container by default uses the byType method to look up the bean and if there are multiple beans of matching types, they are matched by class name; if it is still not sure which bean to inject, a NoSuchBeanDefinitionException is thrown.
1
2
3
4
5
6
7
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required() default true;
    String value() default "";
}

Both the @Resource annotation and the @Autowired annotation are implemented in the Spring Framework source code through the reflection mechanism and the BeanPostProcessor interface for dependency injection. In practice, we can study the corresponding source implementations to get a deeper understanding of how they work, so that we can use these dependency injection annotations better.

Reference: https://juejin.cn/post/7223286420794966076