Сколько существует методов создания экземпляров Spring? Зачем использовать Cglib?

задняя часть Spring
Сколько существует методов создания экземпляров Spring? Зачем использовать Cglib?

Автор: Брат Сяофу
Блог:bugstack.cn

Осаждайте, делитесь, растите и позвольте себе и другим что-то получить! 😄

Справочник «Весенняя практическая колонка»

Введение

技术成长,是对场景设计细节不断的雕刻!

Как вы думаете, когда ваша технология была быстро улучшена после написания большего количества CRUD? Даже не думайте об этом, это абсолютно невозможно! Сколько бы ни было написано CRUD, он может удовлетворить вас только как инструмент для перемещения кирпичей, и скорость прослушивания кода меньшего логического конвейера, и возможности программирования, кроме начального от неопытного до опытного, других очень мало. .

Тогда что вы могли подумать, это способность к программированию? На самом деле больше возможностей программирования - это ваша архитектура сложных сцен и как обеспечить стабильную работу системы, когда у вас большое количество потокового воздействия на каждую технологию.Сколько изучено, сколько раскручено!

В конце концов, когда вы определите требование к продукту, начните думать程序数据结构的设计,核心功能的算法逻辑实现,整体服务的设计模式使用,系统架构的搭建方式,应用集群的部署结构, тогда ваши способности к программированию действительно улучшатся!

2. Цели

Цель этой главы в основном решитьпредыдущая главанас埋下的坑, что это за яма? На самом деле это яма про создание экземпляров объектов Bean с помощью конструкторов.

В предыдущей главе мы расширили функцию контейнера Bean и передали созданный объект контейнеру для унифицированной обработки, но в коде нашего созданного объекта мы не учитывали, содержит ли класс объекта конструктор, то есть , если мы перейдем к экземпляру Если вы конвертируете объект с помощью конструктора, будет выброшено исключение.

Как проверить? На самом деле достаточно добавить конструктор с информацией о входных параметрах в UserService следующим образом:

public class UserService {

    private String name;

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

    // ...
}

Ошибка заключается в следующем:

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)
	...

Основная причина этого явления заключается в том, чтоbeanDefinition.getBeanClass().newInstance();Метод инстанцирования не учитывает параметры конструктора, так что эта яма ждет вас здесь!Тогда наша цель очевидна, давайте залатаем эту дыру!

3. Дизайн

Технический дизайн заполнения этой дыры в основном рассматривает две части: первая — это то, где строковый процесс разумно передает информацию о входных параметрах конструктора в операцию создания экземпляра, а другая — как создать экземпляр объекта, содержащего конструктор.

图 4-1

  • Обратитесь к реализации исходного кода контейнера Spring Bean и добавьте его в BeanFactory.Object getBean(String name, Object... args)интерфейс, так что информация о входных параметрах конструктора может быть передана при получении Bean.
  • Еще одно основное содержание — как создать объект Bean с помощью конструктора? Есть два способа выбора, один основан на методе, который поставляется с самой Java.DeclaredConstructor, другой — использовать Cglib для динамического создания объектов Bean.Cglib реализован на основе фреймворка байт-кода ASM, поэтому вы также можете создавать объекты напрямую через сценарий работы ASM.

4. Реализация

1. Инженерное сооружение

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

Исходный код проекта:公众号「bugstack虫洞栈」,回复:Spring 专栏,获取完整源码

Отношения класса контейнера Spring Bean, как показано на рисунке 4-2.

图 4-2

эта глава“填坑”Основная цель состоит в том, чтобы добавить интерфейс стратегии создания экземпляров InstantiationStrategy в существующий проект и дополнить соответствующую информацию о входных параметрах getBean, чтобы можно было беспрепятственно передавать и создавать экземпляры входных параметров конструктора при внешнем вызове.

2. Добавлен интерфейс getBean

cn.bugstack.springframework.beans.factory.BeanFactory

public interface BeanFactory {

    Object getBean(String name) throws BeansException;

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

}
  • В BeanFactory мы перегрузили метод getBean, содержащий информацию о входных параметрах args, чтобы входные параметры можно было легко передать конструктору для создания экземпляра.

3. Определите созданный интерфейс политики

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

public interface InstantiationStrategy {

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

}
  • Добавьте необходимую информацию о входных параметрах в методе создания экземпляра интерфейса, в том числе: beanDefinition, beanName, ctor, args.
  • Среди них Constructor может быть вам немного незнаком. Это класс Constructor в пакете java.lang.reflect, который содержит некоторую необходимую информацию о классе. Целью этого параметра является получение соответствующего конструктора, который соответствует информации входного параметра. .
  • ARGS — это конкретная информация о доходах, и она будет использоваться в окончательном экземпляре.

4. Создание экземпляра JDK

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);
        }
    }

}
  • Сначала получите информацию о классе через beanDefinition, которая передается при определении компонента.
  • Затем оцените, является ли ctor пустым.Если он пустой, он создается без конструктора.В противном случае он должен быть создан с помощью конструктора.
  • Здесь мы сосредоточимся на создании экземпляра с помощью конструктора, метод создания экземпляраclazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);, передайте информацию о входных параметрах в newInstance для создания экземпляра.

5. Создание экземпляра Cglib

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);
    }

}
  • На самом деле Cglib тоже очень удобен для создания bean-компонентов с конструкторами.Здесь мы упрощаем обработку.Если вы читаете исходный код Spring, вы также увидите такие реализации, как CallbackFilter, но наш текущий метод не повлияет на создание.

6. Создайте вызов политики

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);
    }

}
  • Во-первых, в абстрактном классе AbstractAutowireCapableBeanFactory определяется класс атрибутов стратегии создания экземпляров для создания объектов.InstantiationStrategy instantiationStrategy, здесь мы выбираем класс реализации Cglib.
  • Следующий розыгрышcreateBeanInstanceВ этом методе вам нужно обратить внимание на то, сколько конструкторов представляет Constructor. Вы можете получить все свои конструкторы через beanClass.getDeclaredConstructors(), который является коллекцией.
  • Далее необходимо циклически сравнить набор конструктора и информацию о входных параметрах.argsСитуация сопоставления, здесь мы сравниваем способ, относительно проста, просто количественное сравнение, а фактическая Spring

Исходный код также должен сравнивать типы входных параметров, иначе будет выброшено исключение в случае одинакового количества различных типов входных параметров.

5. Тест

1. Подготовьтесь заранее

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();
    }
}
  • Единственное, что здесь добавлено в UserService — это конструктор с параметром имени, который нам удобен для проверки возможности инстанцирования такого объекта.

2. Тестовый случай

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();
}
  • В дополнение к трем этапам Bean Factory, Регистрация Bean-компонентов и Получение Bean-компонентов, этот модульный тест также добавляет получение и вызов дополнительных объектов. Основной тест здесь — проверить, правильно ли хранится одноэлементный объект в кеше.
  • Кроме того, отличие от процесса тестирования в предыдущей главе заключается в том, что мы передаем UserService.class в BeanDefinition вместо прямой операции new UserService(), как в предыдущей главе.

3. Результаты испытаний

查询用户信息:小傅哥

Process finished with exit code 0
  • Судя по результатам тестирования, самое большое изменение заключается в том, что объекты с конструкторами могут создавать экземпляры.
  • Вы можете попробовать две разные стратегии создания экземпляров.SimpleInstantiationStrategy,CglibSubclassingInstantiationStrategy

4. Операционные случаи

Здесь мы берем несколько разных способов создания экземпляров, помещаем их в модульное тестирование, удобное для вашего сравнения.

4.1 Нет конструктора

@Test
public void test_newInstance() throws IllegalAccessException, InstantiationException {
    UserService userService = UserService.class.newInstance();
    System.out.println(userService);
}
  • Этот способ создания экземпляра также используется непосредственно при реализации контейнера Spring Bean в предыдущей главе.

4.2 Проверка наличия экземпляра конструктора

@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);
}
  • Из простейшей операции, если необходимо создать экземпляр класса с конструктором, вам нужно использоватьgetDeclaredConstructorПолучите конструктор, а затем создайте его экземпляр, передав параметры.

4.3 Получить информацию о конструкторе

@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);
  • Фактически, основной момент в этом случае - получить все конструкторы в классе, что на самом деле является использованием этого метода.beanClass.getDeclaredConstructors()

4.4 Создание экземпляра Cglib

@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);
}
  • Этот кейс-демонстрация очень прост в использовании, но в нем много информации об использовании Cglib в контейнере Spring, и вы также можете узнать больше о расширенных знаниях Cglib.

6. Резюме

  • Основная цель этой главы — улучшить операцию создания экземпляров, расширить интерфейс стратегии создания экземпляров InstantiationStrategy и добавить два новых класса создания экземпляров. Название и реализация этой категории — это, по сути, урезанная версия фреймворка Spring, соответствующий код вы также можете найти в исходном коде Spring в процессе обучения.
  • Как мы видим из нашего постоянного совершенствования и растущих требований, когда ваша структура кода разработана разумно, очень легко и удобно расширять обязанности классов различных атрибутов, не вызывая путаницы в структуре классов из-за увеличения требований. Следовательно, в процессе реализации наших собственных бизнес-требований мы также должны стараться учитывать хорошую масштабируемость и ответственность за разделение класса.
  • Практика — это самый быстрый способ научиться, не позволяйте своим глазам чувствовать, что вы можете это видеть, но это будет бесполезно, если вы начнете. Я также надеюсь, что читатели, которым это нужно, смогут сделать это сами и интегрировать свои идеи в код, который можно реализовать на местах, чтобы увидеть, соответствуют ли ваши мысли и действия вашим действиям.

Семь, рекомендация серии