SpringBoot 笔记

本文探讨了微服务的概念及优势,对比了传统Monolith架构,介绍了微服务如何提高服务交付效率。深入分析了Spring IoC容器的工作原理,包括依赖注入的两种方式:DI和DL。同时,详细阐述了Spring IoC容器的两个主要阶段:收集和注册bean定义,以及分析和组装bean依赖。最后,对比了基于XML和JavaConfig的配置方式,列举了高曝光率的Spring Annotation。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 微服务(Microservice) 是什么?为什么会出现微服务?

SOA — Service-Oriented Architecture

以前的服务实现和实施思路是:将很多功能,从开发到交付,都打包成一个很大的服务单元(一般称为 Monolith),而微服务的实现和实施思路则 更强调功能趋向单一服务单元小型化和微型化
如果用"茶壶煮饺子" 来打比方的话,原来是在一个茶壶里煮很多个饺子,现在(微服务化之后),则基本上是在一个茶壶煮一个饺子,这些饺子就是服务功能,茶壶则是将这些服务功能打包交付的单元。
微服务倡导,尽量将功能进行拆分,将服务粒度做小,使之可以独立承担对外服务的职责,沿着这个思路开发和交付的软件服务实体就叫做 “微服务”。

火车模型,交付的服务就像一辆火车,而这个服务相关的所有功能对应的项目成果,就是要装上火车车厢的一件件货物,交付的列车只有等到所有项目都开发测试完成后才可以装车出发,完成整个服务的交付。
很显然,只要有一个车厢没有准备好货物(即功能项目未开发测试完成),火车就不能发车,服务就不能交付。这大大降低了服务的交付效率。如果每个功能项目可以各自独立交付,那么就不需要都等同一辆火车。各自出发就可以了。

2 微服务的优点
  1. 独立 独立 独立
    现在大家基本上弱化了 Java EE 的 Web容器 早期采用的 “一个Web容器部署多个WAR包"的做法,转而使用"一个Web容器只部署一个WAR包”,这些 Web容器内部,大多通过类加载器(ClassLoader) 以及线程来实现一定程度上的依赖和功能隔离,但这些机制从基因上决定了这些做法不是最好的隔离手段。而 进程(Process) 拥有天然的隔离特性,所以一个 WAR包只部署运行在一个 Web容器进程中才是最好的隔离方式
  2. 多语言生态
    为了让服务的访问者可以用统一的接口访问所有这些,用不同语言开发和交互的微服务,应该尽量统一使用微服务的服务接口和协议
3 Spring IoC

IoC( Inversion Of Control ) 控制反转,DI( Dependency Injection) 依赖注入,DL( Dependency Lookup) 依赖查找

IoC 其实有两种方式,一种是 DI,一种是 DL,前者是当前软件实体,被动接受其依赖的其他组件被 IoC容器注入,而后者则是当前软件实体主动去某个服务注册地查找其依赖的那些服务。

通常提到的 Spring IoC,实际上是指 Spring框架提供的 IoC容器实现(IoC Container),使用 Spring IoC容器的一个典型代码片段就是:

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new FileSystemXmlApplication-Context("...");
        // ...
        MockService service = context.getBean(MockService.class);
        service.doSomething();
    }
}

任何一个使用 Spring框架构建的独立的 Java应用(Standalone Java Application),通常都会存在一行类似于 ‘context,getBean(…)’ 的代码。

实际上,这行代码做的就是 DL的工作,而构建的任何一种 IoC容器背后(比如 BeanFactory 或者 ApplicationContext) 发生的事情,则更多是 DI的过程(也可能有部分 DL的逻辑用语对接遗留系统)

Spring 的 IoC 容器中发生的事情其实也很简单,总结下来即两个阶段:

  • 采摘和收集“咖啡豆”(bean)
  • 研磨和烹饪咖啡

Spring 的 IoC容器的 依赖注入工作可以分为两个阶段:
1) 收集和注册
第一个阶段可以认为是构建和收集 bean 定义的阶段,在这个阶段中,我们可以通过 XML 或者 Java 代码的方式定义一些 bean,然后通过手动组装或者让容器基于某些机制自动扫描的形式,将这些 bean 定义收集到 IoC 容器中
假设我们以 XML 配置的形式来收集并注册单一 bean,一般形式如下:

如果嫌逐个收集 bean 定义麻烦,想批量地收集并注册到 IoC 容器中,我们也可以通过 XML Schema 形式的配置进行批量扫描并采集和注册

<context:component-scan base-package=“com.keevol”>

注意基于 JavaConfig 形式的收集和注册,不管是单一还是批量,后面我们都会单独提及
2) 分析和组装

当第一阶段工作完成后,我们可以先暂且认为 IoC 容器中充斥着一个个独立的 bean,它们之间没有任何关系

但实际上,它们之间是有依赖关系的,所以,IoC 容器在第二阶段要干的事情就是分析这些已经在 IoC 容器之中的 bean,然后根据它们之间的依赖关系先后组装它们

如果 IoC 容器发现某个 bean 依赖另一个 bean,它就会将这另一个 bean 注入给依赖它的那个 bean,直到所有 bean 的依赖都注入完成,所有 bean 都“整装待发”,整个 IoC 容器的工作即算完成

至于分析和组装的依据,Spring 框架最早是通过 XML 配置文件的形式来描述 bean 与 bean 之间的关系的,随着 Java 业界研发技术和理念的转变,基于 Java 代码和 Annotation 元信息的描述方式也日渐兴盛(比如 @Autowired 和 @Inject),但不管使用哪种方式,都只是为了简化绑定逻辑描述的各种“表象”,最终都是为本阶段的最终目的服务

