How many ways to instantiate Spring? Why is Cglib used?


Author: Xiao Fu Ge
Blog: https://bugstack.cn

Precipitate, share, and grow, so that you and others can gain something! ๐Ÿ˜„

Catalogue of "Spring Handbook Column"

I. Introduction

ๆŠ€ๆœฏๆˆ้•ฟ๏ผŒๆ˜ฏๅฏนๅœบๆ™ฏ่ฎพ่ฎก็ป†่Š‚ไธๆ–ญ็š„้›•ๅˆป๏ผ

When do you think your technology has been improved rapidly, after writing more CRUD? Don't even think about it, it's absolutely impossible! No matter how much CRUD writes, it can only satisfy you as a brick-moving tool, and the speed of hitting less logic pipeline code, and the programming ability, except for the first one from unskilled to proficient, there are very few other things. Promoted.

Then you might think what is the improvement of programming ability? In fact, the improvement of more programming ability is your architectural control of complex scenarios and the continuous use of large-scale traffic impact verification for each technical implementation detail. Whether you can ensure the stable operation of the system and determine how much you have seen , How much has been learned, how much has been improved!

When you finally pick a product in demand, began to think about ็จ‹ๅบๆ•ฐๆฎ็ป“ๆž„็š„่ฎพ่ฎก, , ๆ ธๅฟƒๅŠŸ่ƒฝ็š„็ฎ—ๆณ•้€ป่พ‘ๅฎž็Žฐ, ๆ•ดไฝ“ๆœๅŠก็š„่ฎพ่ฎกๆจกๅผไฝฟ็”จ, ็ณป็ปŸๆžถๆž„็š„ๆญๅปบๆ–นๅผ, ๅบ”็”จ้›†็พค็š„้ƒจ็ฝฒ็ป“ๆž„so is the ability to really improve programming time!

2. Goal

The goal of this chapter is mainly to solve us in the previous chapter.ๅŸ‹ไธ‹็š„ๅ‘ What pit is that? In fact, it is a pit about the instantiation of Bean objects with constructors.

In the previous chapter, we expanded the function of the Bean container and handed the instantiated object to the container for unified processing, but in the code of our instantiated object, we did not consider whether the object class contains a constructor, that is to say, if we go to the instance If an object with a constructor is used, then an exception will be thrown.

How to verify? In fact, it is enough to add UserService with a constructor containing parameter information, as follows:

public class UserService {

    private String name;

    public UserService(String name) {
        this.name = name;
    }  

    // ...
}

The error is as follows:

java.lang.InstantiationException: cn.bugstack.springframework.test.bean.UserService

	at java.lang.Class.newInstance(Class.java:427)
	at cn.bugstack.springframework.test.ApiTest.test_newInstance(ApiTest.java:51)
	...

The main reason is because of the occurrence of this phenomenon beanDefinition.getBeanClass().newInstance();is instantiated way and did not consider the argument constructor, so on this pit in this waiting for you! Then our goal is obvious, to fill this hole!

Three, design

The technical design of filling this pit mainly considers two parts. One is how to transfer the input parameter information of the constructor to the instantiation operation in a reasonable way from the string process, and the other is how to instantiate the object containing the constructor.

Pic 4-1
  • Reference source container the Spring Bean implementation, added to the BeanFactory Object getBean(String name, Object... args)the interface, so that you can obtain when the Bean constructor parameter of the transmission of information into it.
  • Another core content is what method is used to create a Bean object with a constructor? There are two ways to choose, one is based on Java's own methods DeclaredConstructor, and the other is to use Cglib to dynamically create Bean objects. Cglib is implemented based on the bytecode framework ASM, so you can also directly create objects through ASM operation instruction codes

Fourth, realize

1. Engineering structure

small-spring-step-03
โ””โ”€โ”€ src
    โ”œโ”€โ”€ main
    โ”‚   โ””โ”€โ”€ java
    โ”‚       โ””โ”€โ”€ cn.bugstack.springframework.beans
    โ”‚           โ”œโ”€โ”€ factory
    โ”‚           โ”‚   โ”œโ”€โ”€ factory
    โ”‚           โ”‚   โ”‚   โ”œโ”€โ”€ BeanDefinition.java
    โ”‚           โ”‚   โ”‚   โ””โ”€โ”€ SingletonBeanRegistry.java
    โ”‚           โ”‚   โ”œโ”€โ”€ support
    โ”‚           โ”‚   โ”‚   โ”œโ”€โ”€ AbstractAutowireCapableBeanFactory.java
    โ”‚           โ”‚   โ”‚   โ”œโ”€โ”€ AbstractBeanFactory.java
    โ”‚           โ”‚   โ”‚   โ”œโ”€โ”€ BeanDefinitionRegistry.java
    โ”‚           โ”‚   โ”‚   โ”œโ”€โ”€ CglibSubclassingInstantiationStrategy.java
    โ”‚           โ”‚   โ”‚   โ”œโ”€โ”€ DefaultListableBeanFactory.java
    โ”‚           โ”‚   โ”‚   โ”œโ”€โ”€ DefaultSingletonBeanRegistry.java
    โ”‚           โ”‚   โ”‚   โ”œโ”€โ”€ InstantiationStrategy.java
    โ”‚           โ”‚   โ”‚   โ””โ”€โ”€ SimpleInstantiationStrategy.java
    โ”‚           โ”‚   โ””โ”€โ”€ BeanFactory.java
    โ”‚           โ””โ”€โ”€ BeansException.java
    โ””โ”€โ”€ test
        โ””โ”€โ”€ java
            โ””โ”€โ”€ cn.bugstack.springframework.test
                โ”œโ”€โ”€ bean
                โ”‚   โ””โ”€โ”€ UserService.java
                โ””โ”€โ”€ ApiTest.java

