An overview,

[1] Note usage

According to the source code for the @AutoWired annotation, you can see that the annotation can be applied to constructors, parameters, methods, and properties, all of which get the value of the parameter component from the container

  • The annotation on the method: @bean + method parameter, the parameter is taken from the container, the default is not @autowired effect is the same, can be automatically assembled
  • The annotation is on the constructor: if there is only one parameter construct on the component, the @autowired of the parameter construct can be omitted, and the component of the parameter position can still be automatically fetched from the container
  • Annotation in parameter position
  • The annotation is in the property position
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required(a) default true;
}
Copy the code

Related notes:

  • Autowired: Assembly by type by default. If we want to use assembly by name, we can use it in conjunction with the @qualifier annotation (provided by Spring).
  • Qualifier() : Specifies the assembled bean, which can be used in conjunction with @AutoWired when multiple instances are involved
  • @primary: annotated when multiple bean candidates are present during autowiring@PrimaryThe bean will be the preferred
  • @resource: Assembly by name by default, assembly by type if no bean matching the name can be found (not supported@Primaryand@Autowired(required = false)Functionality, provided by JDK)
  • Inject: package that needs to be imported into Javax. Inject, same as Autowired functionality, but notrequired=falseFeatures (provided by the JDK)

[2] Automatic assembly

Spring uses dependency injection (DI) to assign dependencies to individual components in the IOC container

@AutowiredAutomatic injection (provided by Spring) :

  • Default in accordance with the priority to the container to find the corresponding components: applicationContext. GetBean ()
  • If multiple components of the same type are found, the name of the property is used as the component ID to look up the component in the container
  • Qualifier() annotation: This annotation specifies the ID of the component to be assembled, rather than using the attribute name
  • By default, autowiring must assign a value to a property. If it does not, an error will be reported@Autowired(required = false)Specifying that it is not necessary will not generate an error
  • @primary Annotation: annotated when multiple bean candidates are present during autowiring@PrimaryThe bean will be the preferred, otherwise an exception will be thrown if used@Qualifier()Specify an assembly bean, and use the bean that explicitly specifies the assembly

@Resource(JSR250) and @Inject(JSR330) (provided by JDK)

@ the Resource:

  • By default, assembly is performed by component name, or by name
  • When no bean matching the name is found, assembly is done by type
  • Does not support@Primaryand@Autowired(required = false)function
  • If both name and Type are specified, a unique matching bean is found from the Spring context and assembled, failing which an exception is thrown.
  • If name is specified, the bean whose name (ID) matches is searched from the context for assembly, and an exception is thrown if no bean is found.
  • If type is specified, an exception is thrown if a unique bean similar to a match is found from the context and assembled.
  • If neither name nor type is specified, byName is automatically used for assembly. If there is no match, it falls back to a primitive type for matching, and if there is a match, it is automatically assembled.

@ Inject:

  • The package that needs to import Javax. inject is the same as the Autowired function, but not availablerequired=falsefunction

[3] The difference between @autoWired and @Resource annotations

  • @autoWired is provided by Spring and injected as byType only; @Resource is provided by J2EE and is automatically injected by byName by default. When no bean matching the name is found, it is assembled by type
  • By default, the dependent object must be required to exist. If you want to allow a null value, you can set its required property to false. Name assembly can be used with the @qualifier annotation if desired.
  • @resource, by default, the assembly is based on the name. The name can be specified by the name attribute. If the name attribute is not specified, when the annotation is written on the field, the name of the field is used by default for name lookup. If the annotation is written on a setter method, it defaults to the property name for assembly. Assemble by type when no bean matching the name can be found. Note, however, that if the name attribute is specified, it will only be assembled by name.

Ii. Case analysis

Here, only the @AutoWired annotation annotation is instantiated at the attribute location

[1] @autowired annotation

/ / start the class
@Test
public void TestMain(a) {
    // Create an IOC container
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    UserService userService = applicationContext.getBean(UserService.class);
    System.out.println("userService:" + userService);
}

// Service
@Service
public class UserService {
    @Autowired(required = false)	// Specify optional
    @Qualifier("userDao2")		// Specify the assembly bean
    private UserDao userDao;
    @Override
    public String toString(a) {
        return "UserService{" +
                "userDao=" + userDao +
                '} '; }}// Dao