很多 Java 开发者一定认为 Spring 的 XML 配置文件是一种配置(Configuration),但本质上,这些配置文件更应该是一种代码形式,XML 在这里其实可以看作一种 DSL(领域专用语言),它用来表述的是 bean 与 bean 之间的依赖绑定关系,如果没有 IoC 容器就要自己写代码新建(new)对象并配置(set)依赖

5 Spring JavaConfig 和 常见 Annotation

Java 5 的推出,加上当年基于纯 Java Annotation 的依赖注入框架 Guice 的出现,使得 Spring框架及其社区也顺应民意,推出并持续完善了基于 Java代码 和 Annotation元信息的 依赖关系绑定描述方式,即 JavaConfig 项目

基于 JavaConfig 方式的 依赖关系绑定描述 ,基本上映射了最早的 基于 XML的 配置方式:
1) 表达形式层面
基于 XML的 配置方式是这样的:

<?xml version="1.0" encoding="UTF-8"?>
<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"
    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">
   
    <!-- bean定义 -->
</beans>

而基于 JavaConfig 的配置方式是 这样的:

@Configurationpublic 
class MockConfiguration{
    // bean定义
}

任何一个标注了 @Configuration 的 Java类定义 都是一个 JavaConfig配置类

2) 注册 bean定义层面
基于 XML的配置形式是这样的:

而基于 JavaConfig 的配置形式的这样的:

@Configuration
public class MockConfiguration {
   @Bean
   public MockService mockService() {
      return new MockServiceImpl();
   }
}

任何一个标注了 @Bean的方法,其返回值将作为一个 bean定义 注册到 Spring的 IoC容器,方法名将默认成该 bean 定义的 id

3) 表达依赖注入关系层面
为了表达 bean 与 bean之间的 依赖关系,在 XML 形式中一般是这样的:

<bean id="mockService" class="..MockServiceImpl">
   <property name="dependencyService" ref="dependencyService" />
</bean>
<bean id="dependencyService" class="DependencyServiceImpl" />   

而在 JavaConfig中 则是这样的:

@Configuration
public class MockConfiguration {
  @Bean
  public MockService mockService() {
     return new MockServiceImpl(dependencyService());
  }
  @Bean
  public DependencyService dependencyService() {
     return new DependencyServiceImpl();
  }
}

如果一个 bean 的定义依赖其他 bean,则直接调用对应 JavaConfig 类中依赖 bean 的创建方法就可以了

在 JavaConfig 形式的依赖注入过程中,我们使用方法调用的形式注入依赖,如果这个方法返回的对象实例只被一个 bean 依赖注入,那也还好,如果多于一个 bean 需要依赖这个方法调用返回的对象实例,那是不是意味着我们就会创建多个同一类型的对象实例
从代码表述的逻辑来看,直觉上应该是会创建多个同一类型的对象实例,但实际上最终结果却不是这样,依赖注入的都是同一个 Singleton 的对象实例,那这是如何做到的
笔者一开始以为 Spring 框架会通过解析 JavaConfig 的代码结构,然后通过解析器转换加上反射等方式完成这一目的,但实际上 Spring 框架的设计和实现者采用了另一种更通用的方式,这在 Spring 的参考文档中有说明。即 通过拦截配置类的方法调用来避免多次初始化同一类型对象的问题,一旦拥有拦截逻辑的子类发现当前方法没有对应的类型实例时才会去请求父类的同一方法来初始化对象实例,否则直接返回之前的对象实例

所以,原来 Spring IoC 容器中有的特性(features)在 JavaConfig 中都可以表述,只是换了一种形式而已,而且,通过声明相应的 Java Annotation 反而“内聚”一处,变得更加简洁明了了

5.1 高曝光率的 Annotation

1) @Configuration
2) @ComponentScan
@ComponentScan 对应 XML 配置形式中的 <context:component-scan> 元素,用于 配合一些元信息 Java Annotation,比如 @Component 和 @Repository 等,将标注了这些元信息 Annotation 的 bean 定义类批量采集到 Spring 的 IoC 容器中
可以通过 basePackages 等属性来 细粒度地定制 @ComponentScan 自动扫描的范围,如果不指定,则 默认 Spring 框架实现 会从声明 @ComponentScan 所在类的 package 进行扫描
@ComponentScan 是 SpringBoot 框架魔法得以实现的一个关键组件,大家重点关注
3) @PropertySource 与 @PropertySources
@PropertySource 用于从某些地方加载 *.properties 文件内容,并将其中的属性加载到 IoC 容器中,便于 填充一些 bean 定义属性的占位符(placeholder),当然,这需要 PropertySourcesPlaceholderConfigurer 的配合

如果我们使用 Java 8 或者更高版本开发,那么,我们可以并行声明多个 @PropertySource:

@Configuration
@PropertySource("classpath:1.properties")
@PropertySource("classpath:2.properties")
@PropertySource("...")
public class XConfiguration{
  ... 
}

如果我们使用低于 Java 8 版本的 Java 开发 Spring 应用,又想声明多个 @PropertySource,则需要借助 @PropertySources 的帮助了,代码如下所示:

@PropertySources({ @PropertySource("classpath:1.properties"),
@PropertySource("classpath:2.properties"), ...})
public class XConfiguration{
   ...
}

4) @Import 与 @ImportResource
在 XML 形式的配置中,我们通过 的形式 将多个分开的容器配置合到一个配置中,在 JavaConfig 形式的配置中,我们则使用 @Import 这个 Annotation 完成同样目的

@Configuration
@Import(MockConfiguration.class)
public class XConfiguration {
   ...
}

@Import 只负责引入 JavaConfig形式定义的 IoC 容器配置,如果有一些遗留的配置或者遗留系统需要以 XML形式来配置(比如 dubbo框架),我们依然可以通过 @ImportResource 将它们一起合并到当前 JavaConfig配置的容器中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值