Skip to content

第三章-java 框架篇

1、 难度系数:⭐

image40

  • 用户发送请求至前端控制器 DispatcherServlet
  • DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。
  • 处理器映射器找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。
  • DispatcherServlet 调用 HandlerAdapter 处理器适配器
  • HandlerAdapter 经过适配调用具体的处理器(Controller,也叫后端控制器)。
  • Controller 执行完成返回 ModelAndView
  • HandlerAdapter 将 controller 执行结果 ModelAndView 返回给 DispatcherServlet
  • DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器
  • ViewReslover 解析后返回具体 View
  • DispatcherServlet 根据 View 进行渲染视图(即将模型数据填充至视图中)。
  • DispatcherServlet 响应用户

2、 难度系数:⭐

  1. @Component 基本注解,标识一个受 Spring 管理的组件
  2. @Controller 标识为一个表示层的组件
  3. @Service 标识为一个业务层的组件
  4. @Repository 标识为一个持久层的组件
  5. @Autowired 自动装配
  6. @Qualifier("") 具体指定要装配的组件的 id 值
  7. @RequestMapping() 完成请求映射
  8. @PathVariable 映射请求 URL 中占位符到请求处理方法的形参

3、简述 SpringMVC 中如何返回 JSON 数据 难度系数:⭐

Step1:在项目中加入 json 转换的依赖,例如 jackson,fastjson,gson 等

Step2:在请求处理方法中将返回值改为具体返回的数据的类型, 例如数据的集合类 List< Employee >等

Step3:在请求处理方法上使用@ResponseBody 注解

4、 难度系数:⭐

Spring 是一个开源框架,为简化企业级应用开发而生。Spring 可以是使简单的 JavaBean 实现以前只有 EJB 才能实现的功能。Spring 是一个 容器框架。

Spring 容器的主要核心是:

,传统的 java 开发模式中,当需要一个对象时,我们会自己使用 new 或者 getInstance 等直接或者间接调用构造方法创建一个对象。而在 spring 开发模式中,spring 容器使用了工厂模式为我们创建了所需要的对象,不需要我们自己创建了,直接调用 spring 提供的对象就可以了,这是控制反转的思想。

,spring 使用 javaBean 对象的 set 方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程,就是依赖注入的思想。

,在面向对象编程(oop)思想中,我们将事物纵向抽成一个个的对象。而在面向切面编程中,我们将一个个的对象某些类似的方面横向抽成一个切面,对这个切面进行一些如权限控制、事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。

5、 难度系数:⭐

你工作当中用过什么设计模式

  1. 代理模式——spring 中两种代理方式,
    • 若目标对象实现了若干接口,spring 使用 jdk 的 java.lang.reflect.Proxy 类代理。
    • 若目标对象没有实现任何接口,spring 使用 CGLIB 库生成目标类的子类。
  2. 单例模式——在 spring 的配置文件中设置 bean 默认为单例模式。
  3. 模板方式模式——用来解决代码重复的问题。比如:RedisTemplate、RabbitMQTemplate、MongoTemplate
  4. 工厂模式——在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用同一个接口来指向新创建的对象。Spring 中使用 beanFactory 来创建对象的实例。

6、Spring 循环依赖问题 难度系数:⭐⭐

参考文献:

https://blog.51cto.com/u_13626762/5226372

image41

常见问法

  • 请解释一下 spring 中的三级缓存
  • 三级缓存分别是什么?三个 Map 有什么异同?
  • 什么是循环依赖?请你谈谈?看过 spring 源码吗?
  • 如何检测是否存在循环依赖?实际开发中见过循环依赖的异常吗?
  • 多例的情况下,循环依赖问题为什么无法解决?

什么是循环依赖?

image42

两种注入方式对循环依赖的影响?

官方解释

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-dependency-resolution

相关概念

实例化:堆内存中申请空间

image43

初始化:对象属性赋值

image44

image45

名称对象名含义
一级缓存singletonObjects成品对象,存放已经经历了完整生命周期的 Bean 对象
二级缓存earlySingletonObjects半成品对象,存放早期暴露出来的 Bean 对象,Bean 的生命周期未结束(属性还未填充完)
三级缓存singletonFactories存放可以生成 Bean 的工厂,用来解决循环依赖问题

四个关键方法

image46

java
package org.springframework.beans.factory.support;
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
   /**
   单例对象的缓存:bean名称—bean实例,即:所谓的单例池。
   表示已经经历了完整生命周期的Bean对象
   第一级缓存
   */
   private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
   /**
   早期的单例对象的高速缓存: bean名称—bean实例。
   表示 Bean的生命周期还没走完(Bean的属性还未填充)就把这个 Bean存入该缓存中也就是实例化但未初始化的 bean放入该缓存里
   第二级缓存
   */
   private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
   /**
   单例工厂的高速缓存:bean名称—ObjectFactory
   表示存放生成 bean的工厂
   第三级缓存
   */
   private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
}