Project source code :ๅ…ฌไผ—ๅทใ€Œbugstack่™ซๆดžๆ ˆใ€๏ผŒๅ›žๅค๏ผšSpring ไธ“ๆ ๏ผŒ่Žทๅ–ๅฎŒๆ•ดๆบ็ 

Spring Bean container class relationship, as shown in Figure 4-2

Figure 4-2

This chapter โ€œๅกซๅ‘โ€mainly adds the InstantiationStrategy instantiation strategy interface to the existing project, and supplements the corresponding getBean input parameter information, so that the input parameters of the constructor can be passed and instantiated smoothly during external calls.

2. Add the getBean interface

cn.bugstack.springframework.beans.factory.BeanFactory

public interface BeanFactory {

    Object getBean(String name) throws BeansException;

    Object getBean(String name, Object... args) throws BeansException;

}
  • In BeanFactory, we overload a getBean method that contains the input parameter information args, so that it is convenient to pass the input parameters to the constructor for instantiation.

3. Define the instantiation strategy interface

cn.bugstack.springframework.beans.factory.support.InstantiationStrategy

public interface InstantiationStrategy {

    Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException;

}
  • Add the necessary input parameter information in the instantiate method of the instantiated interface, including: beanDefinition, beanName, ctor, args
  • The Constructor may be a little unfamiliar to you. It is the Constructor class under the java.lang.reflect package, which contains some necessary class information. The purpose of this parameter is to get the constructor corresponding to the input parameter information.
  • And args is a specific input parameter information, which will be used in the final instantiation.

4. JDK instantiation

cn.bugstack.springframework.beans.factory.support.SimpleInstantiationStrategy

public class SimpleInstantiationStrategy implements InstantiationStrategy {

    @Override
    public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {
        Class clazz = beanDefinition.getBeanClass();
        try {
            if (null != ctor) {
                return clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);
            } else {
                return clazz.getDeclaredConstructor().newInstance();
            }
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new BeansException("Failed to instantiate [" + clazz.getName() + "]", e);
        }
    }

}
  • First obtain the Class information through beanDefinition, which is passed in when the Bean is defined.
  • Next, determine whether the ctor is empty, if it is empty, it means instantiation without a constructor, otherwise, it needs instantiation with a constructor.
  • Here we focus on the instantiation with constructor, the way of instantiation is to clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);pass the input parameter information to newInstance for instantiation.

5. Cglib instantiation

cn.bugstack.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy

public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {

    @Override
    public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(beanDefinition.getBeanClass());
        enhancer.setCallback(new NoOp() {
            @Override
            public int hashCode() {
                return super.hashCode();
            }
        });
        if (null == ctor) return enhancer.create();
        return enhancer.create(ctor.getParameterTypes(), args);
    }

}
  • In fact, Cglib is also very convenient to create a bean with a constructor. Here we have simplified the processing. If you read the Spring source code, you will also see implementations such as CallbackFilter, but our current method will not affect the creation.

6. Create a policy call

cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        Object bean = null;
        try {
            bean = createBeanInstance(beanDefinition, beanName, args);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }

        addSingleton(beanName, bean);
        return bean;
    }

    protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
        Constructor constructorToUse = null;
        Class<?> beanClass = beanDefinition.getBeanClass();
        Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
        for (Constructor ctor : declaredConstructors) {
            if (null != args && ctor.getParameterTypes().length == args.length) {
                constructorToUse = ctor;
                break;
            }
        }
        return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);
    }

}
  • First of all, an instantiation strategy attribute class for creating objects is defined in the AbstractAutowireCapableBeanFactory abstract class InstantiationStrategy instantiationStrategy. Here we choose the Cglib implementation class.
  • Next extraction createBeanInstancemethod, this method should be noted that represents the number of constructors you have a Constructor, can be obtained by beanClass.getDeclaredConstructors () all the way to your constructor, is a collection.
  • Next, it is necessary recycle ratio as a function of the set of parameters into configuration information argsmatching the situation, where we simple way of contrast, only a number of comparison, the actual Spring
    source code needed than for the parameter type, or the same number of different In the case of the parameter type, an exception will be thrown.

