Внедрение зависимостей в Spring. Погружение
Spring-фреймворк содержит огромное количество разных технологий, но в первую очередь он знаменит своим механизмом внедрения зависимостей (Dependency Injection, DI). Одним из самых популярных способов является внедрение с помощью аннотации
Большинство разработчиков знают, что за обработку аннотации
Далее мы попробуем разобраться, как происходит внедрение зависимостей. Для этого создадим простое Spring boot-приложение из 4-х классов.
// Бин, который будем внедрять через @Autowired над конструктором @Component public class DependencyForConstructorInjection {} // Бин, который будем внедрять через @Autowired над полем @Component public class DependencyForFieldInjection {} // Бин, в который будем внедрять зависимости @Component public class DependencyOwner { @Autowired private DependencyForFieldInjection dependencyForFieldInjection; private final DependencyForConstructorInjection dependencyForConstructorInjection; @Autowired public DependencyOwner(DependencyForConstructorInjection dep) { this.dependencyForConstructorInjection = dep; } @Override public String toString() { return "DependencyOwner{\n" + "\tdependencyForFieldInjection=" + dependencyForFieldInjection + ", \n\tdependencyForConstructorInjection=" + dependencyForConstructorInjection + "\n}"; } } // Запускающий класс, точка входа в приложение @SpringBootApplication public class AutowiredDemoApplication { public static void main(String[] args) { ConfigurableApplicationContext ctx = SpringApplication.run(AutowiredDemoApplication.class, args); DependencyOwner dependencyOwner = ctx.getBean(DependencyOwner.class); System.out.println(dependencyOwner); } }
Наше приложение содержит три бина, два из которых являются зависимостями третьего. Причем
DependencyOwner{ dependencyForFieldInjection=ru.otus.autowireddemo.components.DependencyForFieldInjection@6b5894c8, dependencyForConstructorInjection=ru.otus.autowireddemo.components.DependencyForConstructorInjection@1433046b }
Т. е. все бины успешно созданы и связаны друг с другом, как и ожидалось.
Можно приступать к исследованию
Для начала заглянем внутрь аннотации
Note that actual injection is performed through a BeanPostProcessor which in turn means that you cannot use @Autowired to inject references into BeanPostProcessor or BeanFactoryPostProcessor types. Please consult the javadoc for the AutowiredAnnotationBeanPostProcessor class (which, by default, checks for the presence of this annotation)
Внедрение действительно происходит с помощью
BeanPostProcessor implementation that autowires annotated fields, setter methods, and arbitrary config methods. Such members to be injected are detected through annotations: by default, Spring's @Autowired @Value annotations
Вроде то, что нужно. Обрабатывает аннотации
@Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } return pvs; }
В том, что мы угадали, несложно убедиться экспериментально. Поставим точку останова на строку
ОК. С этим все ясно, а что с конструкторами?
Среди содержимого
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)
Это значит, что его использует кто-то извне. Поставим точку останова с тем же условием, что и ранее, на последнюю строку метода, запустим приложение под отладчиком, дождемся, когда выполнение программы остановится на нашей точке, и сделаем шаг вперед. Мы оказались в методе
protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName) throws BeansException { if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName); if (ctors != null) { return ctors; } } } } return null; }
По сути,
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); }
Внутренности
protected BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) { return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs); }
Все вместе
Если дальше опускаться по стеку вызовов, можно составить полную картину происходящего.
- При создании контекста вызывается метод
refresh классаAbstractApplicationContext . - Внутри происходит вызов
this.finishBeanFactoryInitialization(beanFactory); . - Который, в свою очередь, вызывает
beanFactory.preInstantiateSingletons(); . (beanFactory =DefaultListableBeanFactory ) DefaultListableBeanFactory вызывает цепочку методов предка (AbstractAutowireCapableBeanFactory ):getBean -> doGetBean -> getSingleton -> createBean -> doCreateBean -> createBeanInstance .- Что делает
createBeanInstance , и что будет после него, мы уже знаем).
Итоги
Мы узнали, как происходит внедрение зависимостей для вариантов внедрения через конструктор и через поле/сеттер. Нужно отметить, что все бины в приложении были синглтонами. Для остальных скоупов местами процесс будет отличаться. Как минимум, будут работать другие участки кода). Если вы хотите продолжить изучение жизненного цикла бинов самостоятельно, рекомендуем обратить внимание на метод