常用框架
2024-03-24 21:28:47 0 举报
AI智能生成
常用框架
作者其他创作
大纲/内容
Spring 是一款开源的轻量级 Java 开发框架,旨在提高开发人员的开发效率以及系统的可维护性。
Spring 框架指的都是 Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发。
IoC(Inversion of Control:控制反转)。
AOP(Aspect-Oriented Programming:面向切面编程)。
可以很方便地对数据库进行访问。
可以很方便地集成第三方组件(电子邮件,任务,调度,缓存等等)。
对单元测试支持比较好。
支持 RESTful Java 应用程序的开发。
应用
定义
Spring 包含了多个功能模块,其中最重要的是 Spring-Core(主要提供 IoC 依赖注入功能的支持) 模块, Spring 中的其他模块(比如 Spring MVC)的功能实现基本都需要依赖于该模块。
Spring
Spring MVC 是 Spring 中的一个很重要的模块,主要赋予 Spring 快速构建 MVC 架构的 Web 程序的能力。
Spring MVC
Spring Boot 是⼀个微服务框架,旨在简化 Spring 开发(减少配置文件,开箱即用!)。
Spring Boot 只是简化了配置,如果你需要构建 MVC 架构的 Web 程序,你还是需要使用 Spring MVC 作为 MVC 框架。
Spring Boot
SpringSpring MVCSpring Boot
Spring 框架的核心模块,也可以说是基础模块,主要提供 IoC 依赖注入功能的支持。
spring-core:Spring 框架基本的核心工具类。
spring-beans:提供对 bean 的创建、配置和管理等功能的支持。
spring-context:提供对国际化、事件传播、资源加载等功能的支持。
spring-expression:提供对表达式语言(Spring Expression Language) SpEL 的支持,只依赖于 core 模块,不依赖于其他模块,可以单独使用。
Core Container
spring-aspects:该模块为与 AspectJ 的集成提供支持。
spring-aop:提供了面向切面的编程实现。
spring-instrument:提供了为 JVM 添加代理(agent)的功能。
AOP
spring-jdbc:提供了对数据库访问的抽象 JDBC。
spring-tx:提供对事务的支持。
spring-orm:提供对 Hibernate、JPA、iBatis 等 ORM 框架的支持。
spring-oxm:提供一个抽象层支撑 OXM(Object-to-XML-Mapping),例如:JAXB、Castor、XMLBeans、JiBX 和 XStream 等。
spring-jms : 消息服务。自 Spring Framework 4.1 以后,它还提供了对 spring-messaging 模块的继承。
Data Access/Integration
spring-web:对 Web 功能的实现提供一些最基础的支持。
spring-webmvc:提供对 Spring MVC 的实现。
spring-websocket:提供了对 WebSocket 的支持,WebSocket 可以让客户端和服务端进行双向通信。
spring-webflux:提供对 WebFlux 的支持。WebFlux 是 Spring Framework 5.0 中引入的新的响应式框架。与 Spring MVC 不同,它不需要 Servlet API,是完全异步。
Spring Web
spring-messaging 是从 Spring4.0 开始新加入的一个模块,主要职责是为 Spring 框架集成一些基础的报文传送应用。
Messaging
Spring 团队提倡测试驱动开发(TDD)。有了控制反转 (IoC)的帮助,单元测试和集成测试变得更简单。
JUnit(单元测试框架)、TestNG(类似 JUnit)、Mockito(主要用来 Mock 对象)、PowerMock(解决 Mockito 的问题比如无法模拟 final,static,private 方法)等等。
支持
Spring Test
模块
IoC(Inversion of Control:控制反转/反转控制) 是一种设计思想(Java 开发领域对象的创建以及管理的问题),而不是一个具体的技术实现。
将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。
思想
最常见以及最合理的实现方式叫做依赖注入(Dependency Injection,简称 DI)。
原理:将对象的依赖关系由外部容器来管理和注入。
实现
控制:指的是对象创建(实例化、管理)的权力。
反转:控制权交给外部环境(Spring 框架、IoC 容器)。
控制反转
Spring 用来实现 IoC 的载体, IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。
IoC 容器类似工厂,当需要创建一个对象时,只需配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。
IoC 容器
Spring 框架中的 Bean 是否线程安全,取决于其作用域和状态。
以最常用的两种作用域 prototype 和 singleton 为例介绍。几乎所有场景的 Bean 作用域都是使用默认的 singleton ,重点关注 singleton 作用域即可。
每次获取都会创建一个新的 bean 实例,不存在资源竞争问题,所以不存在线程安全问题。
prototype 作用域
IoC 容器中只有唯一的 bean 实例,可能会存在资源竞争问题(取决于 Bean 是否有状态)。
指的是否含可变的成员变量的对象,含即有状态,不含即无状态。
Bean 状态
存在线程安全问题。
有状态
线程是安全的(比如 Dao、Service)。
无状态
singleton 作用域
在 Bean 中尽量避免定义可变的成员变量。
在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐)。
解决办法
线程安全
Bean 代指的就是那些被 IoC 容器所管理的对象。
@Component:通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用 @Component 注解标注。
@ComponentScan:定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中。
@Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。
@Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。
@Controller : 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。
注解
@Component 注解作用于类,而 @Bean 注解作用于方法。
@Component 通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中。@Bean 通常是在标有该注解的方法中定义产生这个 bean,@Bean 告诉了 Spring 这是某个类的实例,当我需要用它的时候还给我。
@Bean 注解比 @Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册 bean。比如当我们引用第三方库中的类需要装配到 Spring 容器时,则只能通过 @Bean 来实现。
@Component 和 @Bean
Spring 内置的 @Autowired 以及 JDK 内置的 @Resource 和 @Inject 都可以用于注入 Bean。
属于 Spring 内置的注解,默认注入方式为 byType(根据类型进行匹配),优先根据接口类型去匹配并注入 Bean (接口的实现类)。
当一个接口存在多个实现类时,byType 方式就无法正确注入对象,而 byName(根据名称进行匹配)可以正常注入。
隐式:通过实现类名(首字母小写)。
显式:通过 @Qualifier 注解,推荐。
byName 注入方式
支持在构造函数、方法、字段和参数上使用。
使用
@Autowired
属于 JDK 提供的注解,默认注入方式为 byName。如果无法通过名称匹配到对应的 Bean 的话,注入方式会变为byType。
有两个比较重要且日常开发常用的属性:name(名称)、type(类型)。
如果仅指定 name 属性则注入方式为 byName,如果仅指定 type 属性则注入方式为 byType,如果同时指定 name 和 type 属性(不建议这么做)则注入方式为 byType + byName。
主要用于字段和方法上的注入,不支持在构造函数或参数上使用。
@Resource
注入
singleton : IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
prototype : 每次获取都会创建一个新的 bean 实例。也就是说,连续 getBean() 两次,得到的是不同的 Bean 实例。
request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。
application/global-session (仅 Web 应用可用):每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。
<bean id=\"...\" class=\"...\" scope=\"singleton\"></bean>
xml 方式
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
配置
作用域
Bean 容器找到配置文件中 Spring Bean 的定义。
Bean 容器利用 Java Reflection API 创建一个 Bean 的实例。
如果涉及到一些属性值 利用 set()方法设置一些属性值。
如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入 Bean 的名字。
如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。
如果 Bean 实现了 BeanFactoryAware 接口,调用 setBeanFactory()方法,传入 BeanFactory对象的实例。
与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法。
如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessBeforeInitialization() 方法。
如果 Bean 实现了InitializingBean接口,执行afterPropertiesSet()方法。
如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessAfterInitialization() 方法
当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。
Spring Bean 生命周期
生命周期
BeanFactory:延迟注入(使用到某个 bean 的时候才会注入),相比于ApplicationContext 来说会占用更少的内存,程序启动速度更快。
ApplicationContext:容器启动的时候,不管你用没用到,一次性创建所有 bean 。BeanFactory 仅提供了最基本的依赖注入支持,ApplicationContext 扩展了 BeanFactory,除了有 BeanFactory 的功能还有额外更多功能。
ClassPathXmlApplication:把上下文文件当成类路径资源。
FileSystemXmlApplication:从文件系统中的 XML 文件载入上下文定义信息。
XmlWebApplicationContext:从 Web 系统中的 XML 文件载入上下文定义信息。
ApplicationContext 的三个实现类
BeanFactory 和 ApplicationContext
Spring Bean
Spring IoC
AOP (Aspect Orient Programming): 面向切面编程,AOP 是 OOP(面向对象编程)的一种延续,二者互补,并不对立。⾯向对象编程将程序抽象成各个层次的对象,⽽⾯向切⾯编程是将程序抽象成各个切⾯。
JDK Proxy 创建代理对象。
有接口实现
Cglib 生成一个被代理对象的子类来作为代理。
无接口实现
基于动态代理
原理
将横切关注点(如日志记录、事务管理、权限控制、接口限流、接口幂等等)从核心业务逻辑中分离出来,通过动态代理、字节码操作等技术,实现代码的复用和解耦,提高代码的可维护性和可扩展性。
作用
被通知的对象。
目标(Target)
向目标对象应用通知之后创建的代理对象。
代理(Proxy)
目标对象的所属类中,定义的所有方法均为连接点。
连接点(JoinPoint)
被切面拦截 / 增强的连接点(切入点一定是连接点,连接点不一定是切入点)。
切入点(Pointcut)
增强的逻辑 / 代码,也即拦截到目标对象的连接点之后要做的事情。
通知(Advice)
切入点(Pointcut) + 通知(Advice)。
切面(Aspect)
将通知应用到目标对象,进而生成代理对象的过程动作。
织入(Weaving)
横切关注点(cross-cutting concerns) :多个类或对象中的公共行为(如日志记录、事务管理、权限控制、接口限流、接口幂等等)。
概念
⼀个功能强⼤且成熟的AOP框架。
Before(前置通知):目标对象的方法调用之前触发。
After (后置通知):目标对象的方法调用之后触发。
AfterReturning(返回通知):目标对象的方法调用完成,在返回结果值之后触发。
AfterThrowing(异常通知):目标对象的方法运行中抛出 / 触发异常后触发。AfterReturning 和 AfterThrowing 两者互斥。
Around (环绕通知):编程式控制目标对象的方法调用。所有通知类型中可操作范围最大的一种。
通知类型
通常使用 @Order 注解直接定义切面顺序。
实现 Ordered 接口重写 getOrder 方法。
执行顺序
Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。
Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。
AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单。
切面比较少时,两者性能差异不大。但当切面太多时,最好选择 AspectJ ,它比 Spring AOP 快很多。
Spring AOP 和 AspectJ AOP
AspectJ
日志记录:自定义日志记录注解,利用 AOP,一行代码即可实现日志记录。
性能统计:利用 AOP 在目标方法的执行前后统计方法的执行时间,方便优化和分析。
事务管理:基于 AOP 实现的 @Transactional 注解可以让 Spring 为我们进行事务管理比如回滚异常操作,免去了重复的事务管理逻辑。
权限控制:利用 AOP 在目标方法执行前判断用户是否具备所需要的权限。例如,SpringSecurity 利用@PreAuthorize 注解一行代码即可自定义权限校验。
接口限流:利用 AOP 在目标方法执行前通过具体的限流算法和实现对请求进行限流处理。
缓存管理:利用 AOP 在目标方法执行前后进行缓存的读取和更新。
场景
Spring AOP
Spring 中的一个很重要的模块,主要赋予 Spring 快速构建 MVC 架构的 Web 程序的能力。
MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码。
整个 Web 应用几乎全部用 JSP 页面组成,只用少量的 JavaBean 来处理数据库连接、访问等操作。
JSP 即是控制层(Controller)又是表现层(View)。
控制逻辑和表现逻辑混杂在一起,导致代码重用率极低。
前端和后端相互依赖,难以进行测试维护并且开发效率极低。
问题
Model 1 时代
“Java Bean(Model)+ JSP(View)+ Servlet(Controller)”开发模式,这就是早期的 JavaWeb MVC 开发模式。
抽象和封装程度还远远不够。
开发时不可避免地会重复造轮子,这就大大降低了程序的可维护性和复用性。
同时期,为了解决这些问题,很多 JavaWeb 开发相关的 MVC 框架应运而生比如 Struts2,但是 Struts2 比较笨重。
Model 2 时代
随着 Spring 轻量级开发框架的流行,Spring 生态圈出现了 Spring MVC 框架, Spring MVC 是当前最优秀的 MVC 框架。
MVC 是一种设计模式,Spring MVC 是一款很优秀的 MVC 框架。
一般把后端项目分为 Service 层(处理业务)、Dao 层(数据库操作)、Entity 层(实体类)、Controller 层(控制层,返回数据给前台页面)。
Spring MVC 时代
发展
DispatcherServlet:核心的中央处理器,负责接收请求、分发,并给予客户端响应。
HandlerMapping:处理器映射器,根据 URL 去匹配查找能处理的 Handler ,并会将请求涉及到的拦截器和 Handler 一起封装。
HandlerAdapter:处理器适配器,根据 HandlerMapping 找到的 Handler ,适配执行对应的 Handler。
Handler:请求处理器,处理实际请求的处理器。
ViewResolver:视图解析器,根据 Handler 返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给 DispatcherServlet 响应客户端。
View:视图,提供了很多的 View 视图类型的⽀持,包括:JstlView、FreemarkerView、PdfView等。最常⽤的视图就是 Jsp。
核心组件
Spring MVC 中,所有的拦截器都需要实现 HandlerInterceptor 接⼝,该接⼝包含如下三个⽅法:preHandle()、postHandle()、afterCompletion()。
拦截器
客户端(浏览器)发送请求, DispatcherServlet 拦截请求。
DispatcherServlet 根据请求信息调用 HandlerMapping 。HandlerMapping 根据 URL 去匹配查找能处理的 Handler(Controller 控制器) ,并会将请求涉及到的拦截器和 Handler 一起封装。
DispatcherServlet 调用 HandlerAdapter 适配器执行 Handler 。
Handler 完成对用户请求的处理后,会返回一个 ModelAndView 对象给 DispatcherServlet。ModelAndView 包含了数据模型以及相应的视图的信息。Model 是返回的数据对象,View 是个逻辑上的 View。
ViewResolver 会根据逻辑 View 查找实际的 View。
DispaterServlet 把返回的 Model 传给 View(视图渲染)。
把 View 返回给请求者(浏览器)。
工作原理
推荐使用注解的方式统一异常处理,具体会使用到 @ControllerAdvice + @ExceptionHandler 这两个注解 。
ExceptionHandlerMethodResolver 中 getMappedMethod 方法决定了异常具体被哪个被 @ExceptionHandler 注解修饰的方法处理异常。getMappedMethod()会首先找到可以匹配处理异常的所有方法信息,然后对其进行从小到大的排序,最后取最小的那一个匹配的方法(即匹配度最高的那个)。
统一异常处理
工厂设计模式 : Spring 使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
代理设计模式 : Spring AOP 功能的实现。
单例设计模式 : Spring 中的 Bean 默认都是单例的。
模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
包装器设计模式 : 让我们可以根据客户的需求能够动态切换不同的数据源。
观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
适配器模式 : Spring AOP 的增强或通知(Advice)使用到了适配器模式、Spring MVC 中也是用到了适配器模式适配 Controller。
设计模式
编程式事务:在代码中硬编码(在分布式系统中推荐使用) 。通过 TransactionTemplate 或者 TransactionManager 手动管理事务,事务范围过大会出现事务未提交导致超时,因此事务要比锁的粒度更小。
声明式事务:在 XML 配置文件中配置或者直接基于注解(单体应用或者简单业务系统推荐使用)。实际是通过 AOP 实现(基于@Transactional 的全注解方式使用最多)。
管理方式
(平台)事务管理器,Spring 事务策略的核心。
Spring 并不直接管理事务,而是提供了多种事务管理器 。Spring 事务管理器的接口是:PlatformTransactionManager 。
JDBC(DataSourceTransactionManager)
Hibernate(HibernateTransactionManager)
JPA(JpaTransactionManager)
通过此接口,Spring 提供了各个平台对应的事务管理器接口
PlatformTransactionManager
TransactionDefinition:事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)。
TransactionStatus:事务运行状态。
管理接口
使用后端数据库默认的隔离级别,MySQL 默认 REPEATABLE_READ,Oracle 默认 READ_COMMITTED。
ISOLATION_DEFAULT
最低的隔离级别,使用很少,因为它允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
ISOLATION_READ_UNCOMMITTED
允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
ISOLATION_READ_COMMITTED
对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
ISOLATION_REPEATABLE_READ
最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,可以防止脏读、不可重复读以及幻读。(严重影响性能)
ISOLATION_SERIALIZABLE
隔离级别
事务传播行为是为了解决业务层方法之间互相调用的事务问题。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。
如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。(默认)
PROPAGATION_REQUIRED
创建一个新的事务,如果当前存在事务,则把当前事务挂起。事务相互独立,互不干扰。
PROPAGATION_REQUIRES_NEW
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行。如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
PROPAGATION_NESTED
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(使用少)
PROPAGATION_MANDATORY
如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。(无回滚)
PROPAGATION_SUPPORTS
以非事务方式运行,如果当前存在事务,则把当前事务挂起。(无回滚)
PROPAGATION_NOT_SUPPORTED
以非事务方式运行,如果当前存在事务,则抛出异常。(无回滚)
PROPAGATION_NEVER
传播行为
指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。
在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒,默认值为-1,这表示事务的超时时间取决于底层事务系统或者没有超时时间。
超时属性
对于只有读取数据查询的事务,可以指定事务类型为 readonly,即只读事务。
只读事务不涉及数据的修改,数据库会提供一些优化手段,适合用在有多条数据库查询操作的方法中。
如果不加 Transactional,每条 sql 会开启一个单独的事务,中间被其它事务改了数据,都会实时读取到最新值。
如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持 SQL 执行期间的读一致性。
如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询 SQL 必须保证整体的读一致性,此时,应该启用事务支持。
只读属性
默认情况下,事务只有遇到运行期异常(RuntimeException 的子类)时才会回滚,Error 也会导致事务回滚,但在遇到检查型(Checked)异常时不会回滚。
回滚规则
方法:推荐将注解使用于方法上,不过需要注意的是:该注解只能应用到 public 方法上,否则不生效。
类:如果这个注解使用在类上的话,表明该注解对该类中所有的 public 方法都生效。
接口:不推荐在接口上使用。
作用范围
事务的传播行为,默认值为 REQUIRED,可选的值在 TransactionDefinition 中。
propagation
事务的隔离级别,默认值采用 DEFAULT,可选的值在 TransactionDefinition 中。
isolation
事务的超时时间,默认值为-1(不会超时)。如果超过该时间限制但事务还没有完成,则自动回滚事务。
timeout
指定事务是否为只读事务,默认值为 false。
readOnly
用于指定能够触发事务回滚的异常类型,并且可以指定多个异常类型。
rollbackFor
配置参数
基于 AOP 实现的,AOP 又是使用动态代理实现的。
@Transactional 注解只有作用到 public 方法上事务才生效,不推荐在接口上使用。
无法避免时则使用 AspectJ 取代 Spring AOP 代理。
避免同一个类中调用 @Transactional 注解的方法,这样会导致事务失效。
正确的设置 @Transactional 的 rollbackFor 和 propagation 属性,否则事务可能会回滚失败。
被 @Transactional 注解的方法所在的类必须被 Spring 管理,否则不生效。
底层使用的数据库必须支持事务机制,否则不生效。
注意
@Transactional
事务
一种规范,通过注解或 xml 描述对象关系表的映射关系,并将运行期间的实体对象持久化到数据库中。
ORM 映射元数据:支持 XML 和 JDK5.0 注解两种元数据形式。
API:用来操作实体对象,可以进行 CURD 操作,从 JDBC 和 SQL 中解放开发者。
查询语言:通过面向对象而非面向数据库的查询语言查询数据,避免程序与 SQL 紧耦合。
技术
JPA(Java Persistence API)
static String transient1;
静态
final String transient2 = \"Satish\";
常量
transient String transient3;
关键字
@TransientString transient4;
非持久化字段
@CreatedDate:表示该字段为创建时间字段,在这个实体被 insert 的时候,会设置值。
@CreatedBy:表示该字段为创建人,在这个实体被 insert 的时候,会设置值。
@LastModifiedDate、@LastModifiedBy 同理。
审计
@OneToOne : 一对一。
@ManyToMany:多对多。
@OneToMany : 一对多。
@ManyToOne:多对一。
利用 @ManyToOne 和 @OneToMany 也可以表达多对多的关联关系。
实体关联关系
Spring Data JPA
Spring Security 是基于 Spring 的身份认证(Authentication)和用户授权(Authorization)框架,提供了一套 Web 应用安全性的完整解决方案。
使用了 Servlet 过滤器、IOC 和 AOP 等。
核心技术
验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。
身份认证(Authentication)
验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所有的权限是不同的。
用户授权(Authorization)
permitAll():无条件允许任何形式访问,不管你登录还是没有登录。
anonymous():允许匿名访问,也就是没有登录才可以访问。
denyAll():无条件决绝任何形式的访问。
authenticated():只允许已认证的用户访问。
fullyAuthenticated():只允许已经登录或者通过 remember-me 登录的用户访问。
hasRole(String) : 只允许指定的角色访问。
hasAnyRole(String) : 指定一个或者多个角色,满足其一的用户即可访问。
hasAuthority(String):只允许具有指定权限的用户访问。
hasAnyAuthority(String):指定一个或者多个权限,满足其一的用户即可访问。
hasIpAddress(String) : 只允许指定 ip 的用户访问。
控制请求权限
Spring Security 提供了多种加密算法的实现,开箱即用,非常方便。
这些加密算法实现类的父类是 PasswordEncoder ,如果你想要自己实现一个加密算法的话,也需要继承 PasswordEncoder。
官方推荐使用基于 BCrypt 强哈希函数的加密算法实现类。
通过 DelegatingPasswordEncoder 兼容多种不同的密码加密方案,以适应不同的业务需求。
DelegatingPasswordEncoder 其实就是一个代理类,并非是一种全新的加密算法,它做的事情就是代理上面提到的加密算法实现类。
在 Spring Security 5.0 之后,默认就是基于 DelegatingPasswordEncoder 进行密码加密的。
优雅更换加密算法
加密
Spring Security
Spring Boot 是⼀个微服务框架,旨在简化 Spring 开发(减少配置文件,开箱即用!)。
开发基于 Spring 的应⽤程序很容易。
Spring Boot 项⽬所需的开发或⼯程时间明显减少,通常会提⾼整体⽣产⼒。
Spring Boot 不需要编写⼤量样板代码、XML 配置和注释。
Spring 引导应⽤程序可以很容易地与 Spring ⽣态系统集成,如 SpringJDBC、Spring ORM、Spring Data、Spring Security 等。
Spring Boot 遵循“固执⼰⻅的默认配置”,以减少开发⼯作(默认配置可以修改)。
Spring Boot 应⽤程序提供嵌⼊式 HTTP 服务器,如 Tomcat 和 Jetty,可以轻松地开发和测试 web 应⽤程序。
Spring Boot 提供命令⾏接⼝(CLI)⼯具,⽤于开发和测试 Spring Boot 应⽤程序,如 Java 或 Groovy。
Spring Boot 提供了多种插件,可以使⽤内置⼯具(如 Maven 和 Gradle)开发和测试 Spring Boot 应⽤程序。
优点
⼀种预配置的模块,它封装了特定功能的依赖项和配置,开发者只需引⼊相关的 Starter 依赖,⽆需⼿动配置⼤量的参数和依赖项。
管理了相关功能的依赖项,包括其他 Starter 和第三⽅库,确保它们能够良好地协同⼯作,避免版本冲突和依赖问题。
应⽤可以通过引⼊不同的 Starter 来实现模块化的开发。每个 Starter 都关注⼀个特定的功能领域,如数据库访问、消息队列、Web 开发等。
开发者可以创建⾃定义的 Starter,以便在项⽬中共享和重⽤特定功能的配置和依赖项。
Starter
Servlet Ver 4.0
Tomcat 9.0(默认嵌入)
Servlet Ver 3.1
Jetty 9.4
Servlet Ver 4.0
Undertow 2.0
只需要修改 pom.xml(Maven)或者 build.gradle(Gradle)就可以了。
切换
内嵌 Servlet 容器
启动流程
通过注解或者一些简单的配置(引入 Starters)就能在 Spring Boot 的帮助下实现某块功能。
@EnableAutoConfiguration 是启动⾃动配置的关键。
Spring Boot 通过 @EnableAutoConfiguration 注解开启⾃动配置,加载 spring.factories 中注册的各种 AutoConfiguration 类。当某个 AutoConfiguration 类满⾜其注解 @Conditional 指定的⽣效条件(Starters 提供的依赖、配置或 Spring 容器中是否存在某个 Bean 等)时,实例化该 AutoConfiguration 类中定义的 Bean(组件等),并注⼊ Spring 容器,就可以完成依赖框架的⾃动配置。
过程
自动装配
application.properties 和 application.yml
YAML 是⼀种⼈类可读的数据序列化语⾔。它通常⽤于配置⽂件。
相⽐于 Properties 配置的⽅式,YAML 配置的⽅式更加直观清晰,简介明了,有层次感。
不⽀持 @PropertySource 注解导⼊⾃定义的 YAML 配置。
缺点
YAML
使⽤ @Value(\"${property}\") 读取⽐较简单的配置信息
@value 这种⽅式是不被推荐的。
通过 @value 读取⽐较简单的配置信息
通过 @ConfigurationProperties 读取并与 bean 绑定。
通过 @ConfigurationProperties 读取并校验。
@PropertySource 读取指定的 properties ⽂件
读取
bootstrap.properties --> bootstrap.yml --> application.properties --> application.yml
加载优先级
命令行参数。
java:comp/env的JNDI属性(当前J2EE应用的环境)。
JAVA系统的环境属性。
操作系统的环境变量。
JAR包外部的 application-xxx.properties 或 application-xxx.yml 配置文件。
JAR包内部的 application-xxx.properties 或 application-xxx.yml 配置文件。
JAR包外部的 application.properties 或 application.yml 配置文件。
JAR包内部的 application.properties 或 application.yml 配置文件。
@Configuration 注解类上的 @PropertySource 指定的配置文件。
通过 SpringApplication.setDefaultProperties 指定的默认属性。
配置文件优先级(由高到低)
配置文件
实现 ApplicationRunner 接⼝ 或 实现 CommandLineRunner接⼝。
设置初始化数据
SpringBoot
⽤于标识主应⽤程序类,通常位于项⽬的顶级包中。它包含了 @Configuration、@EnableAutoConfiguration 和 @ComponentScan。
@EnableAutoConfiguration:启用 SpringBoot 的自动配置机制。
@ComponentScan:扫描被 @Component(@Repository,@Service,@Controller)注解的 bean,注解默认会扫描该类所在的包下所有的类。
@Configuration:允许在 Spring 上下文中注册额外的 bean 或导入其他配置类。
@SpringBootApplication
⽤于标识类作为 Spring MVC 的控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。
单独使用 @Controller 而不加 @ResponseBody 一般用在返回一个视图的情况,属于比较传统的 Spring MVC 的应用,对应于前后端不分离的情况。
@Controller + @ResponseBody 返回 JSON 或 XML 形式数据。
@Controller
类似于 @Controller,但它是专⻔⽤于 RESTful Web 服务的。它包含了@Controller 和 @ResponseBody。
@RestController
⽤于将 HTTP 请求映射到 Controller 的处理⽅法。可以⽤在类级别和⽅法级别。
可以使用 @[Get/Post/Put/Patch/Delete]Mapping 注解平替对应的请求方法。
@RequestMapping
用于获取路径参数。
@PathVariable
用于获取查询参数。
@RequestParam
@RequestBody
⽤于⾃动注⼊ Spring 容器中的 Bean,可以⽤在构造⽅法、字段、Setter ⽅法上。
⽤于标识类作为服务层的 Bean,主要涉及一些复杂的逻辑,需要用到 Dao 层。
@Service
⽤于标识类作为数据访问层的 Bean,通常⽤于与数据库交互。
@Repository
通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用 @Component 注解标注。
@Component
⽤于定义配置类,类中可能包含⼀些 @Bean 注解⽤于定义 Bean。可以使用 @Component 注解替代。
@Configuration
⽤于启⽤ Spring Boot 的⾃动配置机制,根据项⽬的依赖和配置⾃动配置 Spring 应⽤程序。
@EnableAutoConfiguration
⽤于从属性⽂件或配置中读取值,将值注⼊到成员变量中。
@Value
与 @Autowired ⼀起使⽤,指定注⼊时使⽤的 Bean 名称。
@Qualifier
⽤于将配置⽂件中的属性映射到 Java Bean。
@ConfigurationProperties
读取指定 properties 文件。
@PropertySource
⽤于定义不同环境下的配置,可以标识在类或⽅法上。
@Profile
⽤于将⽅法标记为异步执⾏。
@Async
参数加 @Valid 注解,如果验证失败,它将抛出 MethodArgumentNotValidException。
@Valid
类加 @Validated 注解,可以让 Spring 去校验方法参数。
@Validated
字段验证注解
@ControllerAdvice:注解定义全局异常处理类。
@ExceptionHandler:注解声明异常处理方法。
全局处理 Controller 层异常
声明一个类对应一个数据库实体。
@Entity
设置表名。
@Table
声明一个字段为主键。
@Id
定义主键的生成策略。
@GeneratedValue
声明一个主键策略。
@GenericGenerator
声明字段。
@Column
声明不需要与数据库映射的字段,在保存的时候不需要保存进数据库 。
@Transient
声明某个字段为大字段。
@Lob
指定 Lob 类型数据的获取策略( fetch ), FetchType.EAGER 表示非延迟加载,而 FetchType.LAZY 表示延迟加载 。
@Basic
声明使用枚举类型的字段。
@Enumerated
@CreatedDate、@CreatedBy、@LastModifiedDate、@LastModifiedBy
只要继承了 AbstractAuditBase 的类都会默认加上四个字段。
开启 JPA 审计功能。
@EnableJpaAuditing
审计功能
提示 JPA 该操作是修改操作,注意还要配合 @Transactional 注解使用。
@Modifying
JPA相关
声明开启事务。
作用于类:当把 @Transactional 注解放在类上时,表示所有该类的 public 方法都配置相同的事务属性信息。
作用于方法:当类配置了 @Transactional,方法也配置了 @Transactional,方法的事务会覆盖类的事务配置信息。
作用在类上用于过滤掉特定字段不返回或者不解析。
@JsonIgnoreProperties
作用在属性上用于过滤掉特定字段不返回或者不解析。
@JsonIgnore
用来格式化 json 数据。
@JsonFormat
用来扁平化对象。
@JsonUnwrapped
Json 数据处理
一般作用于测试类上, 用于声明生效的 Spring 配置文件。
@ActiveProfiles
声明一个方法为测试方法。
@Test
被声明的测试方法的数据会回滚,避免污染测试数据。
pring Security 提供的,用来模拟一个真实用户,并且可以赋予权限。
@WithMockUser
测试相关
Spring & SpringBoot 常用注解
⼀个应⽤⼴泛的优秀的 ORM 框架,已经成了 JavaWeb 世界近乎标配的部分,这个框架具有强⼤的灵活性。
JDBC 问题:数据库连接创建、释放频繁造成系统资源浪费从⽽影响系统性能,如果使⽤数据库连接池可解决此问题。MyBatis 解决:在 mybatis-config.xml 中配置数据连接池,使⽤连接池管理数据库连接。
JDBC 问题:Sql 语句写在代码中造成代码不易维护,实际应⽤ sql 变化的可能较⼤,sql 变动需要改变 java 代码。MyBatis 解决:将 Sql 语句配置在 XXXXmapper.xml ⽂件中与 java 代码分离。
JDBC 问题:向 sql 语句传参数麻烦,因为 sql 语句的 where 条件不⼀定,可能多也可能少,占位符需要和参数⼀⼀对应。MyBatis 解决:Mybatis ⾃动将 java 对象映射⾄ sql 语句。
JDBC 问题:对结果集解析麻烦,sql 变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成 pojo 对象解析⽐较⽅便。MyBatis 解决:Mybatis ⾃动将 sql 执⾏结果映射⾄ java 对象。
JDBC 和 MyBatis
MyBatis 基于四大组件(核心对象)提供了简单易⽤的插件扩展机制。
MyBatis ⽀持⽤插件对四⼤核⼼对象进⾏拦截,⽤来增强核⼼对象的功能,增强功能本质上是借助于底层的动态代理实现的。
处理SQL的参数对象。
ParameterHandler
处理SQL的返回结果集。
ResultSetHandler
数据库的处理对象,⽤于执⾏SQL语句。
StatementHandler
MyBatis 的执⾏器,⽤于执⾏增删改查操作。
Executor
核心对象
Mybatis 的插件借助于 JDK 动态代理和责任链设计模式进⾏对拦截的处理。
使⽤动态代理对⽬标对象进⾏包装,达到拦截的⽬的。
作⽤于 Mybatis 的作⽤域对象之上。
插件原理
Intercept ⽅法,插件的核⼼⽅法。
plugin ⽅法,⽣成 target 的代理对象。
setProperties ⽅法,传递插件所需参数。
插件接⼝
插件
Mybatis 会把每个 SQL 标签封装成 SqlSource 对象,XML ⽂件中的每⼀个 SQL 标签就对应⼀个 MappedStatement 对象。
id:全限定类名 + ⽅法名组成的 ID。
sqlSource:当前 SQL 标签对应的 SqlSource 对象。
MappedStatement 对象属性
Dao 接⼝的⼯作原理是 JDK 动态代理,Mybatis 运⾏时会使⽤ JDK 动态代理为 Dao 接⼝⽣成代理 proxy 对象,代理对象 proxy 会拦截接⼝⽅法,转⽽执⾏ MappedStatement 所代表的 sql,然后将 sql 执⾏结果返回。
Mybatis 的 Dao 接口可以有多个重载方法,但是多个接口对应的映射必须只有一个,否则启动会报错。
仅有一个无参方法和一个有参方法。
多个有参方法时,参数数量必须一致。且使用相同的 @Param ,或者使用 param1 这种。
条件
重载
不同 namespace 可以重复,无 namespace 则不可以重复。
不同 xml 中的 id 重复问题
通过注解绑定:在接⼝的⽅法上⾯加上 @Select、@Update 等注解⾥⾯包含 Sql 语句来绑定( Sql 语句⽐较简单的时候,推荐注解绑定)。
通过 xml ⾥⾯写 Sql 来绑定:指定 xml 映射⽂件⾥⾯的 namespace 必须为接⼝的全路径名(Sql 语句⽐较复杂的时候,推荐xml绑定)。
接口绑定
MyBatis 将所有 xml 配置信息都封装到 All-In-One 重量级对象 Configuration 内部。
ParameterMap 对象,其每个子元素会被解析为 ParameterMapping 对象。
<parameterMap>
ResultMap 对象,其每个子元素会被解析为 ResultMapping 对象。
<resultMap>
MappedStatement 对象,标签内的 sql 会被解析为 BoundSql 对象。
<select>、<insert>、<update>、<delete>
XML 和 内部数据结构映射
Dao 和 XML
使⽤标签(<resultMap>),逐⼀定义列名和对象属性名之间的映射关系。
使⽤ sql 列的别名(AS)功能,将列别名书写为对象属性名。
直接表示返回类型,自动提交。
resultType
对外部 ResultMap 的引⽤,手动提交
resultMap
resultType 跟 resultMap 不能同时存在。
映射类型
单独发送一个 sql 去查询关联对象,赋给主对象,然后返回主对象。
使用嵌套查询,嵌套查询的含义为使用 join 查询,一部分列是 A 对象的属性值,另外一部分列是关联对象 B 的属性值。
关联对象查询方式
自定义一个 TypeHandler ,实现 TypeHandler 的 setParameter() 和 getResult() 接口方法。
映射枚举类
结果映射
Mybatis 动态 sql 可以让我们在 Xml 映射⽂件内,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能。
使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼接 sql,以此来完成动态 sql 的功能。
<if></if>
<foreach></foreach>
<bind/>
标签
解释器模式:初始化过程中构建出抽象语法树,请求处理时根据参数对象解释语法树,⽣成 sql 语句。
⼯⼚模式:为动态标签的处理⽅式创建⼯⼚类(SqlTagHandlerFactory),根据标签名称获取对应的处理⽅式。
策略模式:将动态标签处理⽅式抽象为接⼝,针对不同标签有相应的实现类。解释抽象语法树时, 定义统⼀的解释流程,再调⽤标签对应的处理⽅式完成解释中的各个⼦环节。
动态 Sql
SqlSession 级别的缓存。在操作数据库时需要构造 sqlSession 对象,在对象中有⼀个数据结构⽤于存储缓存数据。
不同的 sqlSession 之间的缓存数据区域是互相不影响的。
一级缓存
mapper 级别的缓存。多个 SqlSession 去操作同⼀个 Mapper 的 sql 语句,可共⽤⼆级缓存,⼆级缓存是跨 SqlSession 的。⼆级缓存的作⽤范围更⼤。
MyBatis 和 Spring 整合开发后,如果没有事务,⼀级缓存是没有意义的。
每⼀个 namespace 的 mapper 都有⼀个⼆级缓存区域,如果相同两个 mapper 执⾏ sql 查询到数据将存在相同的⼆级缓存区域中。
二级缓存
缓存
SimpleExecutor: 每执行一次 update 或 select,就开启一个 Statement 对象,用完立刻关闭 Statement 对象。
BatchExecutor:执行 update(没有 select,JDBC 批处理不支持 select),将所有 sql 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个 Statement 对象,每个 Statement 对象都是 addBatch()完毕后,等待逐一执行 executeBatch()批处理。
分类
Executor 的这些特点,都严格限制在 SqlSession 生命周期范围内。
在 MyBatis 配置文件中,可以指定默认的 ExecutorType 执行器类型。
可以手动给 DefaultSqlSessionFactory 的创建 SqlSession 的方法传递 ExecutorType 类型参数。
指定
执行器
在 MyBatis 初始化过程中,会加载配置⽂件(mybatis-config.xml)、 映射配置⽂件(Mapper.xml)以及 Mapper 接⼝中的注解信息,解析后的配置信息会形成相应的对象并全部保存到 Configuration 对象中,并创建 DefaultSqlSessionFactory 供 Sql 执⾏过程创建出顶层接⼝ SqlSession 供给⽤户进⾏操作。
SqlSessionFactoryBuilder
XMLConfigBuilder
Configuration
解析 mybatis-config.xml 配置⽂件
XMLMapperBuilder::parse()
XMLStatementBuilder::parseStatementNode ()
XMLLanguageDriver
SqlSource
MappedStatement
解析 Mapper.xml 映射配置⽂件
MapperRegistry
MapperAnnotationBuilder::parse()
解析 Mapper 接⼝中的注解
初始化过程
MyBatis 中的延迟加载,也称为懒加载,是指在进⾏表的关联查询时,按照设置延迟规则推迟对关联对象的 select 查询。
关联对象的查询与主加载对象的查询必须是分别进⾏的 select 语句,不能是使⽤多表连接所进⾏的 select 查询。
要求
直接加载:执⾏完对主加载对象的 select 语句,⻢上执⾏对关联对象的 select 查询。
侵⼊式延迟:执⾏对主加载对象的查询时,不会执⾏对关联对象的查询。但当要访问主加载对象的详情属性时,就会⻢上执⾏关联对象的 select 查询。
深度延迟:执⾏对主加载对象的查询时,不会执⾏对关联对象的查询。访问主加载对象的详情时也不会执⾏关联对象的select查询。只有当真正访问关联对象的详情时,才会执⾏对关联对象的 select 查询。
加载时机
延迟加载
使用 MyBatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql,然后重写 sql,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。
MyBatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页。
可以在 sql 内直接书写带有物理分页的参数来完成物理分页功能。
可以使用分页插件来完成物理分页。
方式
分页
Mybatis 和 Hibernate不同,它不完全是⼀个 ORM 框架,因为 MyBatis 需要程序员⾃⼰编写 Sql 语句。
Mybatis 直接编写原⽣态 sql,可以严格控制 sql 执⾏性能,灵活度⾼,⾮常适合对关系数据模型要求不⾼的软件开发。
Hibernate 对象/关系映射能⼒强,数据库⽆关性好,对于关系模型要求⾼的软件,如果⽤ Hibernate 开发可以节省很多代码,提⾼效率。
Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。
MyBatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 映射工具。
MyBatis 和 Hibernate
#{} 是 sql 的参数占位符,MyBatis 会将 sql 中的 #{} 替换为 ? 号,在 sql 执行前会使用 PreparedStatement 的参数设置方法,按序给 sql 的 ? 号占位符设置参数值。
${} 是 Properties 文件中的变量占位符,它可以用于标签属性值和 sql 内部,属于原样文本替换,可以替换任意内容。
#{} 和 ${}
区别
MyBatis
一个Java NIO技术的开源异步事件驱动的网络编程框架,用于快速开发可维护的高性能协议服务器和客户端。
高并发:Netty 是一款基于 NIO(Nonblocking IO,非阻塞IO)开发的网络通信框架,对比于 BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高。
传输快:Netty 的传输依赖于零拷贝特性,尽量减少不必要的内存拷贝,实现了更高效率的传输。
封装好:Netty 封装了 NIO 操作的很多细节,提供了易于使用调用接口。
特点
使用简单:封装了 NIO 的很多细节,使用更简单。
功能强大:预置了多种编解码功能,支持多种主流协议。
定制能力强:可以通过 ChannelHandler 对通信框架进行灵活地扩展。
性能高:通过与其他业界主流的 NIO 框架对比,Netty 的综合性能最优。
稳定:Netty 修复了已经发现的所有 NIO 的 bug,让开发人员可以专注于业务本身。
社区活跃:Netty 是活跃的开源项目,版本迭代周期短,bug 修复速度快。
优势
阿里分布式服务框架 Dubbo 的 RPC 框架。
RocketMQ 也是使用 Netty 作为通讯的基础。
Hadoop 的高性能通信和序列化组件 Avro 的 RPC 框架。
开源集群运算框架 Spark。
分布式计算框架 Storm。
IO 线程模型:同步非阻塞,用最少的资源做更多的事。
内存零拷贝:尽量减少不必要的内存拷贝,实现了更高效率的传输。
内存池设计:申请的内存可以重用,主要指直接内存。内部实现是用一颗二叉查找树管理内存分配情况。默认堆外内存,开启池化管理。
串形化处理读写:避免使用锁带来的性能开销。
高性能序列化协议:支持 protobuf 等高性能序列化协议。
高性能
Netty 通过 Reactor 模型基于多路复用器接收并处理用户请求,内部实现了两个线程池,boss 线程池和 work 线程池。
负责处理请求的 accept 事件,当接收到 accept 事件的请求时,把对应的 socket 封装到一个 NioSocketChannel 中,并交给 work 线程池。
boss 线程池
负责请求的 read 和 write 事件,由对应的 Handler 处理。
work 线程池
所有 I/O 操作都由一个线程完成,即多路复用、事件分发和处理都是在一个 Reactor 线程上完成的。
单线程模型
Acceptor线程:只负责监听服务端,接收客户端的 TCP 连接请求。
NIO 线程池:负责网络 IO 的操作,即消息的读取、解码、编码和发送。
1 个 NIO 线程可以同时处理 N 条链路,但是1个链路只对应1个 NIO 线程,这是为了防止发生并发操作问题。
在并发百万客户端连接或需要安全认证时,一个Acceptor 线程可能会存在性能不足问题。
多线程模型
多个 Acceptor 线程用于绑定监听端口,接收客户端连接,将 SocketChannel 从主线程池的 Reactor 线程的多路复用器上移除,重新注册到 Sub 线程池的线程上,用于处理 I/O 的读写等操作,从而保证 mainReactor 只负责接入认证、握手等操作。
主从多线程模型
线程模型
TCP 是以流的方式来处理数据,一个完整的包可能会被 TCP 拆分成多个包进行发送,也可能把小的封装成一个大的数据包发送。
应用程序写入的字节大小大于套接字发送缓冲区的大小,会发生拆包现象。
应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包现象。
原因
消息定长:FixedLengthFrameDecoder 类。
自定义分隔符类 :DelimiterBasedFrameDecoder 类。
将消息分为消息头和消息体:LengthFieldBasedFrameDecoder 类。分为有头部的拆包与粘包、长度字段在前且有头部的拆包与粘包、多扩展头部的拆包与粘包。
解决
TCP 粘包/拆包
Netty 的接收和发送 ByteBuffer 采用 DIRECT BUFFERS,使用堆外直接内存进行 Socket 读写,不需要进行字节缓冲区的二次拷贝。
Netty 提供了组合 Buffer 对象,可以聚合多个 ByteBuffer 对象,用户可以像操作一个 Buffer 那样方便的对组合 Buffer 进行操作,避免了传统通过内存拷贝的方式将几个小 Buffer 合并成一个大的 Buffer。
Netty 的文件传输采用了 transferTo 方法,它可以直接将文件缓冲区的数据发送到目标 Channel,避免了传统通过循环 write 方式导致的内存拷贝问题。
零拷贝
Bootstrap,ServerBootstrap:客户端和服务端的启动引导程序。
Channel:数据传送的通道,Netty 网络操作抽象类,它除了包括基本的 I/O 操作,如 bind、connect、read、write 等。
ChannelHandler:Channel 中有相关事件发生的时候会触发执行,充当了所有处理入站和出站数据的逻辑容器。ChannelHandler 主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。
ChannelPipeline:为 ChannelHandler 链提供了容器,当 channel 创建时,就会被自动分配到它专属的 ChannelPipeline,这个关联是永久性的。
EventLoop:Io 异步执行的任务队列和线程池,主要是配合 Channel 处理 I/O 操作,用来处理连接的生命周期中所发生的事情。
ChannelFuture:Io 任务执行以后未来的返回结果。Netty 框架中所有的 I/O 操作都为异步的,因此我们需要 ChannelFuture 的 addListener()注册一个 ChannelFutureListener 监听事件,当操作执行成功或者失败时,监听就会自动触发返回结果。
ChannelHandlerContext:ChannelHandler相关联的上下文信息对象。
重要组件
直接写入 Channel 中,消息从 ChannelPipeline 当中尾部开始移动。
写入和 ChannelHandler 绑定的 ChannelHandlerContext 中,消息从 ChannelPipeline 中的下一个 ChannelHandler 中移动。
发送消息方式
Netty 默认是 CPU 处理器数的两倍,bind 完之后启动。
默认线程数
序列化(编码)是将对象序列化为二进制形式(字节数组),主要用于网络传输、数据持久化等。
反序列化(解码)则是将从网络、磁盘等读取的字节数组还原成原始对象,主要用于网络传输对象的解码,以便完成远程调用。
序列化后的码流大小(网络带宽的占用)、序列化的性能(CPU资源占用);是否支持跨语言(异构系统的对接和开发语言切换)。
性能因素
优点:人机可读性好,可指定元素或特性的名称。
缺点:序列化数据只包含数据本身以及类的结构,不包括类型标识和程序集信息;只能序列化公共属性和字段;不能序列化方法;文件庞大,文件格式复杂,传输占带宽。
适用场景:当做配置文件存储数据,实时数据转换。
XML
一种轻量级的数据交换格式。
优点:兼容性高、数据格式比较简单,易于读写、序列化后数据较小,可扩展性好,兼容性好、与 XML 相比,其协议比较简单,解析速度比较快。
缺点:数据的描述性比 XML 差、不适合性能要求为ms级别的情况、额外空间开销比较大。
适用场景(可替代XML):跨防火墙访问、可调式性要求高、基于 Web browser 的 Ajax 请求、传输数据量相对小,实时性要求相对低(秒级别)的服务。
JSON
采用一种“假定有序快速匹配”的算法。
优点:接口简单易用、目前 java 语言中最快的 json 库。
缺点:过于注重快,而偏离了“标准”及功能性、代码质量不高,文档不全。
适用场景:协议交互、Web 输出、Android 客户端。
Fastjson
不仅是序列化协议,还是一个 RPC 框架。
缺点:使用者较少、跨防火墙访问时,不安全、不具有可读性,调试代码时相对困难、不能与其他传输层协议共同使用(例如 HTTP)、无法支持向持久层直接读写数据,即不适合做数据持久化序列化协议。
适用场景:分布式系统的 RPC 解决方案。
Thrift
Hadoop 的一个子项目,解决了 JSON 的冗长和没有 IDL 的问题。
优点:支持丰富的数据类型、简单的动态语言结合功能、具有自我描述属性、提高了数据解析速度、快速可压缩的二进制数据形式、可以实现远程过程调用RPC、支持跨编程语言实现。
缺点:对于习惯于静态类型语言的用户不直观。
适用场景:在 Hadoop 中做 Hive、Pig 和 MapReduce 的持久化数据格式。
Avro
将数据结构以 .proto 文件进行描述,通过代码生成工具可以生成对应数据结构的 POJO 对象和 Protobuf 相关的方法和属性。
优点:序列化后码流小,性能高、结构化数据存储格式(XML、JSON等)、通过标识字段的顺序,可以实现协议的前向兼容、结构化的文档更容易管理和维护。
缺点:需要依赖于工具生成代码、支持的语言相对较少,官方只支持 Java 、C++ 、python。
适用场景:对性能要求高的 RPC 调用、具有良好的跨防火墙的访问属性、适合应用层对象的持久化。
Protobuf
基于 protobuf 协议,但不需要配置 proto 文件,直接导包即可。
protostuff
可以直接序列化 java 类, 无须实 java.io.Serializable 接口。
Jboss marshaling
一个高效的二进制序列化格式。
Message pack
采用二进制协议的轻量级 remoting onhttp 工具。
Hessian
基于 protobuf 协议,只支持 java 语言,需要注册(Registration),然后序列化(Output),反序列化(Input)。
kryo
对于公司间的系统调用,如果性能要求在100ms以上的服务,基于 XML 的 SOAP 协议是一个值得考虑的方案。
基于 Web browser 的 Ajax,以及 Mobile app 与服务端之间的通讯,JSON 协议是首选。
对于性能要求不太高,或者以动态类型语言为主,或者传输数据载荷很小的的运用场景,JSON也是非常不错的选择。
对于调试环境比较恶劣的场景,采用 JSON 或 XML 能够极大的提高调试效率,降低系统开发成本。
当对性能和简洁性有极高要求的场景,Protobuf,Thrift,Avro 之间具有一定的竞争关系。
对于 T 级别的数据的持久化应用场景,Protobuf 和 Avro 是首要选择。如果持久化后的数据存储在 hadoop 子项目里,Avro 会是更好的选择。
对于持久层非 Hadoop 项目,以静态类型语言为主的应用场景,Protobuf 会更符合静态类型语言工程师的开发习惯。由于 Avro 的设计理念偏向于动态类型语言,对于动态语言为主的应用场景,Avro 是更好的选择。
如果需要提供一个完整的 RPC 解决方案,Thrift 是一个好的选择。
如果序列化之后需要支持不同的传输层协议,或者需要跨防火墙访问的高性能场景,Protobuf 可以优先考虑。
选择
序列化协议
readerIdleTime:为读超时时间(即测试端一定时间内未接受到被测试端消息)。
writerIdleTime:为写超时时间(即测试端一定时间内向被测试端发送消息)。
allIdleTime:所有类型的超时时间。
心跳类型设置
作用不同:Tomcat 是 Servlet 容器,可以视为 Web 服务器,而 Netty 是异步事件驱动的网络应用程序框架和工具用于简化网络编程,例如TCP和UDP套接字服务器。
协议不同:Tomcat 是基于 http 协议的 Web 服务器,而 Netty 能通过编程自定义各种协议,因为 Netty 本身自己能编码/解码字节流,所有 Netty 可以实现 HTTP 服务器、FTP 服务器、UDP 服务器、RPC 服务器、WebSocket 服务器、Redis 的 Proxy 服务器、MySQL 的 Proxy 服务器等等。
Netty 和 Tomcat
Netty Reactor 工作架构图
工作流程
Netty
常用框架
0 条评论
回复 删除
下一页