Five, test

1. Prepare in advance

cn.bugstack.springframework.test.bean.UserService

public class UserService {

    private String name;

    public UserService(String name) {
        this.name = name;
    }

    public void queryUserInfo() {
        System.out.println("ๆŸฅ่ฏข็”จๆˆทไฟกๆฏ๏ผš" + name);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("");
        sb.append("").append(name);
        return sb.toString();
    }
}
  • The only thing that is added to UserService here is a constructor with name as an argument, so that we can verify whether such an object can be instantiated.

2. Test cases

cn.bugstack.springframework.test.ApiTest

@Test
public void test_BeanFactory() {
    // 1.ๅˆๅง‹ๅŒ– BeanFactory
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

    // 2. ๆณจๅ…ฅbean
    BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
    beanFactory.registerBeanDefinition("userService", beanDefinition);

    // 3.่Žทๅ–bean
    UserService userService = (UserService) beanFactory.getBean("userService", "ๅฐๅ‚…ๅ“ฅ");
    userService.queryUserInfo();
}
  • In this unit test, in addition to the three steps: Bean Factory, Registering Bean, and Obtaining Bean, it also adds an additional object acquisition and call. The main test here is to verify whether the singleton object is correctly stored in the cache.
  • In addition, unlike the testing process in the previous chapter, we passed UserService.class to BeanDefinition instead of directly using the new UserService() operation as in the previous chapter.

3. Test results

ๆŸฅ่ฏข็”จๆˆทไฟกๆฏ๏ผšๅฐๅ‚…ๅ“ฅ

Process finished with exit code 0
  • From the test results, the biggest change is that objects with constructors can be instantiated.
  • You can try to use two different instantiation strategies to instantiate. SimpleInstantiationStrategy,CglibSubclassingInstantiationStrategy

4. Operation case

Here we put several different methods of instantiation operations into the unit test, so that everyone can compare and learn.

4.1 No constructor

@Test
public void test_newInstance() throws IllegalAccessException, InstantiationException {
    UserService userService = UserService.class.newInstance();
    System.out.println(userService);
}
  • This method of instantiation is also the method we used directly when we implemented the Spring Bean container in the previous chapter.

4.2 Verify that the constructor is instantiated

@Test
public void test_constructor() throws Exception {
    Class<UserService> userServiceClass = UserService.class;
    Constructor<UserService> declaredConstructor = userServiceClass.getDeclaredConstructor(String.class);
    UserService userService = declaredConstructor.newInstance("ๅฐๅ‚…ๅ“ฅ");
    System.out.println(userService);
}
  • From the point of view the most simple operation, if there is a class constructor need to be instantiated, the need to use the getDeclaredConstructoracquired constructor after instantiated by passing parameters.

4.3 Get constructor information

@Test
public void test_parameterTypes() throws Exception {
    Class<UserService> beanClass = UserService.class;
    Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
    Constructor<?> constructor = declaredConstructors[0];
    Constructor<UserService> declaredConstructor = beanClass.getDeclaredConstructor(constructor.getParameterTypes());
    UserService userService = declaredConstructor.newInstance("ๅฐๅ‚…ๅ“ฅ");
    System.out.println(userService);
  • In fact, the core point in this case is to get all the constructors in a class, which is actually the use of this method beanClass.getDeclaredConstructors()

4.4 Cglib instantiation

@Test
public void test_cglib() {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(UserService.class);
    enhancer.setCallback(new NoOp() {
        @Override
        public int hashCode() {
            return super.hashCode();
        }
    });
    Object obj = enhancer.create(new Class[]{String.class}, new Object[]{"ๅฐๅ‚…ๅ“ฅ"});
    System.out.println(obj);
}
  • This case demonstrates the use of very simple, but about the use of Cglib in the Spring container is very much, you can also learn about the extended knowledge of Cglib.

Six, summary

  • The main purpose of this chapter is to improve the instantiation operation, increase the InstantiationStrategy instantiation strategy interface, and add two new instantiation classes. The name and implementation of this part of the class is basically a reduced version of the Spring framework. You can also find the corresponding code from the Spring source code during the learning process.
  • It can be seen from our continuous improvement and increase of requirements that when your code structure is designed more reasonable, you can easily and conveniently extend the class responsibilities of different attributes without causing confusion in the class structure due to the increase in requirements. . Therefore, in the process of realizing our own business requirements, we should also consider a good scalability and split the responsibilities of good classes as much as possible.
  • Hands-on is the fastest way to learn. Don't let your eyes feel like you can see it, but the hands-on operation will be useless. I also hope that readers in need can do it by themselves, incorporate your ideas into the code that can be implemented, and see if what they think is consistent with what they do.

Seven, series recommendation