Instantiation of Spring bean and Detailed Explanation of IOC Dependency Injection

  • 2021-07-06 10:56:33
  • OfStack

Preface

As we know, IOC is the core of Spring. It is responsible for controlling the life cycle of objects and the relationships between objects.

For example, how do we find someone? It is common to go around on the road to see which MM is beautiful and in good shape, which suits our taste. Just ask for their phone numbers, make connections and find ways to get to know them, and then... omit N step here, and finally fall in love and get married.

IOC is like a matchmaking agency here, which has a lot of information about marriageable men and women. If you have any needs, just tell it what kind of girlfriend you need. It will provide us with an MM, direct love and marriage, perfect!

Let's look at how Spring generates and manages these objects.

1. Method entry
The org. springframework. beans. factory. support. DefaultListableBeanFactory. preInstantiateSingletons () method is today's protagonist, and 1 cut begins with it.


 public void preInstantiateSingletons() throws BeansException {
    //beanDefinitionNames Is to go up 1 All after the initialization of the section is completed BeanDefinition Adj. beanName
    List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
    for (String beanName : beanNames) {
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
        //getBean Is the main force in the main force, responsible for instantiation Bean And IOC Dependency injection 
        getBean(beanName);
      }
    }
  }

2. Instantiation of Bean

In the entry method getBean, the doCreateBean method is called first. The first step is to instantiate an Bean through reflection.


  protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
      //createBeanInstance Is to instantiate Bean The process is nothing more than 1 Some judgments plus reflection, and finally call ctor.newInstance(args);
      instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
  }

3. Support for Annotation

After the Bean instantiation is completed, a post-processor code will be entered. From the code point of view, filter the class that implements the MergedBeanDefinitionPostProcessor interface and call its postProcessMergedBeanDefinition () method. Who implemented the MergedBeanDefinitionPostProcessor interface? Let's focus on three

AutowiredAnnotationBeanPostProcessor CommonAnnotationBeanPostProcessor RequiredAnnotationBeanPostProcessor

Remember in Spring source code analysis (1) Spring initialization and XML, we said that Spring supports annotation-config tags, and registered one special Bean, which just includes the above three. Let's see what they secretly did.

From the method name, they do the same thing, loading annotation metadata. Two more identical things are done inside the method


ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() 
ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback()

Looking at the parameters of the method, targetClass is the Class object of Bean. Next, you can get its fields and methods, judge whether it contains corresponding annotations, and finally turn them into InjectionMetadata objects. The following is a pseudo-code to show the processing process.


  public static void main(String[] args) throws ClassNotFoundException {
    Class<?> clazz = Class.forName("com.viewscenes.netsupervisor.entity.User");
    Field[] fields = clazz.getFields();
    Method[] methods = clazz.getMethods();

    for (int i = 0; i < fields.length; i++) {
      Field field = fields[i];
      if (field.isAnnotationPresent(Autowired.class)) {
        // Convert to AutowiredFieldElement Object, add the container 
      }
    }
    for (int i = 0; i < methods.length; i++) {
      Method method = methods[i];
      if (method.isAnnotationPresent(Autowired.class)) {
        // Convert to AutowiredMethodElement Object, add the container 
      }
    }
    return new InjectionMetadata(clazz, elements);
  }

The InjectionMetadata object has two important properties, targetClass and injectedElements, which are the focus of annotated dependency injection.


  public InjectionMetadata(Class<?> targetClass, Collection<InjectedElement> elements) {
    //targetClass Yes Bean Adj. Class Object 
    this.targetClass = targetClass; 
    //injectedElements Yes 1 A InjectedElement A collection of objects 
    this.injectedElements = elements;
  }
  //member Is the member itself, a field, or a method 
  //pd Yes JDK The introspection mechanism object in, and the following injection attribute values need to be used 
  protected InjectedElement(Member member, PropertyDescriptor pd) {
    this.member = member;
    this.isField = (member instanceof Field);
    this.pd = pd;
  }

Having said so much, finally look at what the source code is like, taking Autowired as an example.


ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
  @Override
  public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
    AnnotationAttributes ann = findAutowiredAnnotation(field);
    if (ann != null) {
      if (Modifier.isStatic(field.getModifiers())) {
        if (logger.isWarnEnabled()) {
          logger.warn("Autowired annotation is not supported on static fields: " + field);
        }
        return;
      }
      boolean required = determineRequiredStatus(ann);
      currElements.add(new AutowiredFieldElement(field, required));
    }
  }
});

ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
  @Override
  public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
    Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
    if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
      return;
    }
    AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
    if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
      if (Modifier.isStatic(method.getModifiers())) {
        if (logger.isWarnEnabled()) {
          logger.warn("Autowired annotation is not supported on static methods: " + method);
        }
        return;
      }
      if (method.getParameterTypes().length == 0) {
        if (logger.isWarnEnabled()) {
          logger.warn("Autowired annotation should be used on methods with parameters: " + method);
        }
      }
      boolean required = determineRequiredStatus(ann);
      PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
      currElements.add(new AutowiredMethodElement(method, required, pd));
    }
  }
});

4. Dependency injection

The instantiation of Bean in the doCreateBean () method was completed, followed by dependency injection.

There are two ways of dependency injection in Bean, one is configuration file and the other is annotation.

4.1. Annotated injection process

In section 3 above, Spring has filtered Field and Method with annotations such as @ Autowired, @ Resource on Bean instances, and returned InjectionMetadata objects containing Class objects, introspective objects, and members. Take @ Autowired as an example, this time calling AutowiredAnnotationBeanPostProcessor. postProcessPropertyValues ().

