公众号|松花皮蛋的黑板报|《Spring Boot编程思想》读书笔记
松花皮蛋的黑板报
  • 分享在京东工作的技术感悟,还有JAVA技术和业内最佳实践,大部分都是务实的、能看懂的、可复现的

扫一扫
关注公众号

《Spring Boot编程思想》读书笔记

博客首页文章列表 松花皮蛋me 2020-02-15 17:52


1、外部化配置


对于可扩展性应用,特别是中间件,它们的功能性组件是可配置化的,如认证信息、端口范围、线程池规模及连接时间等。反之配置行为是可以枚举的,缺少弹性的,就是内部化配置。


注:Spring Boot内建了17种”外部化配置”,并且是有顺序性的。


2、规约大于配置
3、SpringBoot主要特性

  • (1)、SpringApplication
  • (2)、自动装配
  • (3)、外部化配置
  • (4)、Spring Boot Actuator
  • (5)、嵌入式Web容器

4、元注解


一个能声明在其他注解上的注解,那么它就是元注解,比如@Component就是标准的元注解、@Documented能够作为任何注解的元注解。


5、Spring模式注解


Spring核心部分提供了几种内建模式注解,它们都是@Component派生的注解,如@Component、@Service、@Repository、@Controller、@Configuration。
注:可以利用ClassPathBeanDefinitionScanner和AnnotationTypeFilter相互配置,可以不依赖@Component。比如scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class))可以不依赖@Component达到识别@Service所标注类的目的。


注:@Component支持多层次派生,如@SpringBootConfiguration。


6、Spring组合注解


将这些关联的注解行为组合成单个自定义注解,比如@TransactionService、@SpringBootApplication。


注:并不是通过反射获取元注解信息的,而是抽象出AnnotationMetadata接口,通过ASM操作字节码,然后通过递归的方式保存多层次元注解信息。


注: 基于JAVA反射API实现的前提是,Class必须被ClasLoader加载,所以如果指定Java Package就没有必要使用反射了


注:所有的注解都实现了Annotation接口。


7、Spring注解属性抽象AnnotationAttributes


其中AnnotationAttributes直接扩展了LinkedHashMap。所以相同的KEY会被覆盖,也就是在多层次元注解中,如果碰到相同的方法名,则低层次注解属性覆盖较高层,比如@Component.value()和@Service.value()。


8、注解属性覆盖


AnnotationAttributes采用的是就近覆盖的设计原则。


9、注解属性别名


同层次注解使用别名时,需要两个属性方法相互@AliasFor,并且默认值必须相同。如果不同层次注解属性之间使用别名,需要显示指定类,比如@AliasFor(anotation=EnableAutoConfiguration.class)。同时它默认为单向别名关系,和同层次注解的有所区别。


注:多层次注解属性之间的@AliasFor关系只能由低层向较高层建立。


10、@Enable模块驱动


所谓的模块是指具备相同领域的功能组合集合,组合所形成的一个独立的单元,比如Web MVC模块、AspectJ代理模块、Caching模块等。它的引入简化了装配步骤,实现了按需装配,同时屏蔽组合集合装配的细节。


11、@Import注解


@Import注解则用于导入一个或者多个ConfigurationClass,将其他注册成Spring Bean。同时它还可以声明至少一个@Bean方法的类,以及ImportSelector或者ImportBeanDefinitionRegistrar的实现类。


12、基于注解驱动实现@Enable模块


利用@Import注解导入。


13、基于接口编程实现@Enable模块


