0

1

2

3

4

5

6

7

8

9

0

1

{{ noReadMessageTotal }}

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

尚硅谷Java技术之北京高频面试题:第七章 Java框架篇

晴天 晴天 | 228 | 686天前

尚硅谷Java技术之北京高频面试题

版本:V6.0

尚硅谷Java技术中心

第七章 Java框架篇

1、简单的谈一下SpringMVC的工作流程?

  • 用户发送请求至前端控制器DispatcherServlet

  • DispatcherServlet收到请求调用HandlerMapping处理器映射器。

  • 处理器映射器找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

  • DispatcherServlet调用HandlerAdapter处理器适配器

  • HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。

  • Controller执行完成返回ModelAndView

  • HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet

  • DispatcherServlet将ModelAndView传给ViewReslover视图解析器

  • ViewReslover解析后返回具体View

  • DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。

  • DispatcherServlet响应用户

2、说出Spring 或者 Springmvc中常用的5个注解,并解释含义

@Component 基本注解,标识一个受Spring管理的组件

@Controller 标识为一个表示层的组件

@Service 标识为一个业务层的组件

@Repository 标识为一个持久层的组件

@Autowired 自动装配

@Qualifier("") 具体指定要装配的组件的id值

@RequestMapping() 完成请求映射

@PathVariable 映射请求URL中占位符到请求处理方法的形参

只要说出几个注解并解释含义即可,如上答案只做参考

3、简述Springmvc中如何返回JSON数据

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

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

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

4、谈谈你对Spring的理解

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

Spring 容器的主要核心是:

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

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

面向切面编程(AOP),在面向对象编程(oop)思想中,我们将事物纵向抽成一个个的对象。而在面向切面编程中,我们将一个个的对象某些类似的方面横向抽成一个切面,对这个切面进行一些如权限控制、事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。AOP
底层是动态代理,如果是接口采用 JDK 动态代理,如果是类采用CGLIB
方式实现动态代理。

5、Spring中常用的设计模式

(1)代理模式------spring
中两种代理方式,若目标对象实现了若干接口,spring 使用jdk
的java.lang.reflect.Proxy类代理。若目标兑现没有实现任何接口,spring 使用
CGLIB 库生成目标类的子类。

(2)单例模式------在 spring 的配置文件中设置 bean 默认为单例模式。

(3)模板方式模式------用来解决代码重复的问题。

比如:RestTemplate、JmsTemplate、JpaTemplate

  1. 工厂模式------在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用同一个接口来指向新创建的对象。Spring
    中使用 beanFactory 来创建对象的实例。

6、Spring循环依赖问题

常见问法

请解释一下spring中的三级缓存

三级缓存分别是什么?三个Map有什么异同?

什么是循环依赖?请你谈谈?看过spring源码吗?

如何检测是否存在循环依赖?实际开发中见过循环依赖的异常吗?

多例的情况下,循环依赖问题为什么无法解决?

什么是循环依赖?

4645.png

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

官方解释

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

相关概念

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

图片2.png

初始化:对象属性赋值

图片3.png
5566.png

三级缓存


名称 对象名 含义


一级 缓存 singletonObjects 存放已经经历了完整生命周期的Bean对象

二级 缓存 earlySingletonObjects 存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完)

三级 缓存 singletonFactories 存放可以生成Bean的工厂

四个关键方法

678.png

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、介绍一下Spring bean 的生命周期、注入方式和作用域

Bean的生命周期

(1)默认情况下,IOC容器中bean的生命周期分为五个阶段:

  • 调用构造器 或者是通过工厂的方式创建Bean对象

  • 给bean对象的属性注入值

  • 调用初始化方法,进行初始化, 初始化方法是通过init-method来指定的.

  • 使用

  • IOC容器关闭时, 销毁Bean对象.

(2)当加入了Bean的后置处理器后,IOC容器中bean的生命周期分为七个阶段:

  • 调用构造器 或者是通过工厂的方式创建Bean对象

  • 给bean对象的属性注入值

  • 执行Bean后置处理器中的 postProcessBeforeInitialization

  • 调用初始化方法,进行初始化, 初始化方法是通过init-method来指定的.

  • 执行Bean的后置处理器中 postProcessAfterInitialization

  • 使用

  • IOC容器关闭时, 销毁Bean对象

只需要回答出第一点即可,第二点也回答可适当 加分。

注入方式:

通过 setter 方法注入

通过构造方法注入

Bean的作用域

总共有四种作用域:

  • Singleton 单例的

  • Prototype 原型的

  • Request

  • Session

8、请描述一下Spring 的事务管理

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

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

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

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

9、MyBatis中 #{}和${}的区别是什么?

#{}是预编译处理,${}是字符串替换;

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

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