debug 源代码过程

需要 22 个断点(可选)

  1. A 创建过程中需要 B,于是 A 将自己放到三级缓里面,去实例化 B
  2. B 实例化的时候发现需要 A,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了 A 然后把三级缓存里面的这个 A 放到二级缓存里面,并删除三级缓存里面的 A
  3. B 顺利初始化完毕,将自己放到一级缓存里面(此时 B 里面的 A 依然是创建中状态)然后回来接着创建 A,此时 B 已经创建结束,直接从一级缓存里面拿到 B,然后完成创建,并将 A 自己放到一级缓存里面。

总结

  1. Spring 创建 bean 主要分为两个步骤,创建原始 bean 对象,接着去填充对象属性和初始化。
  2. 每次创建 bean 之前,我们都会从缓存中查下有没有该 bean,因为是单例,只能有一个。
  3. 当创建 A 的原始对象后,并把它放到三级缓存中,接下来就该填充对象属性了,这时候发现依赖了 B,接着就又去创建 B,同样的流程,创建完 B 填充属性时又发现它依赖了 A 又是同样的流程,不同的是:这时候可以在三级缓存中查到刚放进去的原始对象 A。所以不需要继续创建,用它注入 B,完成 B 的创建既然 B 创建好了,所以 A 就可以完成填充属性的步骤了,接着执行剩下的逻辑,闭环完成

Spring 解决循环依赖依靠的是 Bean 的"中间态"这个概念,而这个中间态指的是已经实例化但还没初始化的状态—>半成品。实例化的过程又是通过构造器创建的,如果 A 还没创建好出来怎么可能提前曝光,所以构造器的循环依赖无法解决

其他衍生问题

  1. 为什么构造器注入属性无法解决循环依赖问题?
    • 由于 spring 中的 bean 的创建过程为先实例化 再初始化(在进行对象实例化的过程中不必赋值)将实例化好的对象暴露出去,供其他对象调用,然而使用构造器注入,必须要使用构造器完成对象的初始化的操作,就会陷入死循环的状态
  2. 一级缓存能不能解决循环依赖问题? 不能
    • 在三个级别的缓存中存储的对象是有区别的 一级缓存为完全实例化且初始化的对象 二级缓存实例化但未初始化对象 如果只有一级缓存,如果是并发操作下,就有可能取到实例化但未初始化的对象,就会出现问题
  3. 二级缓存能不能解决循环依赖问题?
    • 理论上二级缓存可以解决循环依赖问题,但是需要注意,为什么需要在三级缓存中存储匿名内部类(ObjectFactory),原因在于 需要创建代理对象 eg:现有 A 类,需要生成代理对象 A 是否需要进行实例化(需要) 在三级缓存中存放的是生成具体对象的一个匿名内部类,该类可能是代理类也可能是普通的对象,而使用三级缓存可以保证无论是否需要是代理对象,都可以保证使用的是同一个对象,而不会出现,一会儿使用普通 bean 一会儿使用代理类

7、 难度系数:⭐

Bean 的生命周期,bean 初始化的过程。

image47

  1. 默认情况下,IOC 容器中 bean 的生命周期分为五个阶段:
    • 调用构造器 或者是通过工厂的方式创建 Bean 对象
    • 给 bean 对象的属性注入值
    • 调用初始化方法,进行初始化, 初始化方法是通过 init-method 来指定的.
    • 使用
    • IOC 容器关闭时, 销毁 Bean 对象.
  2. 当加入了 Bean 的后置处理器后,IOC 容器中 bean 的生命周期分为七个阶段:
    • 调用构造器 或者是通过工厂的方式创建 Bean 对象
    • 给 bean 对象的属性注入值
    • 执行 Bean 后置处理器中的 postProcessBeforeInitialization
    • 调用初始化方法,进行初始化, 初始化方法是通过 init-method 来指定的.x
    • 执行 Bean 的后置处理器中 postProcessAfterInitialization
    • 使用
    • IOC 容器关闭时, 销毁 Bean 对象

注入方式:

  • 通过 setter 方法注入
  • 通过构造方法注入

** **

总共有四种作用域:

  • Singleton 单例的
  • Prototype 原型的
  • Request
  • Session

8、 难度系数:⭐

(1)声明式事务管理的定义:用在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。这样的好处是,事务管理不侵入开发的组件,具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可,这样维护起来极其方便。

基于 TransactionInterceptor 的声明式事务管理:两个次要的属性: transactionManager,用来指定一个事务治理器, 并将具体事务相关的操作请托给它; 其他一个是 Properties 类型的 transactionAttributes 属性,该属性的每一个键值对中,键指定的是方法名,方法名可以行使通配符, 而值就是表现呼应方法的所运用的事务属性。

(2):Spring 2.x 还引入了基于 Annotation 的体式格式,具体次要触及@Transactional 标注。@Transactional 可以浸染于接口、接口方法、类和类方法上。算作用于类上时,该类的一切 public 方法将都具有该类型的事务属性。

(3) :在代码中显式挪用 等事务治理相关的方法, 这就是编程式事务管理。Spring 对事物的编程式管理有基于底层 API 的编程式管理和基于 TransactionTemplate 的编程式事务管理两种方式。

更多:

https://blog.csdn.net/sufu1065/article/details/122076645

9、 难度系数:⭐

Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法来赋值;

Mybatis 在处理${}时,就是把${}替换成变量的值;

使用,提高系统安全性。

10、Mybatis 中一级缓存与二级缓存 难度系数:⭐

  1. MyBatis 的缓存分为一级缓存和 二级缓存。

    一级缓存是 SqlSession 级别的缓存,默认开启。

    二级缓存是 NameSpace 级别(Mapper)的缓存,多个 SqlSession 可以共享,使用时需要进行配置开启。

  2. 缓存的查找顺序:

11、MyBatis 如何获取自动生成的(主)键值 难度系数:⭐

在< insert>标签中使用 两个属性来获取自动生成的主键值。

示例:

xml
<insert id="insertname" usegeneratedkeys="true" keyproperty="id">
    insert into names (name) values (#{name})
</insert>

12、 难度系数:⭐

你都用过哪些标签

Mybatis-plus 都用过哪些方法

给你一张表,让你吧 xml 写出来

动态 SQL 是 MyBatis 的强大特性之一 基于功能强大的 OGNL 表达式。

动态 SQL 主要是来解决查询条件不确定的情况,在程序运行期间,根据提交的条件动态的完成查询

常用的标签:

< if> : 进行条件的判断

< where>:在< if>判断后的 SQL 语句前面添加 WHERE 关键字,并处理 SQL 语句开始位置的 AND 或者 OR 的问题

< trim>:可以在 SQL 语句前后进行添加指定字符 或者去掉指定字符.

< set>: 主要用于修改操作时出现的逗号问题

< choose> < when> < otherwise>:类似于 java 中的 switch 语句.在所有的条件中选择其一

< foreach>:迭代操作

13、Mybatis 如何完成 MySQL 的批量操作 难度系数:⭐

image48

image49

image50

MyBatis 完成 MySQL 的批量操作主要是通过< foreach>标签来拼装相应的 SQL 语句

例如:

xml
<insert** id="insertBatch" >
    insert into tbl_employee(last_name,email,gender,d_id) values
   <foreach** collection="emps" item="curr_emp" separator=","**>
      (#{curr_emp.lastName},#{curr_emp.email},#{curr_emp.gender},#{curr_emp.dept.id})
   </foreach>
</insert>

14、 难度系数:⭐⭐

这个框架有什么好处

你们公司为什么选用它

Springboot springcloud 区别与联系

Spring Springboot

Spring Boot 是 Spring 开源组织下的子项目,是 Spring 组件一站式解决方案,主要是简化了使用 Spring 的难度,简省了繁重的配置,提供了各种启动器,开发者能快速上手。

image51

Spring Boot 而且内嵌了各种 servlet 容器,Tomcat、Jetty 等,现在不再需要打成 war 包部署到容器中,Spring Boot 只要打成一个可执行的 jar 包就能独立运行,所有的依赖包都在一个 jar 包内。

spring-boot-starter-web 启动器自动依赖其他组件,简少了 maven 的配置。除此之外,还提供了各种启动器,开发者能快速上手。

Spring Boot 能根据当前类路径下的类、jar 包来自动配置 bean,如添加一个 spring-boot-starter-web 启动器就能拥有 web 的功能,无需其他配置。

Spring Boot 配置过程中无代码生成,也无需 XML 配置文件就能完成所有配置工作,这一切都是借助于条件注解完成的,这也是 Spring4.x 的核心功能之一。

Spring Boot 提供一系列端点可以监控服务及应用,做健康检测。

Spring Boot 虽然上手很容易,但如果你不了解其核心技术及流程,所以一旦遇到问题就很棘手,而且现在的解决方案也不是很多,需要一个完善的过程。

15、 它主要由哪几个注解组成的 难度系数:⭐

启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:

  • @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
  • :打开自动配置的功能,也可以关闭某个自动配置的选项,
  • 如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
  • @ComponentScan:Spring 组件扫描。

难度系数:⭐

话术

  • 注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自动配置的核心,
  • 首先它得是一个配置文件,其次根据类路径下是否有这个类去自动配置。
  • @EnableAutoConfiguration 是实现自动配置的注解
  • @Configuration 表示这是一个配置文件

具体参考文档:

https://mp.weixin.qq.com/s?__biz=MzI3ODcxMzQzMw==&mid=2247484365&idx=1&sn=a4ab1d977d6b03bf122b4d596d7ee1ab&scene=21

17、SpringBoot 配置文件有哪些 怎么实现多环境配置 难度系数:⭐

Java -jar adsd.jar -Xmx200m -Xms200m

Spring Boot 的核心配置文件是 application 和 bootstrap 配置文件。

application 配置文件这个容易理解,主要用于 Spring Boot 项目的自动化配置。

bootstrap 配置文件的特性:

  • bootstrap 由父 ApplicationContext 加载,比 applicaton 优先加载
  • bootstrap 里面的属性不能被覆盖

bootstrap 配置文件有以下几个应用场景:

  • 使用 Spring Cloud Config 配置中心时,这时需要在 bootstrap 配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息;
  • 一些固定的不能被覆盖的属性;
  • 一些加密/解密的场景;

提供多套配置文件,如:

applcation.properties

application-dev.properties

application-test.properties

application-prod.properties

运行时指定具体的配置文件,具体请看这篇文章《Spring Boot Profile 不同环境配置》。

18、难度系数:⭐

Spring Boot 是 Spring 的一套快速配置脚手架,可以基于 Spring Boot 快速开发单个微服务,Spring Cloud 是一个基于 Spring Boot 实现的开发工具;Spring Boot 专注于快速、方便集成的单个微服务个体,Spring Cloud 关注全局的服务治理框架; Spring Boot 使用了默认大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud 很大的一部分是基于 Spring Boot 来实现,必须基于 Spring Boot 开发。

可以单独使用 Spring Boot 开发项目,但是 Spring Cloud 离不开 Spring Boot。

20、 介绍一下作用 难度系数:⭐

笔试题

  1. Nacos--作为注册中心和配置中心,实现服务注册发现和服务健康监测及配置信息统一管理

  2. Gateway--作为网关,作为分布式系统统一的出入口,进行服务路由,统一鉴权等 限流 请求鉴权 跨域处理 cros

  3. OpenFeign--作为远程调用的客户端,实现服务之间的远程调用

    httpclient okhttp java 类库(小框架,小的工具类),发送 http 请求

    http://www.baidu.com/add/user post

    Id :1

    Name:zhangli

  4. Sentinel--实现系统的熔断/限流

  5. Sleuth--实现服务的链路追踪

21、 难度系数:⭐⭐

配置方式

Nacos 英文全称 Dynamic Naming and Configuration Service,Na 为 naming/nameServer 即注册中心,co 为 configuration 即配置中心,service 是指该注册/配置中心都是以服务为核心。

Nacos 注册中心分为 server 与 client,server 采用 Java 编写,为 client 提供注册发现服务与配置服务。而 client 可以用多语言实现,client 与微服务嵌套在一起,nacos 提供 sdk 和 openApi,如果没有 sdk 也可以根据 openApi 手动写服务注册与发现和配置拉取的逻辑。

image52

服务注册原理

image53

21、 难度系数:⭐⭐

主程序入口添加了@EnableFeignClients 注解开启对 FeignClient 扫描加载处理。

根据 Feign Client 的开发规范,定义接口并加@FeignClient 注解。

当程序启动时,会进行包扫描,扫描所有@FeignClient 的注解的类,并且讲这些信息注入 Spring IOC 容器中,

当定义的的 Feign 接口中的方法被调用时,通过 JDK 的代理方式,来生成具体的 RequestTemplate.当生成代理时,Feign 会为每个接口方法创建一个 RequestTemplate。

当生成代理时,该对象封装 HTTP 请求需要的全部信息,如请求参数名,请求方法等信息都是在这个过程中确定的。然后 RequestTemplate 生成 Request,然后把 Request 交给 Client 去处理,这里指的时 Client 可以时 JDK 原生的 URLConnection,Apache 的 HttpClient,也可以时 OKhttp,最后 Client 被封装到 LoadBalanceClient 类,这个类结合 Ribbon 负载均衡发器服务之间的调用。

image54

image55

根据 MIT 许可发布。