注:个人学习使用
使用过 Spring 的小伙伴,一定有被 XML 配置统治的恐惧。即使 Spring 后面引入了基于注解的配置,我们在开启某些 Spring 特性或者引入第三方依赖的时候,还是需要用 XML 或 Java 进行显式配置。
比如需要引入一个ViewResolver,传统的方式需要先在xml文件中进行配置:
**
**
然后通过@Bean注解注入IOC容器;
SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器(此处涉及到 JVM 类加载机制与 Spring 的容器知识),并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。
Spring Boot 中,我们直接引入一个 starter 即可。比如你想要在项目中使用 redis 的话,直接在项目中引入对应的 starter 即可。
引入 starter 之后,我们通过少量注解和一些简单的配置就能使用第三方组件提供的功能了。在我看来,自动装配可以简单理解为:通过注解或者一些简单的配置就能在 Spring Boot 的帮助下实现某块功能。
核心注解 @SpringBootApplication 。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
<1.>@SpringBootConfiguration
<2.>@ComponentScan
<3.>@EnableAutoConfiguration
public @interface SpringBootApplication {
}
大概可以把 @SpringBootApplication看作是 @Configuration、@EnableAutoConfiguration、@ComponentScan 注解的集合。根据 SpringBoot 官网,这三个注解的作用分别是:@EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
@Configuration:允许在上下文中注册额外的 bean 或导入其他配置类
@ComponentScan:扫描被**@Component** (@Service,@Controller)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。如下图所示,容器中将排除TypeExcludeFilter和AutoConfigurationExcludeFilter。
三个注解中的核心是:@EnableAutoConfiguration,它实现了AutoConfigurationImportSelector类。
AutoConfigurationImportSelector 类实现了 ImportSelector接口,也就实现了这个接口中的 selectImports方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中。
因此,总而言之,是因为**@EnableAutoConfiguration实现了一个AutoConfigurationImportSelector类**,而AutoConfigurationImportSelector类实现了ImportSelector接口中的selectImports方法,这个方法会获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中。
因此,这三个核心注解配合使用的效果,就是去外部引用jar包中的META-INF/spring.factories中根据条件将符合条件的类加载到IOC容器中。
第一步,创建threadpool-spring-boot-starter工程;
第二步,引入 Spring Boot 相关依赖;
定义starter需要引入spring-boot-starter依赖、spring-boot-autoconfigure依赖;如果需要配置元信息的话,还需引入spring-boot-configuration-processor依赖。
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-autoconfigure
org.springframework.boot
spring-boot-configuration-processor
true
第三步,创建AutoConfiguration类;*
1.创建该starter需要实现的业务处理类;
2.编写配置类,定义需要的配置信息和默认配置项,并指明关联配置文件的配置项前缀。它可以把相同前缀的配置信息通过配置项名称映射成实体类的属性中;
3.编写自动装配类,使用 @Configuration 和 @Bean 来进行自动装配,注入 Spring 容器中。
使用@Bean将业务处理类注入到Spring容器。
第四步,在threadpool-spring-boot-starter工程的 resources 包下创建META-INF/spring.factories文件;
在spring.factories文件中导入自定义的自动配置类:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.nobody.myjson.config.MyJsonConfiguration
最后新建工程引入threadpool-spring-boot-starter。
1.新建maven工程,编写pom文件,引入springboot依赖和自动配置依赖;
4.0.0
com.nobody
myjson-spring-boot-starter
0.0.1-SNAPSHOT
myjson-spring-boot-starter
Demo project for Spring Boot Starter
1.8
org.springframework.boot
spring-boot-starter
2.3.8.RELEASE
org.springframework.boot
spring-boot-configuration-processor
2.3.8.RELEASE
true
com.alibaba
fastjson
1.2.73
org.springframework.boot
spring-boot-autoconfigure
2.3.8.RELEASE
2.编写业务处理类,实现 Java 对象转换为带有指定前后缀的 JSON 字符串。
package com.nobody.myjson.service;
import com.alibaba.fastjson.JSON;
/**
* @Description 业务处理类
* @Author Mr.nobody
* @Date 2021/2/27
* @Version 1.0
*/
public class MyJsonService {
// 前缀
private String prefixName;
// 后缀
private String suffixName;
/**
* 将Java对象转为带有指定前后缀的JSON字符串
*
* @param o 需要转换的Java对象
* @return 转换后的字符串
*/
public String objectToMyJson(Object o) {
return prefixName + JSON.toJSONString(o) + suffixName;
}
public void setSuffixName(String suffixName) {
this.suffixName = suffixName;
}
}
3.配置类,定义需要的配置信息和默认配置项,并指明关联配置文件的配置项前缀。它可以把相同前缀的配置信息通过配置项名称映射成实体类的属性中。
package com.nobody.myjson.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @Description 配置类(类名一般为模块名+Properties) nobody.json为Starter使用者通过yml配置文件动态修改属性值的变量名前缀
* @Author Mr.nobody
* @Date 2021/2/27
* @Version 1.0
*/
@ConfigurationProperties(prefix = "nobody.json")
public class MyJsonProperties {
// Starter使用者没在配置文件中配置prefixName属性的值时的默认值
public static final String DEFAULT_PREFIX_NAME = "@";
// Starter使用者没在配置文件中配置suffixName属性的值时的默认值
public static final String DEFAULT_SUFFIX_NAME = "@";
private String prefixName = DEFAULT_PREFIX_NAME;
private String suffixName = DEFAULT_SUFFIX_NAME;
public String getPrefixName() {
return prefixName;
}
…………
public void setSuffixName(String suffixName) {
this.suffixName = suffixName;
}
}
4.自动装配类,使用 @Configuration 和 @Bean 来进行自动装配,注入 Spring 容器中。
package com.nobody.myjson.config;
import com.nobody.myjson.service.MyJsonService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Description 自动装配类
* @Author Mr.nobody
* @Date 2021/2/27
* @Version 1.0
*/
@Configuration // 标识此类是配置类
@ConditionalOnClass(MyJsonService.class) // 表示只有指定的class在classpath上时才能被注册
@EnableConfigurationProperties(MyJsonProperties.class) // 激活@ConfigurationProperties
public class MyJsonConfiguration {
private MyJsonProperties myJsonProperties;
// 自动注入配置类
public MyJsonConfiguration(MyJsonProperties myJsonProperties) {
this.myJsonProperties = myJsonProperties;
}
// 创建MyJsonService对象,注入到Spring容器中
@Bean
@ConditionalOnMissingBean(MyJsonService.class) // 当容器没有此bean时,才注册
public MyJsonService myJsonService() {
MyJsonService myJsonService = new MyJsonService();
myJsonService.setPrefixName(myJsonProperties.getPrefixName());
myJsonService.setSuffixName(myJsonProperties.getSuffixName());
return myJsonService;
}
}
5.在 src/main/resources/META-INF目录下新建 spring.factories 文件,输入以下内容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.nobody.myjson.config.MyJsonConfiguration
SpringBoot 项目启动时,类加载器会从 META-INF / spring.factories 加载给定类型的工厂实现的完全限定类名。也就是说类加载器得到工程中所有 jar 包中的 META-INF/spring.factories 文件资源,从而得到了一些包括自动配置相关的类的集合,然后将它们实例化,放入 Spring 容器中。
在开发工具 IDEA 通过 Maven 的 install 命令进行构建打包。或者在项目的目录下,打开命令行窗口,使用mvn install命令进行构建打包。打包后,会在工程的 target 目录下生成一个 jar 包,并且在 maven 本地仓库也会生成相应的 jar 包。
1.Spring
Spring是一个开源容器框架,可以接管web层,业务层,dao层,持久层的组件,也可以配置各种bean和维护bean与bean之间的关系。其核心就是控制反转(IOC)和面向切面(AOP),简单的说就是一个分层的轻量级开源框架。
2.SpringMVC
Spring MVC 是 Spring 中的一个很重要的模块,主要赋予 Spring 快速构建 MVC 架构的 Web 程序的能力。MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码。
3.SpringBoot
使用 Spring 进行开发各种配置过于麻烦比如开启某些 Spring 特性时,需要用 XML 或 Java 进行显式配置。于是,Spring Boot 诞生了!
Spring Boot的自动化配置能力放在第一位,因为它极大的降低了我们使用Spring Framework所付出的成本。这是Spring Boot的自动化配置是一个最具价值的解决方案。
Springboot是一个微服务框架,延续了spring框架的核心思想IOC和AOP,简化了应用的开发和部署。Spring Boot是为了简化Spring应用的创建、运行、调试、部署等而出现的,使用它可以做到专注于Spring应用的开发,而无需过多关注XML的配置。提供了一堆依赖打包,并已经按照使用习惯解决了依赖问题—>习惯大于约定。
Spring Boot基本上是Spring框架的扩展,它消除了设置Spring应用程序所需的XML配置,为更快,更高效的开发生态系统铺平了道路。
4.SpringCloud
Spring Cloud是一套分布式服务治理的框架,既然它是一套服务治理的框架,那么它本身不会提供具体功能性的操作,更专注于服务之间的通讯、熔断、监控等。因此就需要很多的组件来支持一套功能。
Spring boot是Spring的一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务;Spring Cloud是一个基于Spring Boot实现的云应用开发工具;
Spring Boot专注于快速、方便集成的单个个体,Spring Cloud是关注全局的服务治理框架;
Spring boot使用了默认大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot来实现。
Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。
5.SpringCloud组件
1)默默无闻服务
融合在每个微服务中、依赖其它组件并为其提供服务。
① Ribbon:客户端负载均衡,特性有区域亲和、重试机制。
② Hystrix:客户端容错保护,特性有服务降级、服务熔断、请求缓存、请求合并、依赖隔离。
③ Feign:声明式服务调用,本质上就是Ribbon+Hystrix。
④ Stream:消息驱动,有Sink、Source、Processor三种通道,特性有订阅发布、消费组、消息分区。
⑤ Bus:消息总线,配合Config仓库修改的一种Stream实现。
⑥ Sleuth:分布式服务追踪,需要搞清楚TraceID和SpanID以及抽样,如何与ELK整合。
2)独挑大梁
独自启动不需要依赖其它组件,单枪匹马都能干。
① Eureka:服务注册中心,特性有失效剔除、服务保护。
② Dashboard:Hystrix仪表盘,监控集群模式和单点模式,其中集群模式需要收集器Turbine配合。
③ Zuul:API服务网关,功能有路由分发和过滤。
④ Config:分布式配置中心,支持本地仓库、SVN、Git、Jar包内配置等模式。
6.Cloud和Dubbo的区别
1.每一个独立的容器都需要相互通讯:
基于http的RESTful方式. : springcloud
基于RPC方式. : dubbo
2.服务之间是需要一个统一的管理 —— 注册中心.
dubbo: zookeeper(CP,数据一致性,分区容错性)
springcloud: eureka(AP,服务可用性,分区容错性)
3.SpringCloud还比Dubbo多实现了这几个功能:
Feign + Ribbon. - 服务的调用和负载均衡.
Hystrix - 服务的降级,服务的熔断.
config - 分布式配置管理.(通过git管理)
zuul - 网关. (filter)
Dubbo 底层是使用 Netty 这样的 NIO 框架,是基于 TCP 协议传输的,配合以 Hession 序列化完成 RPC 通信。 dubbo协议是阿里巴巴自己实现的一种应用层协议,
Spring Cloud 是基于 Http 协议 Rest 接口调用远程过程的通信,相对来说 Http 请求会有更大的报文,占的带宽也会更多。但是 REST 相比 RPC 更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这在强调快速演化的微服务环境下,显得更为合适,至于注重通信速度还是方便灵活性,具体情况具体考虑。
Dubbo 框架提供了自定义的高性能 RPC 通信协议:基于 HTTP/2 的 Triple 协议 和 基于 TCP 的 Dubbo2 协议。除此之外,Dubbo 框架支持任意第三方通信协议,如官方支持的 gRPC、Thrift、REST、JsonRPC、Hessian2 等,更多协议可以通过自定义扩展实现。这对于微服务实践中经常要处理的多协议通信场景非常有用。
Provider: 暴露服务的服务提供方
Consumer: 调用远程服务的服务消费方
Registry: 服务注册与发现的注册中心
Monitor: 统计服务的调用次数和调用时间的监控中心
Container: 服务运行容器
Dubbo作为一个分布式服务框架,主要具有如下几个核心的要点:
1.服务定义
服务是围绕服务提供方和服务消费方的,服务提供方实现服务,而服务消费方调用服务。
2.服务注册
对于服务提供方,它需要发布服务,而且由于应用系统的复杂性,服务的数量、类型也不断膨胀;对于服务消费方,它最关心如何获取到它所需要的服务,而面对复杂的应用系统,需要管理大量的服务调用。而且,对于服务提供方和服务消费方来说,他们还有可能兼具这两种角色,即既需要提供服务,有需要消费服务。
通过将服务统一管理起来,可以有效地优化内部应用对服务发布/使用的流程和管理。服务注册中心可以通过特定协议来完成服务对外的统一。
Dubbo提供的注册中心有如下几种类型可供选择:
Multicast注册中心
Zookeeper注册中心
Redis注册中心
Simple注册中心
3.服务监控
无论是服务提供方,还是服务消费方,他们都需要对服务调用的实际状态进行有效的监控,从而改进服务质量。
4.远程通信与信息交换
远程通信需要指定通信双方所约定的协议,在保证通信双方理解协议语义的基础上,还要保证高效、稳定的消息传输。Dubbo继承了当前主流的网络通信框架,主要包括如下几个:
Mina
Netty
Grizzly
5.服务调用
下面从Dubbo官网直接拿来,看一下基于RPC层,服务提供方和服务消费方之间的调用关系,如图所示:
上述图所描述的调用流程如下:
服务提供方发布服务到服务注册中心;
服务消费方从服务注册中心订阅服务;
服务消费方调用已经注册的可用服务。
协议支持
Dubbo支持多种协议,如下所示:
Dubbo协议
Hessian协议
HTTP协议
RMI协议
WebService协议
Thrift协议
Memcached协议
Redis协议
6.一个完整的RPC调用过程
1、建立通信
首先要解决通讯的问题:即A机器想要调用B机器,首先得建立起通信连接。
主要是通过在客户端和服务器之间建立TCP连接,远程过程调用的所有交换的数据都在这个连接里传输。连接可以是按需连接,调用结束后就断掉,也可以是长连接,多个远程过程调用共享同一个连接。
2、服务寻址
要解决寻址的问题,也就是说,A服务器上的应用怎么告诉底层的RPC框架,如何连接到B服务器(如主机或IP地址)以及特定的端口,方法的名称名称是什么。
通常情况下我们需要提供B机器(主机名或IP地址)以及特定的端口,然后指定调用的方法或者函数的名称以及入参出参等信息,这样才能完成服务的一个调用。
可靠的寻址方式(主要是提供服务的发现)是RPC的实现基石,比如可以采用Redis或者Zookeeper来注册服务等等。
3、网络传输
3.1、序列化
当A机器上的应用发起一个RPC调用时,调用方法和其入参等信息需要通过底层的网络协议如TCP传输到B机器,由于网络协议是基于二进制的,所有我们传输的参数数据都需要先进行序列化(Serialize)或者编组(marshal)成二进制的形式才能在网络中进行传输。然后通过寻址操作和网络传输将序列化或者编组之后的二进制数据发送给B机器。
3.2、反序列化
当B机器接收到A机器的应用发来的请求之后,又需要对接收到的参数等信息进行反序列化操作(序列化的逆操作),即将二进制信息恢复为内存中的表达方式,然后再找到对应的方法(寻址的一部分)进行本地调用(一般是通过生成代理Proxy去调用,
通常会有JDK动态代理、CGLIB动态代理、Javassist生成字节码技术等),之后得到调用的返回值。
4、服务调用
B机器进行本地调用(通过代理Proxy和反射调用)之后得到了返回值,此时还需要再把返回值发送回A机器,同样也需要经过序列化操作,然后再经过网络传输将二进制数据发送回A机器,而当A机器接收到这些返回值之后,则再次进行反序列化操作,恢复为内存中的表达方式,最后再交给A机器上的应用进行相关处理(一般是业务逻辑处理操作)。
经过以上四个步骤之后,一次完整的RPC调用算是完成了,另外可能因为网络抖动等原因需要重试等。
Dubbo 通过组合使用 Netty建立通信;
ZooKeeper 或其他注册中心完成服务寻址(服务注册与发现);
Hessian、JSON、Protobuf等序列化协议进行网络传输;
通过动态代理技术实现服务的远程调用。
这使得开发者可以更方便地构建分布式应用,实现高性能、高可用的微服务架构。