使用#{}可以有效的防止SQL注入,提高系统安全性。

10、Mybatis 中一级缓存与二级缓存?

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

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

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

(2)缓存的查找顺序:二级缓存 => 一级缓存 => 数据库

11、MyBatis如何获取自动生成的(主)键值?

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

示例:

1.<insert id=”insertname” usegeneratedkeys=”true” keyproperty=”id”>  
2.    insert into names (name) values (#{name})  
3.</insert> 

12、简述Mybatis的动态SQL,列出常用的6个标签及作用

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

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

常用的标签:

<if> : 进行条件的判断

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

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

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

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

<foreach>:迭代操作

13、Mybatis 如何完成MySQL的批量操作,举例说明

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

例如:

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

14、谈谈怎么理解SpringBoot框架?

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

0099.png

Spring Boot的优点

  • 独立运行

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

  • 简化配置

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

  • 自动配置

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

  • 无代码生成和XML配置

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

  • 应用监控

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

Spring Boot缺点:

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

15、Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?

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

  • @SpringBootConfiguration:组合了 @Configuration
    注解,实现配置文件的功能。

  • @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,

    • 如关闭数据源自动配置功能: @SpringBootApplication(exclude = {
      DataSourceAutoConfiguration.class })。
  • @ComponentScan:Spring组件扫描。

16、Spring Boot 自动配置原理是什么?

注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass
就是自动配置的核心,

首先它得是一个配置文件,其次根据类路径下是否有这个类去自动配置。

@EnableAutoConfiguration是实现自动配置的注解

@Configuration表示这是一个配置文件

具体参考文档:

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

17、SpringBoot配置文件有哪些?怎么实现多环境配置?

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

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

bootstrap配置文件的特性:

  • boostrap 由父 ApplicationContext 加载,比 applicaton 优先加载

  • boostrap 里面的属性不能被覆盖

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

  • 使用 Spring Cloud Config 配置中心时,这时需要在 bootstrap
    配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息;

  • 一些固定的不能被覆盖的属性;

  • 一些加密/解密的场景;

提供多套配置文件,如:

applcation.properties
application-dev.properties
application-test.properties
application-prod.properties

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

18、Spring Boot和Spring Cloud是什么关系

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。

19、SpringCloud都用过哪些组件?介绍一下作用

经常用的组件:

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

2.
Gateway–作为网关,作为分布式系统统一的出入口,进行服务路由,统一鉴权等

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

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

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

20、Nacos作用以及注册中心的原理

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手动写服务注册与发现和配置拉取的逻辑。

200.png

服务注册原理

服务注册方法:以Java nacos client v1.0.1
为例子,服务注册的策略的是每5秒向nacos
server发送一次心跳,心跳带上了服务名,服务ip,服务端口等信息。同时 nacos
server也会向client
主动发起健康检查,支持tcp/http检查。如果15秒内无心跳且健康检查失败则认为实例不健康,如果30秒内健康检查失败则剔除实例。

20003.png

21、Feign工作原理

主程序入口添加了@EnableFeignClients注解开启对FeignClient扫描加载处理。根据Feign
Client的开发规范,定义接口并加@FeignClient注解。当程序启动时,会进行包扫描,扫描所有@FeignClient的注解的类,并且讲这些信息注入Spring
IOC容器中,当定义的的Feign接口中的方法被调用时,通过JDK的代理方式,来生成具体的RequestTemplate.当生成代理时,Feign会为每个接口方法创建一个RequestTemplate。当生成代理时,Feign会为每个接口方法创建一个RequestTemplate对象,该对象封装HTTP请求需要的全部信息,如请求参数名,请求方法等信息都是在这个过程中确定的。然后RequestTemplate生成Request,然后把Request交给Client去处理,这里指的时Client可以时JDK原生的URLConnection,Apache的HttpClient,也可以时OKhttp,最后Client被封装到LoadBalanceClient类,这个类结合Ribbon负载均衡发器服务之间的调用。

image.png

相关文章:
第一章 面试技巧篇
第二章 数据结构、设计模式与手写代码
第三章 Java基础篇
第四章 Java高级篇
第五章 MySQL数据库篇
第六章 Java Web篇
第七章 Java框架篇
第八章 Redis数据库篇
第九章 分布式技术篇
第十章 Git与Linux篇
第十一章 电商项目篇之尚品汇商城

真诚点赞 诚不我欺~

{{ praiseUserVoList.length }}人点赞

item.nickname

尚硅谷Java技术之北京高频面试题:第七章 Java框架篇

{{ isPraise ? '已点赞' : '点赞'}}
{{ isCollect ? '已收藏' : '收藏'}}
评论
gOod mornIng
没有更多啦~ 加载中...

关于作者

晴天
晴天

人因梦想而伟大!