Эта статья была включена в Java-проект JavaGuide Github 95k+ Star. Адрес проекта JavaGuide:GitHub.com/snail Climb/….
автор:Miki-byte-1024 & Snailclimb
Каждый раз, когда задают вопрос о Spring Boot, интервьюер любит задавать этот вопрос: «Расскажите мне о принципе автопроводки Spring Boot?».
Я думаю, что мы можем ответить следующим образом:
- Что такое автопроводка SpringBoot?
- Как SpringBoot реализует автопроводку? Как добиться загрузки по требованию?
- Как внедрить Стартер?
Из-за недостатка места эта статья не является подробной, и друзья также могут напрямую использовать метод отладки, чтобы увидеть исходный код части автоматической сборки SpringBoot.
предисловие
Друзья, которые использовали Spring, должны бояться, что ими будут управлять XML-конфигурации. Даже если Spring позже представит конфигурацию на основе аннотаций, нам все равно придется использовать XML или Java для явной настройки, когда мы включим некоторые функции Spring или введем сторонние зависимости.
Например. Когда нет Spring Boot, мы пишем веб-сервис RestFul, и нам нужно сначала настроить следующее.
@Configuration
public class RESTConfiguration
{
@Bean
public View jsonTemplate() {
MappingJackson2JsonView view = new MappingJackson2JsonView();
view.setPrettyPrint(true);
return view;
}
@Bean
public ViewResolver viewResolver() {
return new BeanNameViewResolver();
}
}
spring-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context/ http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc/ http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.howtodoinjava.demo" />
<mvc:annotation-driven />
<!-- JSON Support -->
<bean name="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<bean name="jsonTemplate" class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
</beans>
Однако для проекта Spring Boot нам нужно только добавить соответствующие зависимости без настройки, запустив следующиеmain
метод.
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
И мы передаем глобальный файл конфигурации Spring Bootapplication.properties
илиapplication.yml
Вы можете настроить проект, например изменить номер порта, настроить свойства JPA и т. д.
Почему Spring Boot так кисло использовать?Это благодаря автоматической сборке.Можно сказать, что автопроводка является ядром Spring Boot, так что же такое автопроводка?
Что такое автопроводка SpringBoot?
Когда мы сейчас говорим об автомонтировании, обычно это ассоциируется со Spring Boot. Однако на самом деле Spring Framework уже реализовал эту функцию. Spring Boot только дополнительно оптимизирует его через SPI.
SpringBoot определяет набор спецификаций интерфейса, которые предусматривают, что: SpringBoot будет сканировать внешний эталонный jar-пакет при запуске.
META-INF/spring.factories
файла, загрузите информацию о типе, настроенную в файле, в контейнер Spring (это включает в себя механизм загрузки класса JVM и знания контейнера Spring) и выполните различные операции, определенные в классе. Для внешних jar-файлов вам нужно только следовать стандартам, определенным SpringBoot, для установки собственных функций в SpringBoot.
Без Spring Boot, если нам нужно ввести сторонние зависимости, требуется ручная настройка, что очень хлопотно. Однако в Spring Boot мы можем напрямую ввести стартер. Например, если вы хотите использовать Redis в своем проекте, вы можете напрямую внедрить соответствующий стартер в проект.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
После введения стартера мы можем использовать функции, предоставляемые сторонними компонентами, с несколькими аннотациями и некоторой простой настройкой.
На мой взгляд, автовайринг можно просто понимать как:Определенную функцию можно реализовать с помощью Spring Boot через аннотации или какую-то простую настройку.
Как SpringBoot реализует автопроводку?
Давайте сначала посмотрим на основные аннотации SpringBoot.SpringBootApplication
.
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
<1.>@SpringBootConfiguration
<2.>@ComponentScan
<3.>@EnableAutoConfiguration
public @interface SpringBootApplication {
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration //实际上它也是一个配置类
public @interface SpringBootConfiguration {
}
наверное можно@SpringBootApplication
рассматривается как@Configuration
,@EnableAutoConfiguration
,@ComponentScan
Сборник аннотаций. Согласно официальному сайту SpringBoot, эти три аннотации выполняют следующие функции:
-
@EnableAutoConfiguration
: включить механизм автоматической настройки SpringBoot. -
@Configuration
: позволяет зарегистрировать дополнительные компоненты в контексте или импортировать другие классы конфигурации -
@ComponentScan
: сканирование есть@Component
(@Service
,@Controller
) аннотированный bean-компонент, аннотация будет сканировать все классы в пакете, где класс запуска находится по умолчанию, и вы можете настроить, чтобы не сканировать некоторые bean-компоненты. Как показано на изображении ниже, контейнер будет исключатьTypeExcludeFilter
иAutoConfigurationExcludeFilter
.
@EnableAutoConfiguration
Это важная аннотация для реализации автоматической сборки, мы начнем с этой аннотации.
@EnableAutoConfiguration: основная аннотация для реализации автоматической сборки.
EnableAutoConfiguration
Простая аннотация, реализация основной функции автопроводки на самом деле выполняетсяAutoConfigurationImportSelector
своего рода.
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //作用:将main包下的所欲组件注册到容器中
@Import({AutoConfigurationImportSelector.class}) //加载自动装配类 xxxAutoconfiguration
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
Теперь сосредоточимся на анализеAutoConfigurationImportSelector
Что именно делает класс?
AutoConfigurationImportSelector: загружает автоматически связанные классы.
AutoConfigurationImportSelector
Система наследования классов выглядит следующим образом:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
}
public interface DeferredImportSelector extends ImportSelector {
}
public interface ImportSelector {
String[] selectImports(AnnotationMetadata var1);
}
Как можно видеть,AutoConfigurationImportSelector
класс реализуетImportSelector
интерфейс, реализующийselectImports
метод, который в основном используется дляПолучите полные имена классов всех подходящих классов, которые необходимо загрузить в контейнер IoC..
private static final String[] NO_IMPORTS = new String[0];
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// <1>.判断自动装配开关是否打开
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
//<2>.获取所有需要装配的bean
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
Здесь мы должны сосредоточиться наgetAutoConfigurationEntry()
метод, этот метод в основном отвечает за загрузку класса автоматической конфигурации.
Цепочка вызовов методов выглядит следующим образом:
Теперь мы объединяемgetAutoConfigurationEntry()
Исходный код для детального анализа:
private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();
AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
//<1>.
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
//<2>.
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//<3>.
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//<4>.
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
шаг 1:
Определите, включен ли переключатель автосборки. дефолтspring.boot.enableautoconfiguration=true
, доступны наapplication.properties
илиapplication.yml
установить в
Шаг 2:
для полученияEnableAutoConfiguration
в примечанияхexclude
иexcludeName
.
Шаг 3
Получите все классы конфигурации, которые должны быть автоматически подключены, прочитайтеMETA-INF/spring.factories
spring-boot/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
Как видно из рисунка ниже, конфигурационное содержимое этого файла было нами прочитано.XXXAutoConfiguration
Роль заключается в загрузке компонентов по запросу.
Это зависит не толькоMETA-INF/spring.factories
Читается, что все Spring Boot Starter подMETA-INF/spring.factories
будет прочитан.
Итак, вы можете ясно видеть, что создается Spring Boot Starter пула соединений с базой данных druid.META-INF/spring.factories
документ.
Если мы хотим сами создать Spring Boot Starter, этот шаг необходим.
Шаг 4:
В этот момент интервьюер может спросить вас:spring.factories
С таким количеством конфигураций вам нужно загружать их все каждый раз при запуске? ".
Очевидно, что это не реально. Мы отлаживаем обратно, и вы найдете,configurations
значение уменьшилось.
Поскольку этот шаг прошел проверку,@ConditionalOnXXX
Все условия выполнены, класс вступит в силу.
@Configuration
// 检查相关的类:RabbitTemplate 和 Channel是否存在
// 存在才会加载
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {
}
Заинтересованные детской обувью могут узнать больше об условных аннотациях, предоставляемых Spring Boot.
-
@ConditionalOnBean
: Когда в контейнере есть указанный компонент -
@ConditionalOnMissingBean
: Когда в контейнере не указан компонент -
@ConditionalOnSingleCandidate
: когда указанный компонент имеет только один в контейнере или хотя их несколько, но укажите предпочтительный компонент -
@ConditionalOnClass
: когда в пути к классам есть указанный класс -
@ConditionalOnMissingClass
: когда в пути к классам не указан класс -
@ConditionalOnProperty
: Имеет ли указанное свойство указанное значение. -
@ConditionalOnResource
: Есть ли указанное значение классов -
@ConditionalOnExpression
: На основе выражений SpEL в качестве условий суждения -
@ConditionalOnJava
: на основе версии Java в качестве условия суждения -
@ConditionalOnJndi
: разница в указанной позиции при наличии JNDI -
@ConditionalOnNotWebApplication
: при условии, что текущий проект не является веб-проектом -
@ConditionalOnWebApplication
: при условии, что текущий проект является веб-проектом.
Как реализовать Стартер
Если вы не просто говорите о поддельном дескрипторе, давайте сейчас воспользуемся стартером для реализации пользовательского пула потоков.
Первым шагом является созданиеthreadpool-spring-boot-starter
проект
Второй шаг — представить зависимости, связанные с Spring Boot.
Третий шаг, создайтеThreadPoolAutoConfiguration
Четвертый шаг, вthreadpool-spring-boot-starter
Создан под пакет ресурсов проектаMETA-INF/spring.factories
документ
Представлен последний новый проектthreadpool-spring-boot-starter
Тест пройден! ! !
Суммировать
Весенняя загрузка через@EnableAutoConfiguration
Включите автопроводку и, наконец, загрузите через SpringFactoriesLoader.META-INF/spring.factories
Класс автоконфигурации в реализует автосборку, а класс автоконфигурации фактически через@Conditional
Класс конфигурации, загружаемый по требованию, должен быть введен, чтобы он вступил в силу.spring-boot-starter-xxx
Зависимости запуска реализации пакета