First, get the InjectionMetadata object, and then judge whether the InjectedElement set in it is empty, that is to say, judge whether the fields and methods of Bean contain @ Autowired. Then call InjectedElement. inject (). InjectedElement has two subclasses, AutowiredFieldElement and AutowiredMethodElement, one of which obviously handles Field and the other handles Method.

4.1.1 AutowiredFieldElement

If Autowired is annotated on the field, its configuration is like this.


public class User { 
  @Autowired
  Role role;
}

protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
  // With User Class in the @Autowired Role role For example, here's field Is 
  //public com.viewscenes.netsupervisor.entity.Role com.viewscenes.netsupervisor.entity.User.role
  Field field = (Field) this.member;
  Object value;
  DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
  desc.setContainingClass(bean.getClass());
  Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
  TypeConverter typeConverter = beanFactory.getTypeConverter();
  try {
    // Here's beanName Because Bean , so it will re-enter populateBean Method, complete the Role Injection of objects 
    //value == com.viewscenes.netsupervisor.entity.Role@7228c85c
    value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
  }
  catch (BeansException ex) {
    throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
  }
  if (value != null) {
    // Setting accessible, direct assignment 
    ReflectionUtils.makeAccessible(field);
    field.set(bean, value);
  }
}

4.1.2 AutowiredFieldElement

If Autowired is annotated on the method, it must be written like this.


public class User {
  @Autowired
  public void setRole(Role role) {}
}

Its inject approach is similar to the one above, but the last is method. invoke. Interested partners can turn over the source code.


ReflectionUtils.makeAccessible(method);
method.invoke(bean, arguments);

4.2. Configuration file injection process

Looking at a configuration file first, we injected instances of id, name, age, and Role into the User class.


  protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
      //createBeanInstance Is to instantiate Bean The process is nothing more than 1 Some judgments plus reflection, and finally call ctor.newInstance(args);
      instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
  }
0

In Spring source code analysis (1) Spring initialization and XML section 4.2, bean tag parsing, we see that after the Class object of Bean is reflected, its property attribute will be set, that is, the parsePropertyElements () method is called. There is an MutablePropertyValues attribute in the BeanDefinition object.


  protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
      //createBeanInstance Is to instantiate Bean The process is nothing more than 1 Some judgments plus reflection, and finally call ctor.newInstance(args);
      instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
  }
1

The above figure is the structure of MutablePropertyValues attribute in BeanDefinition object. Now that you have the name and value of property, injection is relatively simple. Get the writeMethod object from the introspective object PropertyDescriptor, set it to be accessible, and invoke can be used. PropertyDescriptor has two objects readMethodRef, and writeMethodRef actually corresponds to get set method.


  protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
      //createBeanInstance Is to instantiate Bean The process is nothing more than 1 Some judgments plus reflection, and finally call ctor.newInstance(args);
      instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
  }
2

5. initializeBean
After Bean instantiation and IOC dependency injection, Spring leaves aside extensions that allow us to do some initialization work on Bean.

5.1, Aware

Aware is an empty interface with nothing. However, many xxxAware inherit from it. Let's look at the source code below. If necessary, our Bean can implement the following interface to get what we want.


  // In instantiating and IOC Called after dependency injection is complete 
    private void invokeAwareMethods(final String beanName, final Object bean) {
    if (bean instanceof Aware) {
      // Let our Bean You can get your own in the container beanName
      if (bean instanceof BeanNameAware) {
        ((BeanNameAware) bean).setBeanName(beanName);
      }
      // Available ClassLoader Object 
      if (bean instanceof BeanClassLoaderAware) {
        ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
      }
      // Available BeanFactory Object 
      if (bean instanceof BeanFactoryAware) {
        ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
      }
      if (bean instanceof EnvironmentAware) {
        ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
      }
      if (bean instanceof EmbeddedValueResolverAware) {
        ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
      }
      if (bean instanceof ResourceLoaderAware) {
        ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
      }
      if (bean instanceof ApplicationEventPublisherAware) {
        ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
      }
      if (bean instanceof MessageSourceAware) {
        ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
      }
      if (bean instanceof ApplicationContextAware) {
        ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
      }
      ...... Unfinished 
    }
  }

The practice is as follows:


  protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
      //createBeanInstance Is to instantiate Bean The process is nothing more than 1 Some judgments plus reflection, and finally call ctor.newInstance(args);
      instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
  }
4

//Output results
BeanNameAware:awareTest1
BeanClassLoaderAware:WebappClassLoader
context: /springmvc_dubbo_producer
delegate: false
repositories:
/WEB-INF/classes/
---------- > Parent Classloader:
java.net.URLClassLoader@2626b418
BeanFactoryAware: org. springframework. beans. factory. support. DefaultListableBeanFactory @ 5b4686b4: defining beans... Unfinished

5.2. Initialize

There are three ways to initialize Bean. In order, @ PostConstruct, afterPropertiesSet, init-method

5.2.1 @PostConstruct

This annotation is deeply hidden, and it is called in InitDestroyAnnotationBeanPostProcessor, the parent class of CommonAnnotationBeanPostProcessor. The initialization method of this annotation does not support taking parameters, and will throw an exception directly.


  protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
      //createBeanInstance Is to instantiate Bean The process is nothing more than 1 Some judgments plus reflection, and finally call ctor.newInstance(args);
      instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
  }
5

5.2.3 init-method


  protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
      //createBeanInstance Is to instantiate Bean The process is nothing more than 1 Some judgments plus reflection, and finally call ctor.newInstance(args);
      instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
  }
6

6. Registration

registerDisposableBeanIfNecessary () completes the cache registration of Bean, registering Bean into Map.


Related articles: