4、框架笔记

1、Spring

  • Spring 支持两种类型的事务管理:

事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。事务四个属性ACID。

  1. 程序化事务管理:在此过程中,在编程的帮助下管理事务。它为您提供极大的灵活性,但维护起来非常困难。
  2. 声明式事务管理:在此,事务管理与业务代码分离。仅使用注解或基于 XML 的配置来管理事务。
  • Spring 支持哪些 ORM 框架
    • Hibernate
    • iBatis
    • JPA

1.1 IOC-控制反转

将对象生成控制权转交出去。可以实现组件之间的解耦。IOC 的实现方式通常有依赖注入DI 和依赖查找DL 。

  • 依赖注入通常借助一个上下文被动的接收(标注 @Autowired 注解 / 标签配置)
  • 依赖查找通常主动使用上下文搜索(拿到 BeanFactory / ApplicationContext 之后主动调用 getBean 方法)

IOC容器

  • BeanFactory 就像一个包含 bean 集合的工厂类。它会在客户端要求时实例化 bean。(懒加载)
  • ApplicationContext 接口扩展了 BeanFactory 接口。它在 BeanFactory 基础上提供了一些额外的功能。(即时加载)
  • Spring 中的 IoC 的实现原理就是工厂模式加反射机制。

1.2 Beans

Bean 是基于用户提供给容器的配置元数据创建,它们由 Spring IoC 容器实例化,配置,装配和管理。

  • @Configuration 类允许通过简单地调用同一个类中的其他 @Bean 方法来定义 bean 间依赖关系;
  • Spring 容器能够自动装配 bean。可以通过检查 BeanFactory 的内容让 Spring 自动解析 bean 的协作者。

Bean Scope

  • Singleton - (默认)每个 Spring IoC 容器仅有一个单实例。关闭工厂 ,所有的对象都会销毁。
  • Prototype - 每次请求都会产生一个新的实例。关闭工厂,所有的对象不会销毁,内部的垃圾回收机制会回收。

1.3 注 解

  • @Component:将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。
  • @Repository:类似 @Component,它将 DAO 导入 IoC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException。
  • @Controller, @Service:以更好的方式指定了意图。
  • @Autowired: 可以更准确地控制应该在何处以及如何进行自动装配。
  • 创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,您可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义。

1.4 AOP(面向切面编程)

实现原理

  • 基于接口的动态代理----JDK动态代理
  • 基于类的动态代理–cglib
  • Spring AOP 基于动态代理方式实现;AspectJ 基于静态代理方式实现(编译阶段就可生成 AOP 代理类)

基本概念

  • Joinpoint(连接点):指那些被拦截到的点,具体指某个方法。
  • Pointcut(切入点):JoinPoint的集合,是程序中需要注入Advice的位置的集合。
  • Advice(通知/增强):指拦截到Joinpoint之后所要做的事情就是通知。
  • Aspect(切面): 是切入点(PointCut)和通知(advice)的结合。

通知类型

  • @Before:前置通知,在 joinpoint 方法之前执行。
  • @AfterReturning:后置通知,在连接点方法正常执行后执行。
  • @AfterThrowing: 异常抛出通知, 在 joinpoint 方法通过抛出异常退出时执行。
  • @After:最终final通知, 在连接点方法之后执行,无论方法退出是正常还是异常返回。
  • @Around:环绕通知,在连接点之前和之后执行。

切面执行顺序

  • 一个方法只被一个Aspect类拦截:
    • @Around→@Before→@Around执行 ProceedingJoinPoint.proceed() →@After→@AfterRunning(异@AfterThrowing)

  • 同一个方法被多个Aspect类拦截:
    • order值越小,优先级越高
    • 实现Ordered接口:实现该接口的int getOrder()方法,
    • 使用@Order注解来修饰一个切面类

1.5 SpringMVC

传统的模型层被拆分为了业务层(Service)和数据访问层(DAO,Data Access Object)。 在 Service 下可以通过 Spring 的声明式事务操作数据访问层,而在业务层上还允许我们访问 NoSQL

DispatcherServlet 的工作流程:

  • 1、客户端请求提交到DispatcherServlet
  • 2、由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
  • 3、DispatcherServlet将请求提交到Controller
  • 4、Controller调用业务逻辑处理后,返回ModelAndView
  • 5、DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
  • 6、视图负责将结果显示到客户端

拦截器

Springmvc的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理

  • preHandle方法:进入Handler方法之前执行。可以用于身份认证、身份授权。
  • postHandle方法:进入Handler方法之后。返回ModelAndView之前执行。
  • afterCompletion方法:执行Handler完成之后执行。应用场景:统一异常处理,统一日志处理等