@Repository
public class UserDao {
    private String label = "1";
    public void setLabel(String label) {
        this.label = label;
    }
    @Override
    public String toString(a) {
        return "UserDao{" +
                "label='" + label + '\' ' +
                '} '; }}/ / configuration class
@Configuration
@ComponentScan({"dao","service","controller"})
public class AppConfig {
    @Primary		// The assembly bean is preferred
    @Bean("userDao2")
    public UserDao userDao(a){
        UserDao userDao = new UserDao();
        userDao.setLabel("2");
        returnuserDao; }}Copy the code

The output is as follows, since @qualifier (“userDao2”) was used above to specify the bean to be assembled, so the output is label= ‘2’ :

  • If you have@Qualifier("userDao2")Instead of@Qualifier("userDao"), the assembly isLabel = '1'

[2] @resource annotation

@Service
public class UserService {
    @Resource(name = "userDao2",type = UserDao.class)
    private UserDao userDao;
    @Override
    public String toString(a) {
        return "UserService{" +
                "userDao=" + userDao +
                '} '; }}Copy the code
  • By default, assembly is performed by component name, or by name
  • When no bean matching the name is found, assembly is done by type
  • Does not support@Primaryand@Autowired(required = false)function
  • If both name and Type are specified, a unique matching bean is found from the Spring context and assembled, failing which an exception is thrown.
  • If name is specified, the bean whose name (ID) matches is searched from the context for assembly, and an exception is thrown if no bean is found.
  • If type is specified, an exception is thrown if a unique bean similar to a match is found from the context and assembled.
  • If neither name nor type is specified, byName is automatically used for assembly. If there is no match, it falls back to a primitive type for matching, and if there is a match, it is automatically assembled.

Third, source tracking

Here is the source code analysis of the underlying @Autowired annotation

Reference: blog.csdn.net/topdevelope…

@autowired is used for assembly of the bean, affirmation and bean instantiation, the refresh method after the first, in finishBeanFactoryInitialization getBean method, and then trigger a bean initialization getObject. Bean initialization is a complicated place, in AbstractAutowireCapableBeanFactory# doCreateBean method, first create a BeanWrapper, Its internal member variable wrappedObject stores the instantiated MyService object, and then enters the populateBean method for property injection

Spring for the realization of the autowire annotation logic in class: AutowiredAnnotationBeanPostProcessor# postProcessProperties, — >findAutowiringMetadata — >buildAutowiringMetadata, the core code in buildAutowiringMetadata method inside

private InjectionMetadata buildAutowiringMetadata(Class
        clazz) {
    if(! AnnotationUtils.isCandidateClass(clazz,this.autowiredAnnotationTypes)) {
        return InjectionMetadata.EMPTY;
    } else {
        List<InjectedElement> elements = new ArrayList();
        // The target class to process
        Class targetClass = clazz;

        do {
            List<InjectedElement> currElements = new ArrayList();
            // get all the fields of the class via reflection, iterate over each field, and use the findAutowiredAnnotation method to iterate over the used annotations for each field, and return the auotowired attribute if decorated with autowiredReflectionUtils.doWithLocalFields(targetClass, (field) -> { MergedAnnotation<? > ann =this.findAutowiredAnnotation(field);
                if(ann ! =null) {
                    // Verify that the autowired annotation is used on static methods
                    if (Modifier.isStatic(field.getModifiers())) {
                        if (this.logger.isInfoEnabled()) {
                            this.logger.info("Autowired annotation is not supported on static fields: " + field);
                        }

                        return;
                    }

                    // Determine whether required is specified
                    boolean required = this.determineRequiredStatus(ann);
                    currElements.add(newAutowiredAnnotationBeanPostProcessor.AutowiredFieldElement(field, required)); }});// The same logic as above, but the method of the class is handled by reflection
            ReflectionUtils.doWithLocalMethods(targetClass, (method) -> {
                Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
                if(BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { MergedAnnotation<? > ann =this.findAutowiredAnnotation(bridgedMethod);
                    if(ann ! =null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                        if (Modifier.isStatic(method.getModifiers())) {
                            if (this.logger.isInfoEnabled()) {
                                this.logger.info("Autowired annotation is not supported on static methods: " + method);
                            }

                            return;
                        }

                        if (method.getParameterCount() == 0 && this.logger.isInfoEnabled()) {
                            this.logger.info("Autowired annotation should only be used on methods with parameters: " + method);
                        }

                        boolean required = this.determineRequiredStatus(ann);
                        PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                        currElements.add(newAutowiredAnnotationBeanPostProcessor.AutowiredMethodElement(method, required, pd)); }}});// There may be more than one @autowired annotation, so they are all added to the currElements container and processed together
            elements.addAll(0, currElements);
            targetClass = targetClass.getSuperclass();
        } while(targetClass ! =null&& targetClass ! = Object.class);returnInjectionMetadata.forElements(elements, clazz); }}Copy the code
  • Gets the target class to work with
  • Pass in the target class parameter with the doWithLocalFields method, get all the fields of the class with reflection, and iterate over each field, and iterate over the used notation for each field with the findAutowiredAnnotation method, and if decorated with autowired, The auotoWired related property is returned
  • Determines whether the Autowired annotation is used on static methods
  • If there are multiple @AutoWired annotations, add them to the currElements container and process them together

