每次问到 Spring Boot, 面试官非常喜欢问这个问题:“讲述一下 SpringBoot 自动装配原理?”。 我觉得我们可以从以下几个方面回答:
篇幅问题,这篇文章并没有深入,小伙伴们也可以直接使用 debug 的方式去看看 SpringBoot 自动装配部分的源代码。 前言使用过 Spring 的小伙伴,一定有被 XML 配置统治的恐惧。即使 Spring 后面引入了基于注解的配置,我们在开启某些 Spring 特性或者引入第三方依赖的时候,还是需要用 XML 或 Java 进行显式配置。 举个例子。没有 Spring Boot 的时候,我们写一个 RestFul Web 服务,还首先需要进行如下配置。 @Configurationpublic class RESTConfiguration{ @Bean public View jsonTemplate() { MappingJackson2JsonView view = new MappingJackson2JsonView(); view.setPrettyPrint(true); return view; } @Bean public ViewResolver viewResolver() { return new BeanNameViewResolver(); } }
<beans xmlns="http://www./schema/beans" xmlns:xsi="http://www./2001/XMLSchema-instance" xmlns:context="http://www./schema/context" xmlns:mvc="http://www./schema/mvc" xsi:schemaLocation="http://www./schema/beans http://www./schema/beans/spring-beans.xsd http://www./schema/context/ http://www./schema/context/spring-context.xsd http://www./schema/mvc/ http://www./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 项目,我们只需要添加相关依赖,无需配置,通过启动下面的 @SpringBootApplicationpublic class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } 并且,我们通过 Spring Boot 的全局配置文件 为什么 Spring Boot 使用起来这么酸爽呢? 这得益于其自动装配。自动装配可以说是 Spring Boot 的核心,那究竟什么是自动装配呢? 什么是 SpringBoot 自动装配?我们现在提到自动装配的时候,一般会和 Spring Boot 联系在一起。但是,实际上 Spring Framework 早就实现了这个功能。Spring Boot 只是在其基础上,通过 SPI 的方式,做了进一步优化。
没有 Spring Boot 的情况下,如果我们需要引入第三方依赖,需要手动配置,非常麻烦。但是,Spring Boot 中,我们直接引入一个 starter 即可。比如你想要在项目中使用 redis 的话,直接在项目中引入对应的 starter 即可。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency> 引入 starter 之后,我们通过少量注解和一些简单的配置就能使用第三方组件提供的功能了。 在我看来,自动装配可以简单理解为:通过注解或者一些简单的配置就能在 Spring Boot 的帮助下实现某块功能。 SpringBoot 是如何实现自动装配的?我们先看一下 SpringBoot 的核心注解 @Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited<1.>@SpringBootConfiguration<2.>@ComponentScan<3.>@EnableAutoConfigurationpublic @interface SpringBootApplication { }@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Configuration //实际上它也是一个配置类public @interface SpringBootConfiguration { } 大概可以把
@EnableAutoConfiguration:实现自动装配的核心注解
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage //作用:将main包下的所欲组件注册到容器中@Import({AutoConfigurationImportSelector.class}) //加载自动装配类 xxxAutoconfigurationpublic @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; } 我们现在重点分析下 AutoConfigurationImportSelector:加载自动装配类
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {}public interface DeferredImportSelector extends ImportSelector { }public interface ImportSelector { String[] selectImports(AnnotationMetadata var1); } 可以看出, 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()); } } 这里我们需要重点关注一下 该方法调用链如下: 现在我们结合 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 步: 判断自动装配开关是否打开。默认 第 2 步 : 用于获取 第 3 步 获取需要自动装配的所有配置类,读取 spring-boot/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories 从下图可以看到这个文件的配置内容都被我们读取到了。 不光是这个依赖下的 所以,你可以清楚滴看到, druid 数据库连接池的 Spring Boot Starter 就创建了 如果,我们自己要创建一个 Spring Boot Starter,这一步是必不可少的。 第 4 步 : 到这里可能面试官会问你:“ 很明显,这是不现实的。我们 debug 到后面你会发现, 因为,这一步有经历了一遍筛选, @Configuration// 检查相关的类:RabbitTemplate 和 Channel是否存在// 存在才会加载@ConditionalOnClass({ RabbitTemplate.class, Channel.class })@EnableConfigurationProperties(RabbitProperties.class)@Import(RabbitAnnotationDrivenConfiguration.class) public class RabbitAutoConfiguration { } 有兴趣的童鞋可以详细了解下 Spring Boot 提供的条件注解
如何实现一个 Starter光说不练假把式,现在就来撸一个 starter,实现自定义线程池 第一步,创建 第二步,引入 Spring Boot 相关依赖 第三步,创建 第四步,在 最后新建工程引入 测试通过!!! 总结Spring Boot 通过 |
|
来自: Laterede0fo33i > 《文件夹1》