方法参数解析器: HandlerMethodArgumentResolver

用于在给定请求的上下文中将方法参数解析为参数值,简单理解它负责处理你Handler方法里的所有入参:包括自动封装、自动赋值、校验等等。

  • supportsParameter(满足某种要求,返回true,方可进入resolveArgument做参数处理)
  • resolveArgument,详细使用

1.6 解决循环依赖的问题

循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。文章一文章二

  • 构造器循环依赖
  • field属性注入循环依赖

对于循环依赖的场景,构造器注入prototype类型的属性注入都会初始化Bean失败。

1.7 经典问答

  • @PostConstruct : 在当前对象加载完依赖注入的 bean 后,运行这个被 @PostConstruct注解的方法,而且只运行一次。

2、Sprinboot

Spring Boot称为搭建程序的脚手架。其最主要作用就是帮我们快速的构建庞大的spring项目,并且尽可能的减少一切xml配置,做到开箱即用,迅速上手,让我们关注于业务而非配置

2.1 Spring Boot启动流程

加载配置文件—>自动装配—>加载组件—>初始应用

  • SpringBootApplication:含有以下三个配置

    • @SpringBootConfiguration:声明当前类是SpringBoot应用的配置类
    • @EnableAutoConfiguration:开启自动配置
    • @ComponentScan:开启注解扫描
  • @EnableAutoConfiguration会开启SpringBoot的自动配置,并且根据你引入的依赖来生效对应的默认配置

    • 入了一个依赖:spring-boot-autoconfigure,其中定义了大量自动配置类(反射加载spring.factories中指定的java配置类)
    • 举例SpringMVC,查看mvc 的自动配置类WebMvcAutoConfiguration:定义了视图解析器、处理器适配器
    • @ConditionalOnMissingBean:判断是否自定义配置,如果自己配置了WebMVCConfigurationSupport的类,这个默认配置就会失效!
    • 引入ResourceProperties,定义prefix和suffix属性,只需要在application.properties中定义与其前缀prefix和字段名一致的属性即可覆盖。

2.2 任务

  • 异步任务:@EnableAsync //开启异步注解功能;@Async //告诉Spring这是一个异步方法,开启多线程;
  • 定时任务:@EnableScheduling //开启基于注解的定时任务;@Scheduled(cron = “0/4 * * * * MON-SAT”) //每4秒执行一次

3、SprinCloud

3.1 微服务的特点:

  • 单一职责:微服务中每一个服务都对应唯一的业务能力,做到单一职责
  • 微:微服务的服务拆分粒度很小,例如一个用户管理就可以作为一个服务。每个服务虽小,但“五脏俱全”。
  • 面向服务:面向服务是说每个服务都要对外暴露Rest风格服务接口API。并不关心服务的技术实现,做到与平台和语言无关,也不限定用什么技术实现,只要提供Rest的接口即可。
  • 自治:自治是说服务间互相独立,互不干扰

3.2 服务调用方式

  • RPC:Remote Produce Call远程过程调用,类似的还有RMI。自定义数据格式,基于原生TCP通信,速度快,效率高。早期的webservice,现在热门的dubbo,都是RPC的典型代表。

    • 公司全部采用Java技术栈,那么使用Dubbo作为微服务架构是一个不错的选择
  • Http:http其实是一种网络传输协议,基于TCP,规定了数据传输的格式。现在客户端浏览器与服务端通信基本都是采用Http协议,也可以用来进行远程服务调用。缺点是消息封装臃肿,优势是对服务的提供和调用方没有任何技术限定,自由灵活,更符合微服务理念。

    • 现在热门的Rest风格(SpringCloud),就可以通过http协议来实现。

3.3 组件介绍

  • Eureka:服务治理组件,包含服务注册中心,服务注册与发现机制的实现。(服务治理,服务注册/发现)
  • Zuul:网关组件,提供智能路由,访问过滤功能
  • Ribbon:客户端负载均衡的服务调用组件(客户端负载)
  • Feign:服务调用,给予Ribbon和Hystrix的声明式服务调用组件 (声明式服务调用)
  • Hystrix:容错管理组件,实现断路器模式,帮助服务依赖中出现的延迟和为故障提供强大的容错能力。(熔断、断路器,容错)

