Прицельный обзор: Как создаются бины в Spring Framework

| Комментарии

Основной интерфейс - BeanFactory. Основной метод - getBean. Его расширяют такие интерфейсы как:

  • ListableBeanFactory для доступа к спискам экземпляров созданных bean’ов
  • HierarchicalBeanFactory для организации иерархичных фабрик бинов
  • ConfigurableBeanFactory определяет методы, конфигурирующие бины

Этих интерфейсов бесчисленное множество. По отдельности они практически не используются, а служат для создания больших многофункциональных объектов фабрик. Для инициации загрузки необходим конфигурационный файл. За обработку источников конфигурации бинов отвечают BeanDefinition объекты:

  • PropertiesBeanDefinitionReader для конфигурирования бинов в property файлах
  • XmlBeanDefinitionReader для конфигурирования бинов с помощью xml

Естественно, наиболее популярен XML BeanDefinition, но очевидно, что скоро и от XML откажутся, как от пережитка прошлого, в пользу Java синтаксиса. Но это будущее, а сейчас вершина FactoryBean иерархии - XmlBeanFactory. Этот объект определяет все специфичные для создания бинов с помощью xml конфигураций реализации, такие как XmlBeanDefinitionReader. Настройки бина, обявленного в конфиге, хранятся в BeanDefinition объекте. Все эти объекты хранятся в реестре бинов - BeanDefinitionRegistry.

Как же строятся бины?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public <T> T getBean(Class<T> requiredType) throws BeansException {
    Assert.notNull(requiredType, "Required type must not be null");
    String[] beanNames = getBeanNamesForType(requiredType);
    if (beanNames.length > 1) {
        ArrayList<String> autowireCandidates = new ArrayList<String>();
        for (String beanName : beanNames) {
            if (getBeanDefinition(beanName).isAutowireCandidate()) {
                autowireCandidates.add(beanName);
            }
        }
        if (autowireCandidates.size() > 0) {
            beanNames = autowireCandidates.toArray(new String[autowireCandidates.size()]);
        }
    }
    if (beanNames.length == 1) {
        return getBean(beanNames[0], requiredType);
    }
    else if (beanNames.length == 0 && getParentBeanFactory() != null) {
        return getParentBeanFactory().getBean(requiredType);
    }
    else {
        throw new NoSuchBeanDefinitionException(requiredType, "expected single bean but found " +
                beanNames.length + ": " + StringUtils.arrayToCommaDelimitedString(beanNames));
    }
}

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

Теперь, когда у нас есть имя бина и его тип, мы можем попытаться получить экземпляр бина. Метод создания бина достаточно большой (135 строк без сторонних используемых методов), поэтому покажу только кусочки. В первую очередь пытаемся получить объект, если это singleton и он был загружен ранее. Т.к. объект бина может быть фабрикой, то идет проверка на соответствие интерфейсу FactoryBean. Если это действительно фабрика, то вызывается ее фабричный метод.

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

1
2
3
if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
}

Таким образом предотвращаются циклические ссылки. Статус создания того или иного бина хранится в NamedThreadLocal переменной.

Далее идут иерархические хитросплетения, которые сводятся к одному, к библиотке CGLib. С помощью неё создаются бины в spring’e.

Comments