Простое введение в проектирование архитектуры Spring

Java задняя часть Spring
Простое введение в проектирование архитектуры Spring

предисловие

Зачем вам Весна?Что такое Весна?

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

Эта статья представлена ​​от простого к глубокому. Вам не нужно паниковать. Я могу гарантировать, что если вы знаете программирование, вы сможете его понять.

Эта статья основана на Spring 5.2.8, и ее чтение займет около 20 минут.

кейс

Давайте сначала рассмотрим случай: у парня есть автомобиль Geely, и он обычно ездит на работу на автомобиле Geely.

Код:

public class GeelyCar {

    public void run(){
        System.out.println("geely running");
    }
}
public class Boy {
		// 依赖GeelyCar
    private final GeelyCar geelyCar = new GeelyCar();

    public void drive(){
        geelyCar.run();
    }
}

Однажды парень заработал деньги, купил еще один Hongqi и захотел водить новую машину.

Просто замените зависимость наHongQiCar

Код:

public class HongQiCar {

    public void run(){
        System.out.println("hongqi running");
    }
}
public class Boy {
    // 修改依赖为HongQiCar
    private final HongQiCar hongQiCar = new HongQiCar();

    public void drive(){
        hongQiCar.run();
    }
}

Я устал ездить на новой машине и хочу пересесть обратно на старую машину, в этот раз возникнет проблема: код менялся раз за разом.

Ясно, что этот случай нарушает нашиПринцип инверсии зависимостей (DIP): Программы должны зависеть не от реализации, а от абстракций.

Оптимизировано

Теперь оптимизируем код следующим образом:

img

Boyзависит отCarинтерфейс, в то время как предыдущийGeelyCarиHongQiCarзаCarреализация интерфейса

Код:

Определить интерфейс автомобиля

public interface Car {

    void run();
}

предыдущийGeelyCarиHongQiCarизменить наCarкласс реализации

public class GeelyCar implements Car {

    @Override
    public void run(){
        System.out.println("geely running");
    }
}

То же, что и HongQiCar

Человек теперь зависит отCarинтерфейс

public class Boy {
		// 依赖于接口
    private final Car car;
		
    public Person(Car car){
        this.car = car;
    }

    public void drive(){
        car.run();
    }
}

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

ограничение

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

Однажды парень не мог водить машину после пьянки, поэтому ему нужно было найти шофера. Шоферу все равно, какого парня он водит, и на какой машине он ездит, а парень вдруг становится абстракцией. В это время код нужно снова менять. Код шофера, зависящего от парня, может выглядеть нравится:

private final Boy boy = new YoungBoy(new HongQiCar());

По мере увеличения сложности системы поддерживать такие проблемы будет все труднее, так как же нам решить эту проблему?

считать

Во-первых, мы можем быть уверены: используйтеПринцип инверсии зависимостиНет никакой проблемы, это решает нашу проблему в определенной степени.

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

Может быть, мы можем подуматьобеспечить регрессНажмите: что у нас есть, что использует программа!

когда мы только достигаемHongQiCarиYoungBoyКогда шофер использует открытыйHongQiCarизYoungBoy!

когда мы только достигаемGeelyCarиOldBoy, шофер естественно перешел на открытуюGeelyCarизOldBoy!

А как реверсировать — большая проблема, которую решает Spring.

Введение в весну

Весна — универсальный магазинлегкийТяжеловесная среда разработки предназначена для решения сложных задач разработки приложений корпоративного уровня.Она обеспечивает всестороннюю поддержку инфраструктуры для разработки приложений Java, так что разработчикам Java больше не нужно заботиться о зависимостях между классами и они могут сосредоточиться на разработке приложения (crud). .

Spring предоставляет богатую функциональность для разработки на уровне предприятия, и нижний уровень этих функций опирается на две его основные функции: внедрение зависимостей (DI) и аспектно-ориентированное программирование (AOP).

Основные концепции Spring

IoC-контейнер

Полное название IoCInversion of Control , что означает инверсию управления, IoC также известен как внедрение зависимостей (DI), который представляет собой процесс внедрения объекта через зависимость: объект создается только конструкторами, фабричными методами или свойствами, установленными для него при создании экземпляра объекта. Определите его зависимости (т. е. другие объекты, из которых они состоят), а затем контейнер внедряет эти необходимые зависимости при создании компонента. Этот процесс, по сути, является обратным самому компоненту, контролирующему создание экземпляров или размещение своих зависимостей с помощью либо класса прямой сборки, либо механизма, такого как шаблон местоположения службы (отсюда и название «инверсия управления»).

Принцип инверсии зависимостей — это принцип проектирования IoC, а внедрение зависимостей — это способ реализации IoC.

контейнер

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

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

Bean

В Spring объекты, которые создаются контейнером IOC Spring -> управление сборкой -> составляют скелет программы, называются Beans. Компоненты — это один из многих объектов в приложении.

Вышеупомянутые три пункта связаны друг с другом: Spring — это контейнер IoC, который размещает bean-компоненты и обрабатывает зависимости между bean-компонентами посредством внедрения зависимостей.

AOP

Аспектно-ориентированное программирование является функциональным дополнением к объектно-ориентированному программированию (ООП), основным объектом ООП является класс, а АОП — аспект. Он играет очень важную роль в обработке журналов, управлении безопасностью и управлении транзакциями. АОП является важным компонентом среды Spring.Хотя контейнер IOC не зависит от АОП, АОП предоставляет очень мощные функции в дополнение к IOC.

АОП позволяет нам улучшать наши бизнес-функции, не изменяя исходный код: вырезая часть функции в указанном нами месте, например, выводя журналы между цепочками вызовов методов.

Преимущества весны

1. Spring упрощает разработку Java на уровне предприятия с помощью DI и AOP.

2. Низкоинтрузивный дизайн Spring снижает загрязнение кода.

3. Контейнер IoC Spring снижает сложность между бизнес-объектами и отделяет компоненты друг от друга.

4. Поддержка Spring AOP позволяет централизованно обрабатывать некоторые общие задачи, такие как безопасность, транзакции, ведение журнала и т. д., тем самым улучшая повторное использование.

5. Высокая открытость Spring не обязывает приложение полностью зависеть от Spring, разработчики могут свободно выбирать часть или весь фреймворк Spring.

6. Высокая расширяемость Spring позволяет разработчикам легко интегрировать свои собственные фреймворки в Spring.

7. Экология Spring чрезвычайно полна, объединяя различные отличные фреймворки, что позволяет разработчикам легко их использовать.

Мы можем жить без Java, но мы не можем жить без Spring~

Дооснастите корпус пружиной

Теперь мы знаем, что такое Spring, и теперь попробуем использовать Spring, чтобы преобразовать случай

Первоначальная структура не изменилась, просто вGeelyCarилиHongQiCarувеличивать@Componentаннотация,Boyдобавить при использовании@Autowiredаннотация

Стиль кода:

@Component
public class GeelyCar implements Car {

	@Override
	public void run() {
		System.out.println("geely car running");
	}
}

То же, что и HongQiCar

В Spring, когда класс помечен аннотацией @Component, это означает, что это Bean-компонент, которым может управлять IoC-контейнер.

@Component
public class Boy {
	
	// 使用Autowired注解表示car需要进行依赖注入
	@Autowired
	private Car car;

	public void driver(){
		car.run();
	}
}

То, что мы говорили ранее: что мы реализуем, что использует программа, здесь эквивалентно тому, какой класс мы идентифицируем.ComponentАннотация, какой класс будет Bean, Spring будет использовать его для внедренияBoyсвойстваCarсередина

Итак, когда мы даемGeelyCarлоготипComponentПри аннотации,BoyмашинаGeelyCar, когда мы даемHongQiCarлоготипComponentПри аннотации,BoyмашинаHongQiCar

Конечно, мы не можемGeelyCarиHongQiCarв то же времяComponentАннотация, потому что тогда Spring не знает, что использоватьCarИнжекторный - Весна тоже затрудняется с выбором (или один мальчик не может водить две машины?)

Использование Spring Bootstrap

// 告诉Spring从哪个包下扫描Bean,不写就是当前包路径@ComponentScan(basePackages = "com.my.spring.test.demo")public class Main {	public static void main(String[] args) {		// 将Main(配置信息)传入到ApplicationContext(IoC容器)中		ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);		// 从(IoC容器)中获取到我们的boy		Boy boy = (Boy) context.getBean("boy");		// 开车		boy.driver();	}}

Здесь вы можете интерпретировать знания, которые мы только что представили о Spring.

имеютComponentScanАннотация (информация о конфигурации)Mainкласс, перешел наAnnotationConfigApplicationContext(контейнер IoC) для инициализации, это эквивалентно: контейнер IoC создает экземпляры, управляет и собирает bean-компоненты, получая информацию о конфигурации.

Как выполнить впрыск зависимости внутри контейнера IOC, который также является центром этой статьи.

считать

У нас есть полное понимание основных функций Spring через случай преобразования и есть конкретный опыт предыдущих концепций, но мы все еще не знаем, как завершается внутреннее действие внедрения зависимостей Spring. наши существующие знания и понимание Spring, давайте сделаем смелые предположения (это очень важная способность)

На самом деле угадывание означает: если мы позволим реализовать это самим, как мы реализуем этот процесс?

Во-первых, нам нужно четко понимать, что нам нужно делать: сканировать классы в указанном пакете, создавать их экземпляры и комбинировать их в соответствии с зависимостями.

Разбивка по шагам:

Сканировать класс в указанном пакете -> если этот класс идентифицирует аннотацию компонента (является компонентом) -> сохранить информацию об этом классе

Создание экземпляра -> просмотр сохраненной информации о классе -> создание экземпляров этих классов посредством отражения

Объединение зависимостей -> анализ информации о классе -> оценка наличия полей в классе, которым требуется внедрение зависимостей -> внедрение полей

Реализация схемы

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

определить аннотации

Сначала нам нужно определить аннотации, которые нам нужно использовать:ComponentScan,Component,Autowired

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {

    String basePackages() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {

    String value() default "";
}
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface Autowired {}

Сканировать классы в указанном пакете

Сканирование всех классов в указанном пакете звучит как момент замешательства, но на самом деле это эквивалентно другой проблеме: как пройти по каталогу файлов?

Так что же следует использовать для хранения информации о классе? Давайте посмотрим на приведенный выше примерgetBeanметод, это как получить значение по ключу в Map? В Map есть много реализаций, но есть только одна потокобезопасная, то естьConcurrentHashMap(Не говорите мне о HashTable)

Определите карту для хранения информации о классе

private final Map<String, Class<?>> classMap = new ConcurrentHashMap<>(16);

Для конкретного процесса реализация кода также прилагается ниже:

Реализация кода, вы можете посмотреть его вместе с блок-схемой:

Информация о классе сканирования

private void scan(Class<?> configClass) {
  // 解析配置类,获取到扫描包路径
  String basePackages = this.getBasePackages(configClass);
  // 使用扫描包路径进行文件遍历操作
  this.doScan(basePackages);
}
private String getBasePackages(Class<?> configClass) {
  // 从ComponentScan注解中获取扫描包路径
  ComponentScan componentScan = configClass.getAnnotation(ComponentScan.class);
  return componentScan.basePackages();
}
private void doScan(String basePackages) {
  // 获取资源信息
  URI resource = this.getResource(basePackages);

  File dir = new File(resource.getPath());
  for (File file : dir.listFiles()) {
    if (file.isDirectory()) {
      // 递归扫描
      doScan(basePackages + "." + file.getName());
    }
    else {
      // com.my.spring.example + . + Boy.class -> com.my.spring.example.Boy
      String className = basePackages + "." + file.getName().replace(".class", "");
      // 将class存放到classMap中
      this.registerClass(className);
    }
  }
}
private void registerClass(String className){
  try {
    // 加载类信息
    Class<?> clazz = classLoader.loadClass(className);
    // 判断是否标识Component注解
    if(clazz.isAnnotationPresent(Component.class)){
      // 生成beanName com.my.spring.example.Boy -> boy
      String beanName = this.generateBeanName(clazz);
      // car: com.my.spring.example.Car
      classMap.put(beanName, clazz);
    }
  } catch (ClassNotFoundException ignore) {}
}

создавать экземпляр

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

Определите карту, которая хранит Bean

private final Map<String, Object> beanMap = new ConcurrentHashMap<>(16);

Конкретный процесс, реализация кода также приведены ниже:

Реализация кода, вы можете посмотреть его вместе с блок-схемой:

траверсclassMapсоздавать экземпляры бобов

public void instantiateBean() {
  for (String beanName : classMap.keySet()) {
    getBean(beanName);
  }
}
public Object getBean(String beanName){
  // 先从缓存中获取
  Object bean = beanMap.get(beanName);
  if(bean != null){
    return bean;
  }
  return this.createBean(beanName);
}
private Object createBean(String beanName){
  Class<?> clazz = classMap.get(beanName);
  try {
    // 创建bean
    Object bean = this.doCreateBean(clazz);
    // 将bean存到容器中
    beanMap.put(beanName, bean);
    return bean;
  } catch (IllegalAccessException e) {
    throw new RuntimeException(e);
  }
}
private Object doCreateBean(Class<?> clazz) throws IllegalAccessException {
  // 实例化bean
  Object bean = this.newInstance(clazz);
  // 填充字段,将字段设值
  this.populateBean(bean, clazz);
  return bean;
}
private Object newInstance(Class<?> clazz){  try {    // 这里只支持默认构造器    return clazz.getDeclaredConstructor().newInstance();  } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {    throw new RuntimeException(e);  }}
private void populateBean(Object bean, Class<?> clazz) throws IllegalAccessException {  // 解析class信息,判断类中是否有需要进行依赖注入的字段  final Field[] fields = clazz.getDeclaredFields();  for (Field field : fields) {    Autowired autowired = field.getAnnotation(Autowired.class);    if(autowired != null){      // 获取bean      Object value = this.resolveBean(field.getType());      field.setAccessible(true);      field.set(bean, value);    }  }}
private Object resolveBean(Class<?> clazz){
  // 先判断clazz是否为一个接口,是则判断classMap中是否存在子类
  if(clazz.isInterface()){
    // 暂时只支持classMap只有一个子类的情况
    for (Map.Entry<String, Class<?>> entry : classMap.entrySet()) {
      if (clazz.isAssignableFrom(entry.getValue())) {
        return getBean(entry.getValue());
      }
    }
    throw new RuntimeException("找不到可以进行依赖注入的bean");
  }else {
    return getBean(clazz);
  }
}
public Object getBean(Class<?> clazz){
  // 生成bean的名称
  String beanName = this.generateBeanName(clazz);
  // 此处对应最开始的getBean方法
  return this.getBean(beanName);
}

комбинация

Были написаны два основных метода, а затем, объединив их, я реализовал их в пользовательскихApplicationContextВ классе конструктор выглядит следующим образом:

public ApplicationContext(Class<?> configClass) {
  // 1.扫描配置信息中指定包下的类
  this.scan(configClass);
  // 2.实例化扫描到的类
  this.instantiateBean();
}

Диаграмма классов UML:

контрольная работа

Структура кода такая же, как и в случае, здесь мы показываем, может ли наш собственный Spring нормально работать.

Работает нормально, китайцы китайцам не врут

Исходный код будет дан в конце статьи

обзор

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

1. Внедрение конструктора не поддерживается, и, конечно, внедрение метода не поддерживается, что является функциональным недостатком.

2. Проблема загрузки информации о классе, мы используемclassLoader.loadClassспособом, хотя это позволяет избежать инициализации класса (не используйтеClass.forNameпуть), но по-прежнему неизбежно загружать метаинформацию класса в метапространство, что тратит нашу память, когда мы сканируем ненужные классы в пакете.

3. Циклическая зависимость между bean-компонентами не может быть разрешена.Например, объект A зависит от объекта B, а объект B зависит от объекта A. В это время, если мы снова посмотрим на логику кода, мы обнаружим, что он упадет в бесконечный цикл.

4. Масштабируемость очень плохая. Мы пишем все функции в одном классе. Когда мы хотим улучшить функции (например, 3 проблемы выше), нам нужно часто изменять этот класс, и этот класс будет становиться все более и более раздутым. ., не говоря уже о повторении новых функций, обслуживание будет головной болью.

Оптимизация

Первые три проблемы аналогичны функциональным проблемам, и функции могут быть изменены.

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

Итак, как нам улучшить масштабируемость нашего решения?Шесть принципов проектирования дают нам хорошее руководство.

В схеме,ApplicationContextДелать много вещей, в основном можно разделить на две части

1. Сканировать классы в указанном пакете

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

С идеей принципа единой ответственности: класс делает только одно, метод делает только одно.

мы кладемСканировать классы в указанном пакетеЭтим вопросом занимается отдельный процессор, поскольку конфигурация сканирования поступает из класса конфигурации, тогда мы называем его процессором класса конфигурации: ConfigurationCalssProcessor.

То же самое верно и для создания экземпляров bean-компонентов.Создание экземпляров bean-компонентов делится на две вещи: создание экземпляров и внедрение зависимостей.

Создание экземпляра бина эквивалентно процессу производства бина. Мы используем фабричный класс для обработки этого вопроса, который называется: BeanFactory. Поскольку он производит бин, ему нужно сырье (класс), поэтому мы помещаемclassMapиbeanMapздесь определены

Процесс внедрения зависимостей на самом деле имеет дело сAutowiredAnnotation, то она называется AutowiredAnnotationBeanProcessor.

Мы все еще знаем, что в Spring есть не только этот способ использования, но и способ xml, mvc, SpringBoot, поэтому мыApplicationContextАннотация, реализуйте только основной процесс, исходный метод аннотации передаетсяAnnotationApplicationContextвыполнить.

С помощью принципа инверсии зависимостей: программы должны зависеть от абстракций

В будущем информация о классе может поступать не только из информации о классе, но и из конфигурационных файлов, поэтому мы абстрагируем ConfigurationCalssProcessor

Способ внедрения зависимостей не обязательно должен использоватьсяAutowriedИдентификатор аннотации также может быть другим идентификатором аннотации, напримерResource, поэтому мы абстрагируем AutowiredAnnotationBeanProcessor

Также может быть много типов bean-компонентов, которые могут быть одноэлементными, многоэкземплярными или фабричными bean-компонентами, поэтому мы абстрагируем BeanFactory.

Теперь мы оптимизировали наше решение с помощью двух принципов проектирования, которые можно охарактеризовать как «перерожденные» по сравнению с предыдущими.

Дизайн Весны

На предыдущем шаге мы реализовали собственное решение и оптимизировали масштабируемость, основываясь на некоторых предположениях Теперь давайте взглянем на реальный дизайн Spring.

Итак, каковы «роли» в Spring?

1. Bean: Spring как IoC-контейнер, самое главное, конечно же, Bean.

2. BeanFactory: Фабрика, которая производит бобы и управляет ими.

3. BeanDefinition: определение Bean, который является классом в нашей схеме, Spring инкапсулирует его.

4. BeanDefinitionRegistry: Подобно отношениям между Bean и BeanFactory, BeanDefinitionRegistry используется для управления BeanDefinition.

5. BeanDefinitionRegistryPostProcessor: Процессор для разбора классов конфигурации, аналогичный ClassProcessor в нашей схеме

6. BeanFactoryPostProcessor: родительский класс BeanDefinitionRegistryPostProcessor, чтобы мы могли выполнять постобработку после разбора класса конфигурации.

7. BeanPostProcessor: постпроцессор Bean, который используется для выполнения некоторой обработки в процессе создания bean-компонента, например внедрения зависимостей, аналогично нашему AutowiredAnnotationBeanProcessor.

8. ApplicationContext: если вышеперечисленные роли — это все рабочие, которые производят bean-компоненты на фабрике, то ApplicationContext — это фасад нашего Spring.ApplicationContext и BeanFactory находятся в комбинированных отношениях, поэтому он полностью расширяет функции BeanFactory и строится на его основе. Добавлены дополнительные корпоративные функции, такие как хорошо известный ApplicationListener (прослушиватель событий).

Вышеупомянутые сходства на самом деле ставят телегу впереди лошади, потому что на самом деле реализация в нашем решении должна быть похожа на реализацию в Spring, это просто для того, чтобы все понимали лучше.

После того, как мы испытали дизайн и оптимизацию наших собственных решений, эти роли на самом деле очень легко понять.

Далее, давайте подробно рассмотрим один за другим

BeanFactory

BeanFactory — это интерфейс верхнего уровня в Spring, который определяет способ получения bean-компонентов. В Spring есть еще один интерфейс под названием SingletonBeanRegistry, который определяет способ работы с singleton bean-компонентами. Здесь я представлю их вместе. Потому что они примерно одинаковы. , в аннотации SingletonBeanRegistry также написано, что его можно реализовать вместе с интерфейсом BeanFactory для облегчения унифицированного управления.

BeanFactory

1. ListableBeanFactory: интерфейс, который определяет методы, связанные с получением списка Bean/BeanDefinition, такие какgetBeansOfType(Class type)

2. AutowireCapableBeanFactory: интерфейс, определяющий методы, связанные с жизненным циклом Bean-компонента, такие как создание bean-компонента, внедрение зависимостей, инициализация.

3. AbstractBeanFactory: абстрактный класс, который в основном реализует все методы, связанные с операциями Bean, и определяет абстрактные методы, связанные с жизненным циклом Bean.

4. AbstractAutowireCapableBeanFactory: абстрактный класс, который наследует AbstractBeanFactory и реализует содержимое, связанное с жизненным циклом Bean.Хотя это абстрактный класс, он не имеет абстрактных методов.

5. DefaultListableBeanFactory: наследует и реализует все вышеперечисленные классы и интерфейсы, является нижней BeanFactory в Spring и реализует сам интерфейс ListableBeanFactory.

6, ApplicationContext: также является интерфейсом, о нем мы расскажем ниже.

SingletonBeanRegistry

1. DefaultSingletonBeanRegistry: определяет кеш-пул bean-компонентов, аналогичный нашему BeanMap, и реализует операции, связанные с синглтонами, такие какgetSingleton(кэш L3, часто задаваемый в интервью, находится здесь)

2. FactoryBeanRegistrySupport: обеспечивает поддержку FactoryBean, например получение компонентов из FactoryBean.

BeanDefinition

BeanDefinition на самом деле является интерфейсом (неожиданно), который определяет множество методов операций, связанных с информацией о классе, который удобно использовать непосредственно при создании bean-компонентов, таких какgetBeanClassName

Его примерная структура выглядит следующим образом (вот пример подкласса RootBeanDefinition):

Различные атрибуты в нем должны быть знакомы каждому.

Точно так же у него есть много классов реализации:

1. AnnotatedGenericBeanDefinition: при анализе класса конфигурации и анализе класса, представленного аннотацией Import, он будет использоваться для инкапсуляции.

2. ScannedGenericBeanDefinition: инкапсулирует информацию о классе, полученную при сканировании пакета с помощью @ComponentScan.

3. ConfigurationClassBeanDefinition: инкапсулирует информацию о классе, полученную с помощью аннотации @Bean.

4. RootBeanDefinition: родительский класс ConfigurationClassBeanDefinition, который обычно используется внутри Spring для преобразования других BeanDefinition в этот класс.

BeanDefinitionRegistry

Определите операции, связанные с Beandefiniton, такие какregisterBeanDefinition,getBeanDefinition, в BeanFactory классом реализации является DefaultListableBeanFactory.

BeanDefinitionRegistryPostProcessor

Междометие: Говоря об этом, вы обнаружили, что именование Spring чрезвычайно стандартизировано?Команда Spring однажды сказала, что имена классов в Spring подтверждаются после многократного обсуждения, что действительно достойно названия, так что это действительно очень удобно нужно взглянуть на исходный код Spring.Вы можете догадаться, что они делают, взглянув на имя класса и имя метода.

Этот интерфейс определяет только одну функцию: обработку BeanDefinitonRegistry, то есть синтаксический анализ в классе конфигурации.Import,Component,ComponentScanПодождите, пока аннотации будут обработаны соответствующим образом, а затем зарегистрируйте эти классы как соответствующиеBeanDefinition

Внутри Spring есть только одна реализация: ConfigurationClassPostProcessor.

BeanFactoryPostProcessor

Так называемый постпроцессор BeanFactory, который определяет логику обработки, которая может быть вызвана после разбора класса конфигурации, похож на слот, Если мы хотим что-то сделать после разбора класса конфигурации, мы можем реализовать этот интерфейс.

Внутри Spring его реализует только ConfigurationClassPostProcessor: для специальной обработки плюсConfigurationАннотированный класс

Здесь есть небольшая проблема в поле, если вы знаете следующий код:

@Configuraiton
public class MyConfiguration{
  @Bean
  public Car car(){
      return new Car(wheel());
  }
  @Bean
  public Wheel wheel(){
      return new Wheel();
  }
}

В: Сколько раз объект Wheel становится новым при запуске Spring? Зачем?

BeanPostProcessor

Перевод Jianghu: постпроцессор Бина

Постпроцессор проходит через весь жизненный цикл бина.Во время создания бина он вызывается в общей сложности 9 раз.Что касается 9 раз, мы рассмотрим это в следующий раз.Далее описывается его класс реализации и функция.

1. AutowiredAnnotationBeanPostProcessor: используется для вывода конструктора для создания экземпляра и для обработки аннотаций Autowired и Value.

2. CommonAnnotationBeanPostProcessor: обрабатывать аннотации в спецификации Java, такие как Resource, PostConstruct.

3. ApplicationListenerDetector: используется после инициализации компонента, добавляя компонент, реализующий интерфейс ApplicationListener, в список прослушивателей событий.

4. ApplicationContextAwareProcessor: используется для обратного вызова компонентов, реализующих интерфейс Aware.

5. ImportAwareBeanPostProcessor: используется для обратного вызова Bean-компонента, реализующего интерфейс ImportAware.

ApplicationContext

Будучи ядром Spring, ApplicationContext изолирует BeanFactory в режиме фасада, определяет структуру процесса запуска Spring в режиме метода шаблона и вызывает различные процессоры в режиме стратегии... Это действительно сложно и изысканно!

Его класс реализации выглядит следующим образом:

1. ConfigurableApplicationContext: интерфейс, который определяет операции, связанные с конфигурацией и жизненным циклом, такие как обновление

2. AbstractApplicationContext: абстрактный класс, который реализует метод обновления. Метод обновления является ядром ядра Spring. Можно сказать, что весь Spring находится в обновлении. Все подклассы запускаются через метод обновления. После вызова этого метода , все одноэлементные

3. AnnotationConfigApplicationContext: используйте соответствующие считыватели и сканеры аннотаций при запуске, зарегистрируйте необходимые процессоры в контейнере Spring, а затем вызовите метод обновления основным процессом.

4. AnnotationConfigWebApplicationContext: Реализуйте метод loadBeanDefinitions для вызова в процессе обновления для загрузки BeanDefintion.

5. ClassPathXmlApplicationContext: то же, что и выше.

Как видно из подкласса, разница между подклассами заключается в том, как загружать BeanDefiniton, AnnotationConfigApplicationContext загружается через процессор класса конфигурации (ConfigurationClassPostProcessor), в то время как AnnotationConfigWebApplicationContext и ClassPathXmlApplicationContext сами реализуют метод loadBeanDefinitions, а остальные процессы являются в точности такой же

Весенний процесс

Выше у нас были четкие основные роли и роли в Spring, теперь мы пытаемся их объединить, чтобы построить процесс запуска Spring

Также возьмите в качестве примера наш часто используемый AnnotationConfigApplicationContext.

На рисунке показана только часть общего процесса в Spring, и мы расширим детали в следующих главах.

резюме

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

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

Эта статья не только об архитектурном дизайне Spring, но и, как мы надеемся, станет для нас руководством по обзору общего содержания Spring в будущем.

Наконец, после прочтения статьи я считаю, что легко ответить на следующие часто задаваемые вопросы во время интервью.

1. Что такое BeanDefinition?

2. Какова связь между BeanFactory и ApplicationContext?

3. Классификация и роль постпроцессоров?

4. Каков основной процесс Spring?

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

Я Ао Бин,Чем больше вы знаете, тем больше вы не знаете, спасибо за ваши таланты:как,собиратьиКомментарий, увидимся в следующий раз!