3.4 SpringCloud Alibaba

  • Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。负责服务注册发现和服务配置,可以这样认为nacos=eureka+config。

  • Open-feign:Feign是Spring Cloud提供的一个声明式的伪Http客户端, 它使得调用远程服务就像调用本地服务 一样简单, 只需要创建一个接口并添加一个注解即可。Nacos很好的兼容了Feign, Feign默认集成了 Ribbon, 所以在Nacos下使用Fegin默认就实现了负载 均衡的效果。

  • Gateway:Spring公司为了替换Zuul而开发的网关服务;

    • predicates/filters 主要是断言匹配和过滤,RewritePath路径重写;
    • 路径优先级:上下顺序决定优先级,/api/member/** 会优于/api/**
  • Sentinel: (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量 为切入点, 从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。

    • 当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则 对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。

3.5 Dubbo

  • Dubbo 是基于NIO的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小,异步调用会返回一个 Future 对象。
  • 使用 Zookeeper 作为注册中心
  • 服务失效踢出基于 zookeeper 的临时节点原理。
  • Dubbo 推荐协议:dubbo://
  • 在注册中心找不到对应的服务,检查 service 实现类是否添加了@service 注解
  • 无法连接到注册中心,检查配置文件中的对应的测试 ip 是否正确
  • 服务上线怎么兼容旧版本:可以用版本号(version)过渡,多个不同版本的服务注册到注册中心,版本号不同的服务相互间不引用。这个和服务分组的概念有一点类似
  • dubbo 服务发布之后,我们可以利用 telnet 命令进行调试、管理

4、MyBatis

Mybatis 是一个半 ORM(对象关系映射)框架,它内部封装了 JDBC,开发时
只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建
statement 等繁杂的过程。

4.1 优、缺点

  • 优点:
    • 1、基于 SQL 语句编程,SQL 写在 XML 里,解除 sql 与程序代码的耦合;提供 XML标签,支持编写动态 SQL 语句,并可重用。
    • 2、与 JDBC 相比,减少了 50%以上的代码量,消除了 JDBC 大量冗余的代码,不需要手动开关连接;
    • 3、很好的与各种数据库兼容
    • 4、提供映射标签,支持对象与数据库的 ORM 字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
  • 缺点:
    • SQL 语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL 语句的功底有一定要求;
    • SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

4.2 #{}和${}的区别

  • #{}是预编译处理
    • Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,使用#{}可以有效的防止 SQL注入,提高系统安全性
  • ${}是字符串替换。
    • Mybatis 在处理时 , 就 是 把 {}时,就是把{}替换成变量的值。

4.3 Dao 接口的工作原理是什么

Mapper 接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻
找策略。Mapper 接口的工作原理是 JDK 动态代理,Mybatis 运行时会使用 JDK
动态代理为 Mapper 接口生成代理对象 proxy,代理对象会拦截接口方法,转而
执行 MapperStatement 所代表的 sql,然后将 sql 执行结果返回

4.4 Mybatis 是如何进行分页的

Mybatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内
存分页,而非物理分页。可以在 sql 内直接书写带有物理分页的参数来完成物理分
页功能,也可以使用分页插件来完成物理分页

分页插件的基本原理是使用 Mybatis 提供的插件接口,实现自定义插件,(Interceptor 接口并复写 intercept()方法)在插件的拦截方法内拦截待执行的 sql,然后重写 sql,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。

4.5 MyBatis 与 Hibernate 不同

  • Mybatis 不完全是一个 ORM 框架,需要程序员自己编写 Sql 语句;
  • Mybatis 直接编写原生态 sql,可以严格控制 sql执行性能,灵活度高,适合对关系数据模型要求不高、需求变化频繁 的软件开发;
  • Hibernate 对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,如果用 hibernate 开发可以节省很多代码,提高效率。

4.6 Mybatis 动态 sql

Mybatis 动态 sql 可以在 Xml 映射文件内,以标签的形式编写动态 sql,执行原理
是根据表达式的值 完成逻辑判断并动态拼接 sql 的功能

Mybatis 提供了 9 种动态 sql 标签:trim | where | set | foreach | if | choose
| when | otherwise | bind。

4.7 Mybatis缓存

  • 一级缓存: 基于 PerpetualCache 的 HashMap本地缓存,其存储作用域为Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。
  • 二级缓存与一级缓存其机制相同,默认也是采用PerpetualCache,HashMap存储,不同在于其存储作用域为Mapper(Namespace)

5、Hibernate

5.1 映射对象的状态

  • 临时状态/瞬时状态(transient):刚刚用 new 语句创建,没有被持久化 不处于 session 中(没有使用 session的方法去操作临时对象)。该对象成为临时对象
  • 持久化状态/托管状态(persistent):已经被持久化,加入到 session 的缓存中。session 是没有 关闭该状态的对象为持久化对象。
  • 游离状态/脱管状态(detached):已经被持久化,但不处于 session 中。 该状态的对象为游离对象。
  • 删除状态(removed):对象有关联的 ID,并且在 Session 管理下,但是已经被计划(事务提交的时候,commit())删除。如果没有事务就不能删除。

5.2 Hibernate 的缓存

为了提供访问速度,把磁盘或数据库访问变成内存访问。

  • 一级缓存就是 Session 级别的缓存,在事务范围内有效是,内置的不能被卸载
  • 二级缓存 是 SesionFactory 级别的缓存,从应用启动到应用结束有效。是可选的,默认没有二级缓存, 需要手动开启。
      1. 很少被修改的数据
      1. 经常被查询的数据
      1. 不是很重要的数据,允许出现偶尔并发的数据
      1. 不会被并发访问的数据
  • hibernate 的二级缓存默认是不支持分布式缓存的。使用redis 等中央缓 存来代替二级缓存

6、RabbitMQ

采用 AMQP 高级消息队列协议的一种消息队列技术,最大的特点就是消费并不需要确保提供方存在,实现了服务之间的高度解耦

6.1 什么是消息队列

消息队列是典型的:生产者、消费者模型。生产者不断向消息队列中生产消息,消费者不断的从队列中获取消息。因为消息的生产和消费都是异步的,而且只关心消息的发送和接收,没有业务逻辑的侵入,这样就实现了生产者和消费者的解耦。

  • 1、在分布式系统下具备异步,削峰,负载均衡等一系列高级功能;
  • 2、拥有持久化的机制,进程消息,队列中的信息也可以保存下来。
  • 3、实现消费者和生产者之间的解耦。
  • 4、对于高并发场景下,利用消息队列可以使得同步访问变为串行访问达到一定量的限流,利于数据库的操作。
  • 5、可使用消息队列达到异步下单的效果,排队中,后台进行逻辑下单。

6.2 使用场景

  • 1、服务间异步通信
  • 2、顺序消费
  • 3、定时任务
  • 4、请求削峰

6.3 如何避免消息丢失

消息的丢失,在MQ角度考虑,一般有三种途径:

  • 生产者确认发送到MQ服务器(生产者确认机制)
  • MQ服务器不丢数据(消息持久化)
  • 消费者确认消费掉消息(消费者确认机制)

生产者/消费者保证消息不丢失有两种实现方式:

  • 开启事务模式(大幅降低消息发送及接收效率,极少使用)
  • 消息确认模式(生产者/消费者的ACK机制)

消费者消息确认机制(ACK)

生产者确认机制有很严重的性能问题,如果每秒钟只有几百的消息量,可以使用。

消息一旦被消费者接收,队列中的消息就会被删除。

如果消费者领取消息后,还没执行操作就挂掉了呢?或者抛出了异常?消息消费失败,但是RabbitMQ无从得知,这样消息就丢失了!

RabbitMQ有一个ACK机制。当消费者获取消息后,会向RabbitMQ发送回执ACK,告知消息已经被接收。RabbitMQ就会把消息从队列中删除。如果此时消费者宕机,那么消息就丢失了。

  • 自动ACK:消息一旦被接收,消费者自动发送ACK,(消息不太重要,丢失也没有影响)
  • 手动ACK:消息接收后,不会发送ACK,需要手动调用(消息非常重要,不容丢失)

6.4 延时队列

延时队列就是用来存放需要在指定时间被处理的元素的队列。

如果数据量比较少,确实可以这样做,可以使用定时任务,一直轮询数据,每秒查一次,取出需要被处理的数据,然后处理。

  1. 订单在十分钟之内未支付则自动取消。
  2. 新创建的店铺,如果在十天内都没有上传过商品,则自动发送消息提醒。
  3. 账单在一周内未支付,则自动结算。
  4. 用户注册成功后,如果三天内没有登陆则进行短信提醒。
  5. 用户发起退款,如果三天内没有得到处理则通知相关运营人员。
  6. 预定会议后,需要在预定的时间点前十分钟通知各个与会人员参加会议。

6.5 经典问答

  • RabbitMQ 使用信道的方式来传输数据。信道是建立在真实的 TCP 连接内的虚拟连接,且每条 TCP连接上的信道数量没有限制。
  • 避免消息重复投递或重复消费,要求消息体中必须要有一个 bizId(对于同一业务全局唯一,如支付 ID、订单 ID、帖子 ID 等)作为去重的依据。

版权声明:本文为qq_35988274原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。