Finally, return an InjectionMetadata collection containing all the annotations decorated with Autowire, as follows

  • TargetClass: targetClass to process
  • Elements: All sets of elements obtained by the above method
public InjectionMetadata(Class
        targetClass, Collection
       
         elements)
        {
    this.targetClass = targetClass;
    this.injectedElements = elements;
}
Copy the code

Now that we have the target class and all the elements we need to inject, we can implement AutoWired’s dependency injection logic as follows:

public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
    if (!this.validatedBeanNames.contains(beanName)) {
        if (!this.shouldSkip(this.beanFactory, beanName)) {
            List<String> invalidProperties = new ArrayList();
            PropertyDescriptor[] var6 = pds;
            int var7 = pds.length;

            for(int var8 = 0; var8 < var7; ++var8) {
                PropertyDescriptor pd = var6[var8];
                if (this.isRequiredProperty(pd) && !pvs.contains(pd.getName())) {
                    invalidProperties.add(pd.getName());
                }
            }

            if(! invalidProperties.isEmpty()) {throw new BeanInitializationException(this.buildExceptionMessage(invalidProperties, beanName)); }}this.validatedBeanNames.add(beanName);
    }

    return pvs;
}
Copy the code

Call the Inject method defined in InjectionMetadata:

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Collection<InjectionMetadata.InjectedElement> checkedElements = this.checkedElements; Collection<InjectionMetadata.InjectedElement> elementsToIterate = checkedElements ! =null ? checkedElements : this.injectedElements;
    if(! ((Collection)elementsToIterate).isEmpty()) { Iterator var6 = ((Collection)elementsToIterate).iterator();while(var6.hasNext()) { InjectionMetadata.InjectedElement element = (InjectionMetadata.InjectedElement)var6.next(); element.inject(target, beanName, pvs); }}}Copy the code

Iterate, and then call inject method, inject method its implementation logic is as follows:

protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs) throws Throwable {
    if (this.isField) {
        Field field = (Field)this.member;
        // Brute force method, through reflection technology to instantiate and assign values to objects
        ReflectionUtils.makeAccessible(field);
        field.set(target, this.getResourceToInject(target, requestingBeanName));
    } else {
        if (this.checkPropertySkipping(pvs)) {
            return;
        }

        try {
            Method method = (Method)this.member;
            ReflectionUtils.makeAccessible(method);
            // The name of the injected bean. The function of this method is to fetch the bean based on its name
            method.invoke(target, this.getResourceToInject(target, requestingBeanName));
        } catch (InvocationTargetException var5) {
            throwvar5.getTargetException(); }}}Copy the code
  • The use of reflection technology, divided into fields and methods to deal with.
  • Methods like makeAccessible, which can be called brute-force cracking, use reflection techniques to instantiate and assign values to objects
  • The getResourceToInject method takes the name of the bean to inject. The function of this method is to retrieve the bean based on its name

Four,

@autowired Automatic injection process diagram: