
本文主要介绍 Java 中常用的应用框架,重点讲解如下三部分内容。
- Spring 框架中的主要知识点;
- NIO 框架 Netty 以及基于 Netty 实现的主流 RPC 框架 Motan、Dubbo 和 gRPC;
- ORM 框架 MyBatis。
常用框架汇总
先来看常用框架的知识点汇总,如下图所示。
如上图所示,左上方是 Spring 系列。很多研发人员把 Spring 看作心目中最好的 Java 项目,没有之一。Spring 系列包含非常多的项目,可以满足 Java 开发中的方方面面。那么来看几个常用的 Spring 框架。
Spring
Spring Framework,也就是我们常说的 Spring 框架,包括了 IoC 依赖注入,Context 上下文、 Bean 管理、SpringMVC 等众多功能模块,其他 Spring 项目比如 Spring Boot 也会依赖 Spring 框架。
Spring Boot 的目标是简化 Spring 应用和服务的创建、开发与部署,简化了配置文件,使用嵌入式 Web 服务器,含有诸多开箱即用的微服务功能,可以和 Spring Cloud 联合部署。Spring Boot 的核心思想是约定大于配置,应用只需要很少的配置即可,简化了应用开发模式。
Spring Data 是一个数据访问及操作的工具集,封装了多种数据源的操作能力,包括:JDBC、Redis、MongoDB 等。
Spring Cloud 是一套完整的微服务解决方案,是一系列不同功能的微服务框架的集合。Spring Cloud 基于 Spring Boot,简化了分布式系统的开发,集成了服务发现、配置管理、消息总线、负载均衡、断路器、数据监控等各种服务治理能力。比如sleuth提供了全链路追踪能力,Netflix套件提供了hystrix熔断器、zuul网关等众多的治理组件。config 组件提供了动态配置能力,bus组件支持使用 RabbitMQ、Kafka、ActiveMQ 等消息队列,实现分布式服务之间的事件通信。
Spring Security 用于快速构建安全的应用程序和服务,在 Spring Boot 和 Spring Security OAuth2 的基础上,可以快速实现常见安全模型,如单点登录,令牌中继和令牌交换。这里可以了解一下 OAuth2 授权机制和 JWT 认证方式。OAuth2 是一种授权机制,规定了完备的授权、认证流程。JWT 全称是 JSON Web Token,是一种把认证信息包含在 token 中的认证实现,OAuth2 授权机制中就可以应用 JWT 来作为认证的具体实现方法。
Struts
Struts 是曾经非常火爆的 Web 组合 SSH 中的控制层。我们知道 Web 服务一般都采用 MVC 分层模型构建,就是 Model 层负责内部数据模型,Controller 负责请求的分发控制,View 层负责返回给用户展示的视图。Struts 实现的就是其中控制层的角色。
Struts 采用 Filter 实现,针对类进行拦截,每次请求就会创建一个 Action。不过使用 Struts 的 SSH 组合已经逐渐被使用 SpringMVC 的 SSM 组合代替,也就是 SpringMVC+Spring+MyBatis的组合,一方面原因是由于 Struts 对几次安全漏洞的处理,让大家对 Struts 的信心受到影响;另一方面,SpringMVC 更加灵活,不需要额外配置,不存在和 Spring 整合等问题,使用更加方便。所以建议以 SSM 框架的学习为主。
ORM
ORM 就是对象关系匹配,解决面向对象与关系数据库存在的互不匹配的问题。简单来说,就是把关系数据库中的数据转换成面向对象程序中的对象。常用的 ORM 框架有 Hibernate 和 MyBatis,也就是 SSH 组合和 SSM 组合中的 H 与 M。
来看一下 Hibernate 和 MyBatis 的特点和区别。
- Hibernate 对数据库结构提供了完整的封装,实现了 POJO 对象与数据库表之间的映射,能够自动生成并执行 SQL 语句。只要定义了 POJO 到数据库表的映射关系,就可以通过 Hibernate 提供的方法完成数据库操作。Hibernate 符合 JPA 规范,就是 Java 持久层 API。
- MyBatis 通过映射配置文件,将 SQL 所需的参数和返回的结果字段映射到指定对象,MyBatis 不会自动生成 SQL,需要自己定义 SQL 语句,不过更方便对 SQL 语句进行优化。
总结起来,Hibernate 配置要比 MyBatis 复杂的多,学习成本也比 MyBatis 高。MyBatis,简单、高效、灵活,但是需要自己维护 SQL;Hibernate 功能强大、全自动、适配不同数据库,但是非常复杂,灵活性稍差。
Netty
Netty 是一个高性能的异步事件驱动的网络通信框架,Netty 对 JDK 原生 NIO 进行封装,简化了网络服务的开发。
另外,同类型的框架还有 MINA、Grizzly,不过目前使用得相对较少,一般不会在面试题目中出现,可以作为兴趣简单了解。
RPC
RPC 服务,Motan、Dubbo、gRPC 都是比较常用的高性能 RPC 框架,可以提供完善的服务治理能力,Java 版本的通信层都是基于前面提到的 Netty 实现。
其他框架
此外,Jersy 和 RESTEasy 都是可以快速开发 RESTful 服务的框架。与 SpringMVC 相比,这两个框架都是基于 JAX-RS 标准,而 SpringMVC 基于 Servlet,使用自己构建的 API,是两个不同的标准。
Shiro 框架是一个与 Spring Security 类似的开源的权限管理框架,用于访问授权、认证、加密及会话管理。能够支持单机与分布式 session 管理。相比 Security,Shiro更加简单易用
详解 Spring 框架
对于 Spring 框架,讲解中涉及的流程与实现默认都是基于最新的 5.x 版本。先来看 Spring 中的几个重要概念。
IoC
IoC,也就是控制反转,如下图,拿公司招聘岗位来举例。假设一个公司有产品、研发、测试等岗位。如果是公司根据岗位要求,逐个安排人选,如图中向下的箭头,这是正向流程。如果反过来,不用公司来安排候选人,而是由第三方猎头来匹配岗位和候选人,然后进行推荐,如图中向上的箭头,这就是控制反转。
在 Spring 中,对象的属性是由对象自己创建的,就是正向流程;如果属性不是对象创建,而是由 Spring 来自动进行装配,就是控制反转。这里的 DI 也就是依赖注入,就是实现控制反转的方式。正向流程导致了对象于对象之间的高耦合,IoC 可以解决对象耦合的问题,有利于功能的复用,能够使程序的结构变得非常灵活
Context 和 Bean
Spring 进行 IoC 实现时使用的两个概念:Context 上下文和 Bean。如下图所示,所有被 Spring 管理的、由 Spring 创建的、用于依赖注入的对象,就叫作一个 Bean。Spring 创建并完成依赖注入后,所有 Bean 统一放在一个叫作 Context 的上下文中进行管理。
AOP
AOP,就是面向切面编程。如下图所示,一般我们的程序执行流程是从 Controller 层调用 Service 层、然后 Service 层调用 DAO 层访问数据,最后在逐层返回结果。这个是图中向下箭头所示的按程序执行顺序的纵向处理。
但是,我们思考这样一个问题,一个系统中会有多个不同的服务,例如用户服务、商品信息服务等等,每个服务的Controller层都需要验证参数,都需要处理异常,如果按照图中红色的部分,对不同服务的纵向处理流程进行横切,在每个切面上完成通用的功能,例如身份认证、验证参数、处理异常等等、这样就不用在每个服务中都写相同的逻辑了,这就是 AOP 思想解决的问题。AOP 以功能进行划分,对服务顺序执行流程中的不同位置进行横切,完成各服务共同需要实现的功能
组件
再来看到 Spring 框架,下图中列出了 Spring 框架主要包含的组件。这张图来自 Spring4.x 的文档。目前最新的 5.x 版本中右面的 Portlet 组件已经被废弃掉,同时增加了用于异步响应式处理的 WebFlux 组件。这里不需要对所有的组件都详细了解,只需要重点了解最常用的几个组件实现,以及知道每个组件用来实现哪一类功能就可以了。
图中红框框住的是比较重要的组件,Core 组件是 Spring 所有组件的核心;Bean 组件和 Context 组件我刚才提到了,是实现 IoC 和依赖注入的基础;AOP 组件用来实现面向切面编程;Web 组件包括 SpringMVC,是 Web 服务的控制层实现
动态代理和静态代理
接下来是 Spring 中机制和概念相关的知识点,如下图所示。
AOP 的实现是通过代理模式,在调用对象的某个方法时,执行插入的切面逻辑。实现的方式有动态代理,也叫运行时增强,比如 JDK 代理、CGLIB;静态代理是在编译时进行织入或类加载时进行织入,比如 AspectJ。关于 AOP 还需要了解一下对应的 Aspect、pointcut、advice 等注解和具体使用方式。
PlaceHolder 动态替换
PlaceHolder 动态替换主要需要了解替换发生的时间,是在 Bean Definition 创建完成后,Bean 初始化之前,是通过 BeanFactoryPostProcessor 接口实现的。主要实现方式有 PropertyPlaceholderConfigurer 和 PropertySourcesPlaceholderConfigurer。这两个类实现逻辑不一样,Spring Boot 使用 PropertySourcesPlaceholderConfigurer 实现。
事务
事务,需要了解 Spring 中对事务规定的隔离类型和事务传播类型。这里要知道事务的隔离级别是由具体的数据库来实现的,在数据库部分会作详细介绍。事务的传播类型,可以重点了解最常用的 REQUIRED 和 SUPPORTS类型
核心接口/类
再来看图右上方需要重点掌握的核心类。
- ApplicationContext 保存了 IoC 的整个应用上下文,可以通过其中的 BeanFactory 获取到任意到 Bean;
- BeanFactory 主要的作用是根据 Bean Definition 来创建具体的 Bean;
- BeanWrapper 是对 Bean 的包装,一般情况下是在 Spring IoC 内部使用,提供了访问 Bean 的属性值、属性编辑器注册、类型转换等功能,方便 IoC 容器用统一的方式来访问 Bean 的属性;
- FactoryBean 通过 getObject 方法返回实际的 Bean 对象,例如 Motan 框架中 referer 对 service 的动态代理就是通过 FactoryBean 来实现的。
Scope
Bean 的 Scope 是指 Bean 的作用域,默认情况下是单例模式,这也是使用最多的一种方式;多例模式,即每次从 BeanFactory 中获取 Bean 都会创建一个新的 Bean。Request、Session、Global-session 是在 Web 服务中使用的 Scope。
- Request 每次请求都创建一个实例;
- Session 是在一个会话周期内保证只有一个实例;
- Global-session 在 5.x 版本中已经不再使用,同时增加了 Application 和 Websocket 两种Scope,分别保证在一个 ServletContext 与一个 WebSocket 中只创建一个实例。
还可以了解一下 Spring 的事件机制,知道 Spring 定义的五种标准事件,了解如何自定义事件和实现对应的 ApplicationListener 来处理自定义事件。
应用
下面来看 Spring 应用相关的知识点,如下图所示。
首先要熟练掌握常用注解的使用。
类型类的注解,包括 Controller、Service 等,可以重点了解一下 Component 和 Bean 注解的区别:
- @Component 注解在类上使用表明这个类是个组件类,需要 Spring 为这个类创建 Bean
- @Bean 注解使用在方法上,告诉 Spring 这个方法将会返回一个 Bean 对象,需要把返回的对象注册到 Spring 的应用上下文中。
设置类注解可以重点了解 @Autowire 和 @Qualifier 以及 byType、byName 等不同的自动装配机制
Web 类主要以了解为主,关注 @RequestMapping、@GetMapping、@PostMapping 等路径匹配注解,以及 @PathVariable、@RequestParam 等参数获取注解。
功能类的注解,包括 @ImportResource 引用配置、@ComponentScan 注解自动扫描、@Transactional 事务注解等等,这里不一一介绍了。
如上图右边所示,Spring 应用部分,还需要了解配置 Spring 的几种方式:XML 文件配置、注解配置和使用 API 进行配置。
自动装配机制需要了解按类型匹配进行自动装配,按 Bean 名称进行自动装配,构造器中的自动装配和自动检测等主要的四种方式。
最后还可以了解一下 List、Set、Map 等集合类属性的配置方式以及内部 Bean 的使用。
Context 初始化流程
如下图所示,左侧是三种类型的 Context:
- XML 配置方式的 Context;
- Spring Boot 的 Context;
- Web 服务的 Context。
不论哪种 Context,创建后都会调用到 AbstractApplicationContext 类的 refresh 方法,流程如下。
- 首先对刷新进行准备,包括设置开始时间、设置激活状态、初始化 Context 环境中的占位符,这个动作根据子类的需求由子类来执行,然后验证是否缺失必要的 properties。
- 刷新并获得内部的 Bean Factory
- 对 BeanFactory 进行准备工作,比如设置类加载器和后置处理器、配置不进行自动装配的类型、注册默认的环境 Bean。
- 为 Context 的子类提供后置处理 BeanFactory 的扩展能力。如果子类想在 Bean 定义加载完成后,开始初始化上下文之前做一些特殊逻辑,可以复写这个方法。
- 执行 Context 中注册的 Bean Factory 后缀处理器。这里有两种后置处理器,一种是可以注册 Bean 的后缀处理器,另一种是针对 BeanFactory 进行处理的后置处理器。执行的顺序是,先按优先级执行可注册 Bean 的处理器,在按优先级执行针对 BeanFactory的处理器。对 Spring Boot 来说,这一步会进行注解 Bean Definition 的解析。流程如图右侧所示,由 ConfigurationClassPostProcessor 触发、由 ClassPathBeanDefinitionScanner 解析并注册到 BeanFactory。
- 按优先级顺序在 BeanFactory 中注册 Bean的后缀处理器,Bean 后置处理器可以在 Bean 初始化前、后执行处理。
- 初始化消息源,消息源用来支持消息的国际化
- 初始化应用事件广播器。事件广播器用来向 ApplicationListener 通知各种应用产生的事件,是一个标准的观察者模式
- 是留给子类的扩展步骤,用来让特定的 Context 子类初始化其他的 Bean。
- 把实现了 ApplicationListener 的 Bean 注册到事件广播器,并对广播器中的早期未广播事件进行通知。
- 冻结所有 Bean 描述信息的修改,实例化非延迟加载的单例 Bean。
- 完成上下文的刷新工作,调用 LifecycleProcessor 的 onFresh() 方法以及发布 ContextRefreshedEvent 事件
- 在 finally 中,执行第十三步,重置公共的缓存,比如 ReflectionUtils 中的缓存、 AnnotationUtils 中的缓存等等;
至此,Spring 的 Context 初始化完成。由于篇幅和时间的关系,这里介绍了最主要的主流程,建议课后阅读源码来复习这个知识点,补全细节。
Bean 生命周期
面试中经常问到 Bean 的生命周期,如下图,我们先看绿色的部分,Bean 的创建过程。
- 调用 Bean 的构造方法创建 Bean;
- 通过反射调用 setter 方法进行属性的依赖注入
- 如果实现 BeanNameAware 接口的话,会设置 Bean 的 name;
- 如果实现了 BeanFactoryAware,会把 BeanFactory 设置给 Bean
- 如果实现了 ApplicationContextAware,会给 Bean 设置 ApplictionContext;
- 如果实现了 BeanPostProcessor 接口,则执行前置处理方法;
- 实现了 InitializingBean 接口的话,执行 afterPropertiesSet 方法;
- 执行自定义的 init 方法;
- 执行 BeanPostProcessor 接口的后置处理方法。
以上就完成了 Bean 的创建过程。而在使用完 Bean 需要销毁时,会先执行 DisposableBean 接口的 destroy 方法,然后在执行自定义的 destroy 方法。这部分也建议阅读源码加深理解
扩展接口
在对 Spring 进行定制化功能扩展时,可以选择一些扩展点,如下图所示。
- BeanFactoryPostProcessor 是 BeanFactory 后置处理器,支持在 BeanFactory 标准初始化完成后,对 BeanFactory 进行一些额外处理。讲 Context 初始化流程时介绍过,这时所有的 Bean 的描述信息已经加载完毕,但是还没有进行 Bean 初始化。例如前面提到的 PropertyPlaceholderConfigurer,就是在这个扩展点上对 Bean 属性中的占位符进行替换。
- BeanDefinitionRegistryPostProcessor,它扩展自BeanFactoryPostProcessor,在执行 BeanFactoryPostProcessor 的功能前,提供了可以添加 Bean Definition 的能力,允许在初始化一般 Bean 前,注册额外的 Bean。例如可以在这里根据 Bean 的 Scope 创建一个新的代理 Bean。
- BeanPostProcessor,提供了在 Bean 初始化之前和之后插入自定义逻辑的能力。与 BeanFactoryPostProcessor 的区别是处理的对象不同,BeanFactoryPostProcessor 是对 BeanFactory 进行处理,BeanPostProcessor 是对 Bean 进行处理。
上面这三个扩展点,可以通过实现 Ordered 和PriorityOrdered 接口来指定执行顺序。实现 PriorityOrdered 接口的 processor 会先于实现 Ordered 接口的执行。
- ApplicationContextAware,可以获得 ApplicationContext 及其中的 Bean,当需要在代码中动态获取 Bean 时,可以通过实现这个接口来实现。
- InitializingBean,可以在 Bean 初始化完成,所有属性设置完成后执行特定逻辑,例如对自动装配对属性进行验证等。
- DisposableBean,用于在 Bean 被销毁前执行特定的逻辑,例如做一些回收工作等。
- ApplicationListener,用来监听 Spring 的标准应用事件或者自定义事件。
Spring Boot
下面来看 Spring Boot 相关的知识点,如下图所示。
首先是 Spring Boot 启动流程的主要步骤:
- 要配置 Environment。
- 准备 Context 上下文,包括执行 ApplicationContext 的后置处理、初始化 Initializer、通知Listener 处理 ContextPrepared 和 ContextLoaded 事件
- 执行 refreshContext,也就是前面介绍过的 AbstractApplicationContext 类的 refresh 方法。
然后要知道在 Spring Boot 中有两种上下文,一种是 bootstrap, 另外一种是 application。其中,bootstrap 是应用程序的父上下文,会先于 applicaton 加载。bootstrap 主要用于从额外的资源来加载配置信息,还可以在本地外部配置文件中解密属性。bootstrap 里面的属性会优先加载,默认也不能被本地相同配置覆盖。
再来看 Spring Boot 的注解。
需要知道 @SpringBootApplication 包含了 @ComponentScan、@EnableAutoConfiguration、@SpringBootConfiguration 三个注解,而 @SpringBootConfiguration 注解包含了 @Configuration 注解。也就是 Spring Boot 的自动配置功能。@Conditional 注解就是控制自动配置的生效条件的注解,例如 Bean 或 Class 存在、不存在时进行配置,当满足条件时进行配置等。
最后,了解一下 Spring Boot 的几个特色模块。
- Starter 是 Spring Boot 提供的无缝集成功能的一种方式,使用某个功能时开发者不需要关注各种依赖库的处理,不需要具体的配置信息,由 Spring Boot 自动配置进行 Bean的创建。例如需要使用 Web 功能时,只需要在依赖中引入 Spring-boot-starter-web 即可。
- Actuator 是用来对应用程序进行监视和管理,通过 RESTful API 请求来监管、审计、收集应用的运行情况。
- DevTools 提供了一系列开发工具的支持,来提高开发效率。例如热部署能力等。
- CLI 就是命令行接口,是一个命令行工具,支持使用 Groovy 脚本,可以快速搭建 Spring 原型项目。
详解 Netty
下面我们来看 Netty 相关的知识点,如下图所示。
特点
如上图左侧所示,首先了解 Netty 的特点。
- Netty 是一个高性能的异步事件驱动的 NIO 框架,它对消息的处理采用串行无锁化设计,提供了对 TCP、UDP 和文件传输的支持。
- Netty 内置了多种 encoder 和 decoder 实现来解决 TCP 粘包问题。
- Netty 处理消息时使用了池化的缓冲池 ByteBufs,提高性能。
- 结合内存零 copy 机制,减少了对象的创建,降低了 GC 的压力。
主要概念
需要掌握 Netty 中的一些对象概念,比如将 Socket 封装成 Channel 对象,在 Channel 读写消息时,使用 ChannelHandler 对消息进行处理,一组 Handler 顺序链接组成 ChannelPipeline 的责任链模式。一个 Channel 产生的所有事件交给一个单线程的 EventLoop 事件处理器来进行串行处理。而 Bootstrap 对象的主要作用是配置整个 Netty 程序串联起各个组件,是一个 Netty 应用的起点。
零内存复制
要了解 Netty 的内存零 copy 技术。包括使用堆外内存来避免在 Socket 读写时缓冲数据在堆外与堆内进行频繁复制;使用 CompositeByteBuf 来减少多个小的 buffer 合并时产生的内存复制;使用 FileRegion 实现文件传输的零拷贝等。
粘包与半包
要了解 TCP 协议下粘包与半包等产生原因,知道 Netty 提供的多个 decoder 是用什么方式解决这个问题的。例如 FixedLengthFrameDecoder 用来解决固定大小数据包的粘包问题、LineBasedFrameDecoder 适合对文本进行按行分包、DelimiterBasedFrameDecoder 适合按特殊字符作为分包标记的场景、LengthFieldBasedFrameDecoder 可以支持复杂的自定义协议分包等等。
Netty3 和 Netty4
简单了解一下 Netty3 和 Netty4 的区别,其中主要的就是两个版本的线程处理模型完全不同, Netty4 处理得更加优雅。其他的以 Netty4 的特点为主即可。
线程模型
Netty 线程模型采用“服务端监听线程”和“IO 线程”分离的方式,如下图,左侧 Boss 线程组负责监听事件,创建 Socket 并绑定到 Worker 线程组。
Worker 线程组负责 IO 处理。线程组由 EventLoopGroup 实现,其中包含了多个 EventLoop 事件处理器,每个 EventLoop 包含一个处理线程。通常情况下在 NIO 非阻塞模式下,Netty 为每个 Channel 分配一个 EventLoop,并且它的整个生命周期中的事件都由这个 EventLoop 来处理。一个 EventLoop 可以绑定多个 Channel。
如上图右侧所示,EventLoop 的处理模型,Netty4 中 Channel 的读写事件都是由 Worker 线程来处理。请求处理中最主要的就是 ChannelPipeline,其中包含了一组 ChannelHandler。这些 Handler 组成了责任链模式,依次对 Channel 中的消息进行处理。一般接收消息时,Pipeline 处理完成会把消息提交到业务线程池进行处理,当业务线程处理完成时,会封装成 Task,提交回 Channel 对应的 EventLoop 来写回返回值。
详解 RPC
RPC 是远程过程调用的简写,RPC 与 HTTP 一样都可以实现远程服务的调用,但是使用方式上有很大的区别。它能够像使用本地方法一样调用远程的方法。
交互流程
如下图所示,来看 RPC 的交互流程。图中绿色的模块是 RPC 中最主要的三个角色。左边的是 Client 端,就是请求的发起方,也可以叫作 Consumer 或者 Referer。右边的模块是 Server 端,就是提供服务实现的一方,也叫作 Provider。
为了保持较高的性能,Client 端一般都是直接请求远端的 Server 节点。因此,RPC 框架需要自动的服务注册与发现的能力,上方的绿色的注册中心就是用来动态维护可用服务节点信息的模块。
图中的箭头代表交互流程。当 Server 提供服务时,向注册中心注册服务信息,告诉注册中心可以提供哪些服务。同时与注册中心保持心跳或者维持长链接,来维持 Server 可用状态,具体方式与注册中心的实现有关,例如 ZK 使用长链接推送方式而 Consul 使用心跳方式。
如上图所示,当 Client 需要使用服务时,会先向注册中心订阅服务,获得可用的 Server 节点,并保存在 Client 本地。当 Server 节点发生变更时会通知 Client 更新本地 Server 节点信息。Client 按某种负载均衡策略直接请求 Server 使用服务。注意:注册中心只参与服务节点的注册与变更通知,并不会参与具体请求的处理。
另外一般的 RPC 框架都提供了完整的服务治理能力,因此会有额外的管理模块和信息采集模块来监控、管理服务。如图中灰色的模块所示。
开源框架
来看三款比较有特色的主流 RPC 框架,如下图所示。
Dubbo 是阿里开源的 RPC 框架,提供完善的服务治理能力,可以快速为 Java 服务提供 RPC 能力。Dubbo 提供了随机、轮询、最少调用优先等多种负载均衡策略,提供对 ZK 等多种注册中心等支持,能够自动完成服务的注册与发现。Dubbo 提供可视化的管理后台,方便对服务状态进行监控和管理。Dubbo 的数据通信默认使用我 Netty 来实现,拥有非常不错的性能。
微博开源的轻量级服务治理框架 Motan。Motan 的特点是轻量级,提供强大灵活的扩展能力,Motan 提供了多语言支持,目前支持 Java、PHP、Lua、Golang 等多语言交互,目前 Python 和 C++ 的客户端也在研发中。Motan 通过 Agent 代理方式,实现了的跨语言 ServiceMesh 的支持。ServiceMesh 被誉为下一代微服务,在课时 10 还会重点介绍。Motan Java 版本的通信层也是通过 Netty 来实现的,基于 TCP 的私有协议进行通信
Google 开源的 gRPC。gRPC 默认使用 Protobuf 进行消息序列化,非常适合多语言服务之间进行交互。虽然 gRPC 本身支持的服务治理能力并不强,但拥有非常灵活的插件扩展能力,可以方便的实现自定义的服务治理能力。gRPC 基于 HTTP2 协议,能够支持链接复用,并且提供了流式调用能力,也支持从服务端进行推送消息的能力。
详解 MyBatis
特点
下面我们来看 ORM 框架 MyBatis,它的知识结构图如下所示。首先要了解它的特点,可以和 Hibernate 来对比进行理解。
MyBatis 的优点:
- MyBatis 是原生SQL,不像 Hibernate 的 HQL 需要额外的学习成本;
- MyBatis 的 SQL 语句与代码进行了解耦合,这与 Hibernate 是一致的;
- MyBatis 功能简单,学习成本比较低,使用的门槛也非常低,可以快速上手;
- MyBatis SQL调优比较灵活,而 Hibernate,SQL 语句是自动生成的,当有复杂语句需要进行优化时就比较难处理。
MyBatis 的缺点:
- 相比 Hibernate 这样的全自动 ORM 框架,不能自动生成 SQL 语句,编写 SQL 的工作量比较大,尤其是字段多、关联表多的情况下
- 另外一个缺点就是 SQL 语句依赖于具体数据库,导致数据库迁移性差,而 Hibernate 则拥有良好的数据库可移植性。
缓存
MyBatis 提供了两级缓存。MyBatis 的一级缓存的存储作用域是 Session,会对同一个 Session 中执行语句的结果进行缓存,来提高再次执行时的效率。MyBatis 内部通过 HashMap 实现存储缓存,一级缓存是默认开启的。
MyBatis 的二级缓存的作用域是一个 Mapper 的 namespace,在同一个 namespace 中查询 SQL 时可以从缓存中获取数据。二级缓存能够跨 SqlSession 生效,并且可自定义存储源,比如 Ehcache。MyBatis 的二级缓存可以设置剔除策略、刷新间隔、缓存数量等参数来进行优化。
应用
- MyBatis 提供 #{} 的变量占位符,来支持 SQL 预编译,防止 SQL 注入。
- 获取自增主键的 id 可以通过 keyProperty 配置和使用 selectKey 两种方式来实现。
- 要记住动态 SQL 常用的几个标签,例如 foreach、where、if、choose、trim 等等。
主要对象
需要理解 MyBatis 的主要对象有哪些,它们的作用是什么,举例如下。
- SqlSessionFactory 是用来创建 SqlSession 的工厂类,一个 SqlSessionFactory 对应配置文件中的一个环境,也就是一个数据库配置。
- 对数据库的操作必须在 SqlSession 中进行,SqlSession 非线程安全,每一次操作完数据库后都要调用 Close 对其进行关闭。
- SqlSession 通过内部的 Executor 来执行增删改查操作。
- StatementHandler 用来处理 SQL 语句预编译,设置参数等。
- ParameterHandler 用来设置预编译参数。
- ResultSetHandler 用来处理结果集。
- TypeHandler 进行数据库类型和 JavaBean 类型的互相映射。
插件机制
MyBatis 的插件机制是通过拦截器组成责任链来对 Executor、StatementHandler、ParameterHandler、ResultSetHandler 这四个作用点进行定制化处理。另外可以了解一下基于插件机制实现的 PageHelper 分页插件
处理流程
如下图所示,MyBatis 的处理流程。
在执行 SQL 时,首先会从 SqlSessionFactory 中创建一个新的 SqlSession。
SQL 语句是通过 SqlSession 中的 Executor 来执行,Executor 根据 SqlSession 传递的参数执行 query() 方法,然后创建一个 StatementHandler 对象,将必要的参数传递给 StatementHandler,由 StatementHandler 来完成对数据库的查询。
StatementHandler 调用 ParameterHandler 的 setParameters 方法,把用户传递的参数转换成 JDBC Statement 所需要的参数, 调用原生 JDBC 来执行语句。
最后由 ResultSetHandler 的 handleResultSets 方法将 JDBC 返回的 ResultSet 结果集转换成对象集,并逐级返回结果,完成一次 SQL 语句执行。
考察点
下面是需要注意的面试考察点。
- 首先要掌握 Spring 的核心概念 IoC、AOP 以及具体的实现方式。
- 要重点掌握 SpringContext 的初始化流程、Bean 的生命周期。
- 以应用为主,了解常用注解的作用和使用方式。
- 要了解一下 Spring Boot 相关的知识点,目前使用 Spring Boot 的项目越来越多,建议根据前面列出的知识点来学习。
- 要理解 Netty 的线程模型和消息处理的pipeline机制。
- 要理解 RPC 的交互流程及常用 RPC 框架的特点。
- 要了解 MyBatis 或者 Hibernate 这样的 ORM 框架解决了什么问题,了解框架的实现原理。
这一节课的内容比较多,前面提到的核心机制、核心流程,建议阅读源码加深理解。提供一个小技巧,在学习时可以通过断点调试的方式,结合给出的流程图来阅读源码。
加分项
- 本课时涉及考察点大多是以应用能力为主的,但是如果你阅读过源码,能突出对底层实现细节的掌握能力,一定会另面试官刮目相看。
- 除了应用之外,最好能理解框架的理念,例如理解 Spring 的控制反转与 AOP 思想。
- 能够知道框架最新版本的实现和发展方向,保持对新技术的兴趣和敏感。例如了解 Spring 的 Web Flux 响应式编程的实现与应用,关注 Spring Cloud 的应用等等。
- 如果能在应用的基础上有调优的经验,会让你在面试时更加突出。例如你有 Netty 的调优经验,知道要尽量减少 IO 线程占用,把可以后置的处理放到业务线程池中进行。
真题汇总
最后汇总一些相关的面试真题作为参考,以及需要注意的地方,如下所示。
第 1 题,除了说出 SSH 框架是 Struct+Spring+Hibernate,SSM 是指的 Spring MVC+Spring+MyBatis,另外要重点说一下 SpringMVC 和 Struts 的区别,以及 MyBatis 和 Hibernate 的区别。
第 4 题,要答出是通过 BeanFactoryPostProcessor 后置处理器进行的占位符替换,如果自定义处理,可以扩展 PropertyPlaceholderConfigurer 或 PropertySourcesPlaceholderConfigurer 来实现。
第 5 题,大致可以分为:从 HandlerMapping 中查找 Handler、执行 Handler、执行完成给适配器返回 ModelAndView、视图解析、返回视图,这些步骤。建议通过调试来阅读源码,补充细节、增加理解
第 6 题,可以从构造器循环依赖和 setter 循环依赖两部分来回答,构造器循环通过使用创建中 Bean 标示池,来判断是否产生了循环创建;setter 循环依赖通过引入 ObjectFactory 来解决。
第 7 题,题目给出的就是执行顺序。
第 8 题,可以从 Channel、Socket、EventLoop、ChannelPipeline 等对象展开介绍。
第 9 题,可以从下面几方面回答:
使用方式,HTTP 使用 Client 方式进行远程调用,RPC 使用动态代理的方式实现远程调用;
请求模型,HTTP 一般会经过 DNS 解析、4−7 层代理等中间环节,而 RPC 一般是点对点直连;
服务治理能力,RPC 提供更加丰富的服务治理功能,例如熔断、负载均衡等;
语言友好性,HTTP 对跨语言服务之间交互更加友好。
- 本文标题:Spring、rpc、orm常用框架总结
- 本文作者:形而上
- 创建时间:2021-10-21 13:31:00
- 本文链接:https://deepter.gitee.io/2021_10_21_spring_rpc_orm/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!