实现ImportSelector或者ImportBeanDefinitionRegistrar接口。装载的实现原理是(AnnotationConfigBeanDefinitionParser#parse、ConfigurationClassParser#processImports)


14、WEB自动装配


通过ServletContent配置方法addServlet,动态地为其装配,随后,在ServletContainerInitializer#onStartup实现方法中加以实现。


注:有三种抽象实现,分别为AbstractContextLoaderInitializer、AbstractDispatcherServletInitializer、AbstractAnnotationConfigDispatcherServletInitializer


注:SpringServletContainerInitializer通过实现Servlet 3.0 SPI接口ServletContainerInitializer,与@HandlesTypes配合过滤出WebApplicationInitializer具体实现集合,随后顺序迭代地执行该集合元素,进而利用Servlet3.0配置API实现WEB自动装配的目的。


15、条件装配@Profile和@Conditional


对于同一个应用,在不同环境中所依赖的资源或者表现的行为可能存在差异,通常通过编译时差异化、运行时配置化达到目的。其中,在Spring中,当激活某个配置时,Spring上下文并不会同时注册激活的和对应相排斥的Bean,它是有选择性地注册的。实际上,条件装配均采用@Conditional实现,只不过@Profile偏向于”静态激活和配置”,@Conditional则更关注”运行时动态选择”。


注:应用在自动装配过程中,出于保险起见,可以考虑@ConditionalOnClass和@ConditionalOnMissingClass配合使用,避免版本升级时编译通过但是运行时异常。


16、@Profile注解驱动处理


常见的注解驱动Bean注册方式有:扫描Spring模式注解(@ComponentScan)、@Import导入(@Configuration Class)、@Bean方法定义、注册BeanClass(AnnotationConfigApplicationContext)。在这些注解元信息处理类均覆盖了@Profile的处理,首先判断Class注解元信息AnnotationMetadata是否包含@Profile属性元信息AnnotationAttributes,并且其属性值能够被Environment Profile接受。不过在最新的版本中,这些判断都抽象到了ConditionEvaluator#shouldSkip方法中。


17、Spring Boot自动装配


当Spring应用自动装配某些组件时,它需要一种综合性技术手段,重新深度整合Spring注解编程模型、@Enable模块驱动及条件装配等等Spring Framework原生特性,这种技术就是”Spring Boot自动装配”;我们可以通过@EnableAutoConfiguration或者@SpringBootApplication中的任意一项,激活自动装配特性。当然自动装配是尝试性的,它是尝试性地自动装配应用的组件,这些组件来自应用依赖的JAR。


注:@EnableAutoConfiguration除了利用AutoConfigurationImportSelector自动装配Class,它还将标注类所在的package添加到BasePackages中,为后续扫描组件提供BasePackags数据来源。


18、AutoConfigurationImportSelector读取自动装配Class的流程

  • (1)、通过SpringFactoriesLoader#loadFactoryNames方法获取所有/META-INF/spring.factories资源中@EnableAutoConfiguration所关联的自动装配Class集合。
  • (2)、读取当前配置类所标注的@EnableAutoConfiguration属性exclude和excludeName,并与spring.autoconfigure.exclude配置属性合并为自动装配Class排除集合。
  • (3)、检查自动装配Class排除集合是否合法。
  • (4)、排除候选自动装配Class集合中的排除名单。
  • (5)、再次过滤候选自动装配Class集合中Class不存在的成员。
  • (6)、触发自动装配的导入事件


注:AutoConfigurationImportSelector实现了DeferredImportSelector接口,可见其具备延迟性。


19、@EnableAutoConfiguration排序自动装配组件


代码实现:AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports
。大多数情况下,建议尽可能使用@AutoConfigureBefore或者@AutoConfigureAfter的name属性方法,不推荐使用value方法。


20、AutoConfigurationMetadataLoader


META-INF/spring-autoconfigure-metadata.properties可认为是自动装配Class预处理元信息配置的资源。当该资源文件存在自动装配Class的注解元信息配置时,自动配置Class无须ClassLoader加载,即可得到所需的元信息,减少了运行时的计算消耗。


21、自定义Spring Boot Starter


建议保持将spring-boot-starter等相关依赖声明为<optional>true</optional>的良好习惯。其目的在于说明自定义的Starter不应该传递spring-boot-starter的依赖。


22、ResourceLoader对象


通常情况下,Spring Framework的ConditionContext#getResourceLoader()方法所返回的ResouceLoader对象即当前Spring应用上下文实例。DefaultResourceLoader实际上是Spring Framework中唯一的ResourceLoader实现。


23、Spring 事件\监听机制


观察者模式通过java.util.Observable和java.util.Observer实现,前者可以认为是数据的发布者,后者为数据的接受者。Spring事件\监听机制属于事件\监听器模式,可视为观察者模式的扩展。观察者传播的数据更为抽象,事件\监听器模式所发布的内容有类型限制,必须为java.util.EventObject对象。


在Spring Boot中,事件是由SimpleApplicationEventMulticaster发布的,该接口主要有两个职责,一是关联ApplicationListener,二是广播ApplicationEvent。通过调用SimpleApplcationEventMulticaster#multicastEvent方法广播 ,根据ApplicationEvent具体类型查找匹配的ApplicationListener列表,然后依次同步或异步地调用ApplicationListener#onApplicationEvent(Application event)方法,实现ApplicationListener事件。


注:Spring内建事件包括ContentRefreshEvent、ContextStartedEvent、ContextStopEvent、ContextCloasedEvent、RequestHandledEvent。


注:AbstractApplicationEventMulticaster在底层实现采用了(ListenerRetriever)LinkedHashSet集合存储结构,并且通常ApplicationListerner实现类不会覆盖Object的hashCode和equals方法,所以相同对象的重复插入不会引起事件的重复监听。


注:SpringApplicationRunListener唯一的实现类为EventPublishingRunListener。


25、@EventListener注解驱动


@EventListener方法必须是Spring Bean中的public方法,并支持返回类型为非void的情况。当它监听一个或多个ApplicationEvent时,其参数最多有一个ApplicationEvent。事实上,无论是接口驱动,还是注解驱动,事件监听器的监听方法是公开且没有返回值的,同时也不会抛出异常。


实现原理:在AnnotationConfigUtils#registerAnnotationConfigProcessors方法中,处理对@EventListener注解的相关Bean的注册。


26、ExitCodeGenerator Bean退出码