1. Spring面试基础
1.1 Spring框架概念及其相关组件
1.1.1 Spring是什么
Spring是一个开源框架,Spring的核心是控制反转(IoC)和面向切面(AOP)。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。
轻量级:与EJB对比,依赖资源少,消耗的资源少。
分层: 一站式,每一个层都提供的解决方案
1.1.2 Spring相关组件:
- Spring Core: 核心类库,提供IOC服务;
- Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等);
- Spring AOP:AOP服务;
- Spring DAO:对JDBC的抽象,简化了数据访问异常的处理;
- Spring ORM:对现有的ORM框架的支持;
- Spring Web:提供了基本的面向Web的综合特性,例如多方文件上传;
- Spring MVC:提供面向Web应用的Model-View-Controller实现。
1.2 什么是IOC和DI
1.2.1 IOC
IOC就是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。DI依赖注入,和控制反转是同一个概念的不同角度的描述,即 应用程序在运行时依赖IoC容器来动态注入对象需要的外部资源。
1.2.2 DI
DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
1.3 SpringBean的配置方式
- 基于XML配置
- 基于注解的配置(@Component、@Repository、@Controller @Service)
- 基于Java类+配置注解的方式(@Configuration @Bean)
1.4 IOC容器Bean的常见属性
id标签是bean的唯一标识,IoC容器中bean的id标签不能重复,否则报错。
name是bean的别名,可以定义多个
class属性是bean常用属性,为bean的全限定类名,指向classpath下类定义所在位置
factory-method属性factory-method工厂方法属性,通过该属性,我们可以调用一个指定的静态工厂方法,创建bean实例。
scope属性 bean的作用范围(singleton — 单例, prototype — 多例)
init-method和destory-method属性 Bean的初始化和销毁时执行的方法
autowire属性 表示bean的自动装配方式 可选值: no:默认,不进行自动装配 .byName .byType
1.5 IOC容器Bean的作用域
singleton:表示整个IOC容器共享一个Bean,也就是说每次说每次通过getBean获取的bean都是同一个。
prototype:每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。
request:每次HTTP请求将会生成各自的bean实例
session:每次会话请求对应一个bean实例
global-session:全局作用域
* singleton和prototype经常使用,其他的很少使用。
1.6 IOC依赖注入方式
● 构造器注入: <constructor-arg> 标签
● setter方法注入: <property name = "xxxx" value = "xxx"></property>
● 根据注解注入: @Autowired, @Resource
1.7 IOC容器中重要的类
IOC容器的顶级接口 BeanFactory , 仅提供了最基本的容器方法
ApplicationContext接口 则扩展了 BeanFactory ,提供了很多功能,使我们常用的容器
BeanFacotry的实现类 XmlBeanFactory
ApplicationContext的实现类:
- ClassPathXmlApplicationContext:可以加载类路径下的配置文件,实现容器的实例化。
- FileSystemXmlApplicationContext:可以加载磁盘任意路径下的配置文件,实现容器的实例化。(必须有访问权限)
- AnnotationConfigApplicationContext :扫描配置注解,根据注解配置实现容器的实例化。
1.8 Spring常用注解
@ComponentScan 组件扫描, 会扫描指定包下的 @Component @Controller @Service @Repository
@Controller 标注当前类为容器的对象,代表控制组件
@Service 标注当前类为容器的对象,代表服务组件
@Repository 标注当前类为容器的对象,代表持久组件
@Component 标注当前类为容器的对象,代表组件
@Autowired 自动装配注解 默认按类型装配
@Transactional 事务注解
@Value 将配置文件中的值,注入到指定字段
@Bean 配置bean对象 标签作用一致
@Configuration 标注当前的java类是一个配置类
1.9 Autowired和Resource注解区别
@Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。
@Autowired默认按类型装配(这个注解是属于spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以搭配@Qualifier注解进行使用
@Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
2. Spring面试重点
2.1 IOC容器初始化流程
- 创建配置
(1) 基于xml 或 基于注解 配置Spring Bean - 配置解析
(1) 读取xml内容,并得到一个Document对象
(2) 解析Document对象,遍历bean标签的节点
(3) 将每一个Bean标签封装成一个BeanDefinition对象
- 注册bean定义到容器中
(1) 将bean的定义对象 存储到容器中的map集合中
(2) map集合: 在容器的核心实现类 DefaultListableBeanFactory中, ConcurrentHashMap
(3) 以 bean标签中配置的id作为key
(4) 以 beanDefinition对象作为value存储到map集合中
- 初始化所有单例对象
(1) 完成注册后,查看所有注册bean定义
(2) 如果是非抽象 、 并且不是懒加载的单例对象会被立刻创建
(3) 创建出来的单例对象会存储到singletonObjects map集合中 也是一个ConcurrentHashMap
2.2 IOC容器getBean流程
- 判断单例map集合中 是否已经创建该对象
>有,返回
>无,下一流程 - 判断是否存在父容器,且父容器是否包含该对象
>包含,返回
>不包含,下一流程 - 准备尝试创建对象,beanDefinitionMap中获取bean定义
>无定义,抛出NoSuchBeanDefinition异常
>有定义,下一流程 - 判断bean定义的scope属性
>单例 (1) 调用createBean创建对象 (2) 加入单例map集合
>多例 调用createBean创建对象
>其他 createBean创建对象
(1) 获取要创建bean的class
(2) 判断是否使用工厂方法创建对象
是 —-> 调用工厂方法创建对象
否 —-> 使用构造器创建
(1) 根据构造器参数的个数、类型(2) 确定到底使用哪个构造器创建
(3) 使用JDK反射方法创建对象依赖注入 —-> 调用populateBean方法进行依赖注入
2.3 SpringBean的循环依赖和覆盖问题
2.3.1 循环依赖
多例 : 对象都是多例对象,会报循环依赖异常
单例:
- 如果是构造器依赖的属性,会报循环依赖异常
- 如果是通过属性依赖产生的循环依赖,默认允许循环依赖 (通过Spring的三级缓存解决)
- 设置allowCircularReferences为false会报循环依赖异常
2.4 Spring管理Bean的生命周期
- bean对象的实例化
- 封装属性,也就是设置properties中的属性值
- 如果bean实现了BeanNameAware,则执行setBeanName方法,也就是bean中的id值
- 如果实现BeanFactoryAware或者ApplicationContextAware ,需要设置setBeanFactory或者上下文对象setApplicationContext
- 如果存在类实现BeanPostProcessor后处理bean,执行postProcessBeforeInitialization,可以在初始化之前执行一些方法
- 如果bean实现了InitializingBean,则执行afterPropertiesSet,执行属性设置之后的操作
- 调用
执行指定的初始化方法 - 如果存在类实现BeanPostProcessor则执行postProcessAfterInitialization,执行初始化之后的操作
- 执行自身的业务方法
- 如果bean实现了DisposableBean,则执行spring的的销毁方法
- 调用
执行自定义的销毁方法。 2.5 SpringAOP面试点
2.5.1 谈谈你对AOP的理解
Aspect Orieted Programming ,中文叫“面向切面编程”或“面向方面编程”
是一种编程模式,将分布在多个类中的功能封装到一个类中,这些功能称为cross-cutting concerns(横切关注点),如日志、事务、缓存权限、安全等
-
2.5.2 项目中AOP的实际应用场景
事务
- 权限
- 异常
- 用户行为日志
- 性能监控
- 缓存等
2.5.3 AOP的具体使用方式
- 定义切面类 我们使用注解的方式 @Aspect @Component
- 定义切入点表达式 可以通过 扫描包的方式 也 可以通过指定注解的方式
-
2.5.4 例: 以用户行为日志举例AOP的使用
日志效果
- 定义日志注解,在需要记录日志的方法中加上注解
- 引入AOP依赖
- 定义切面及切入点
- 定义环绕通知,记录日志内容
![)G(TFZDF81OBAG[TN~~}[B.png](https://cdn.nlark.com/yuque/0/2022/png/26104240/1644409139139-031da636-3447-4fb6-a964-8a5778040290.png#clientId=u596893f3-152f-4&crop=0&crop=0&crop=1&crop=1&from=ui&height=363&id=u35a9a95f&margin=%5Bobject%20Object%5D&name=%29G%28TFZDF81OBAG%5BTN~~%7D%5BB.png&originHeight=685&originWidth=1182&originalType=binary&ratio=1&rotation=0&showTitle=false&size=95000&status=done&style=none&taskId=u4fcdf188-5180-4588-aefd-cebfd03f8b5&title=&width=627)
—-> ThreadLocal获取当前登录用户信息
—-> 将日志行为信息保存数据库
2.5.5 例: 基于AOP实现读写分离数据源动态处理
- AOP的底层的工作原理 就是使用动态代理对目标业务进行增强
- 而动态代理主要有JDK 和 CGLIB两种动态代理实现
- JDK是自带的的动态代理实现,要求被代理的类必须实现接口
- CGLIB是开源的动态代理实现,需要引入依赖,要求被代理的类必须可以被继承
2.6 Spring事务面试点
2.6.1 Spring事务的配置方式
- Spring 默认使用数据库的隔离级别
- ISOLATION_READ_UNCOMMITTED:读未提交
- ISOLATION_READ_COMMITTED:读已提交
- ISOLATION_REPEATABLE_READ:可重复读
- ISOLATION_SERIALIZABLE:串行化
2.6.3 Spring事务的失效场景
- 数据库引擎不支持事务(innoDB )
- 没有被Spring管理
- * 方法不是public的
- * 自身调用问题
- 数据源未配置事务管理
- 传播行为中设置了不支持事务
- * 异常被捕获掉
- * 异常类型错误, 默认捕获RuntimeException异常
2.6.4 Spring事务的传播行为
PROPAGATION_REQUIRED 表示当前方法必须在一个具有事务的 上下文中运行,如有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。( 如果被调用端发生异常,那 么调用端和被调用端事务都将回滚)
PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
PROPAGATION_SUPPORTS 表示当前方法不必需要具有一个事务 上下文,但是如果有一个事务的话,它也可以在这个事务中运行
PROPAGATION_MANDATORY 表示当前方法必须在一个事务中运行,如果没有事务,将抛出异常
PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。
PROPAGATION_NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常
PROPAGATION_NESTED 表示如果当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中 ,被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。如果封装事务不存在,则同propagation. required的一样
2.7 Spring使用的设计模式
工厂模式: Spring中的FactoryBean就是典型的工厂方法模式。使用工厂模式创建对象的实例
单例模式:在Spring配置文件中定义的Bean默认为单例模式
模板方法:用来解决代码重复的问题,比如我们常使用的RestTemplate、JmsTemplate、JpaTemplate都是基于模板方法
代理模式: AOP的底层实现就是基于JDK动态代理 及 CGLIB动态代理
观察者模式: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 spring中Observer模式常用的地方是listener的实现。如ApplicationListener
适配器模式:AOP的方法拦截器中
策略模式:Spring中在实例化对象的时候用到Strategy模式
3. SpringMVC面试题
3.1 SpringMVC常用注解
@RequestMapping 指定请求资源路径
[@ResponseBody ](/ResponseBody ) 注解表示将该方法的返回值 直接返回到客户端,一般我们返回的都是 json
[@RequestBody ](/RequestBody ) 接收前台 json参数
[@RequestParam ](/RequestParam ) 接收前台表单参数 key = value 形式参数 ? a=1 & b=2
[@PathVariable ](/PathVariable ) 获取路径参数的 http:ip:port/user/{id}
@RestController 相当于 @ResponseBody 和 @Controller 的合体
@ControllerAdvice controller的增强注解,用于统一处理功能:如统一异常处理
@ExceptionHandler 异常处理
@CookieValue 指定cookie中的参数
@RequestHeader 指定请求消息头中的参数
3.2 SpringMVC运行流程
- 用户发送请求至前端控制器DispatcherServlet;
- DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;
- 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
- DispatcherServlet 调用 HandlerAdapter处理器适配器;
- HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器);
- Handler执行完成返回ModelAndView;
- HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
- DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
- ViewResolver解析后返回具体View;
- DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
- DispatcherServlet响应用户。
3.3 Filter、Interceptor、Aop实现与区别
- AOP使用的主要是动态代理 ,
- 过滤器使用的主要是函数回调;
拦截器使用是反射机制 。
一个请求过来 ,先进行过滤器处理,看程序是否受理该请求 。 过滤器放过后 程序中的拦截器进行处理 ,处理完后进入 被 AOP动态代理重新编译过的主要业务类进行处理 。
Filter:和框架无关,可以控制最初的http请求,但是更细一点的类和方法控制不了。
Interceptor:可以控制请求的控制器和方法,但控制不了请求方法里的参数(用于处理页面提交的请求响应并进行处理,例如做国际化,做主题更换,过滤等)。
Aspect : 可以自定义切入的点,有方法的参数,但是拿不到http请求,可以通过其他方式如RequestContextHolder获得。
都可以实现权限检查,日志记录。不同的是使用的范围不同,规范不同,深度不同。
3.4 SpringMVC如何上传图片
- 依赖两个jar包 commons-io.jar 及 commons-fileupload.jar
- 首先在spring mvc的配置文件中配置 Mutipart解析器
- 然后前端上传图片的时候需要在form 表单上加上enctype=mutilpart-data
- 后台controller的接收方法中 使用spring的 MutilpartFile接收文件对象
4. Mybatis面试题
4.1 #{}和${}的区别是什么
{}是预编译处理,${}是字符串替换。
- Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
- Mybatis在处理${}时,就是把${}替换成变量的值。
- 使用#{}可以有效的防止SQL注入,提高系统安全性。
- 使用建议:
能用 #{} 的地方就用 #{},不用或少用 ${}
表名作参数时,必须用 ${}。如:select * from ${tableName}
order by 时,必须用 ${}。如:select * from t_user order by ${columnName}
使用 ${} 时,要注意何时加或不加单引号,即 ${} 和 ‘${}’
4.2 实体类中的属性名和表中的字段名不一样,怎么办
写sql语句时起别名 ,别名和属性相对应
在MyBatis的全局配置文件中开启驼峰命名规则 ,mybatis会将数据库中 下划线的字段自动转为驼峰格式的属性
在Mapper映射文件中使用resultMap来自定义映射规则
4.3 常见的mybatis的动态标签
-
4.4
标签的作用及如何使用 collection 集合名称
- item 每个元素名称
- separator 分隔符
4.5 Mapper接口里的方法,参数不同时,方法能重载吗
不可以,不论是注解配置的mybatis 还是xml配置的mybatis,被解析后每个方法都会封装成一个MappedStatement 存储到一个map集合中,集合的key使用的是mapper接口的全限类名+方法名,value就是MappedStatement对象。
如:
我想写个重载方法 findUser 一个带ID参数 返回值User 一个不带参数 返回值为List但是这个方法在map中寻找时都是以 xxx.UserMapper.findUser作为key ,会发生冲突 实际上这个时候mybatis也会报错。 4.6 如何获取自动生成的(主)键值
- 可以通过查询最后一条的sql语句
SELECT LAST_INSERT_ID()
- 也可以通过mybatis的标签属性
4.7 在Mapper中如何传递多个参数
通过实体类传参
通过map传参
通过定义多个@Param注解传参
4.8 MyBatis如何执行批量插入
- 通过mybatis的
动态标签 利用 MyBatis 批处理特性,批量提交(ExecutorType.BATCH)
* SimpleExecutor 简单执行器
* ReuseExecutor 可复用的执行器
* BatchExecutor 批量4.9 介绍一下MyBatis的二级缓存
一级缓存: 一级缓存:默认开启 一级缓存的范围是同一个SqlSession对象,当我们使用SqlSession对象进行查询时mybatis会帮我们把查询的数据存入到内存中,当我们在这个SqlSession中再一次执行同样的查询操作时,我们就可以直接去缓存中获取数据
二级缓存: 二级缓存:默认关闭 二级缓存是mapper级别的缓存,多个SqlSession共享,其作用域是mapper的同一个namespace,不同的SqlSession两次执行相同的namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次查询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。
4.10 一对一、一对多的关联查询
一对一: 在xml中定义映射关系, 使用association代表关联一个实体类, property映射的属性名,javaType该属性的实体类类型,fetchType是否延迟加载,column用于执行select方法所传递的参数,select需要执行的mapper方法的全限定名称
一对多: 在xml中定义映射关系,因为是一个集合 所以使用collection标签,ofType代表集合中实体类的类型,其他属性和上面对一的情况一致
4.11 MyBatis是如何进行分页的
通过pageHelper插件进行分页:
PageHelper.startPage(1,1); // 在代码中执行startPage方法 第一个参数第几页,第二个是几条数据 PageHelper分页的实现在我们执行SQL语句之前动态的将SQL语句拼接了分页的语句,从而实现了从数据库中分页获取数据的过程。
4.12 MyBatis与Hibernate有哪些不同
(1)Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句。
(2)Mybatis直接编写原生态sql,可以严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需求变化要求迅速输出成果。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件,则需要自定义多套sql映射文件,工作量大。
(3)Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,如果用hibernate开发可以节省很多代码,提高效率。
4.13 Mybatis中使用到了哪些设计模式
- Builder模式,例如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder;
- 工厂模式,例如SqlSessionFactory、ObjectFactory、MapperProxyFactory;
- 单例模式,例如ErrorContext和LogFactory;
- 代理模式,Mybatis实现的核心,比如MapperProxy、ConnectionLogger,用的jdk的动态代理;还有executor.loader包使用了cglib或者javassist达到延迟加载的效果;
- 装饰者模式,例如Cache包中的cache.decorators子包中等各个装饰者的实现;
- 组合模式,例如SqlNode和各个子类ChooseSqlNode等;
- 模板方法模式,例如BaseExecutor和SimpleExecutor,还有BaseTypeHandler和所有的子类例如IntegerTypeHandler;
- 适配器模式,例如Log的Mybatis接口和它对jdbc、log4j等各种日志框架的适配实现;
- 迭代器模式,例如迭代器模式PropertyTokenizer;
5. SpringBoot
5.1 Spring 和 SpringBoot的区别
5.1.1 SpringBoot介绍
SpringBoot 是Spring旗下的一款开发框架,他可以让我们创建基于Spring的应用变得更加的方便, SpringBoot 整合了常用的Spring和第三方的类库,整合后提供带Starter的pom,整合后的项目采用自动配置,如果配置不满足需要,SpringBoot也提供了配置文件 只需简单的修改便可以。 SpringBoot内置了 servlet容器(tomcat,jboss)使得我们的项目启动部署更加的方便 (java -jar xx.jar) Springboot 提供了企业级应用的健康检测方案
5.1.2 SpringBoot优点
- 快速搭建项目,
- 与主流框架集成无需配置集成.
- 内嵌服务容器.
- 具有应用监控.
- 开发部署方便
5.2 SpringBoot中常见的starter
参考: Spring官方文档5.3 SpringBoot的配置文件
springboot启动后 会默认的去指定文件夹加载 名称为 application的文件,后缀为
.properties 或 .yml .yaml
application.properties/yml
bootstrap.properties/yml
如果在同一目录下出现多个配置文件: springboot会都加载
application.properties > .yml > .yaml
属性名相同: 优先级高的生效
属性名不同: 都生效
项目目录:
config:
application.yml
项目目录:
application.yml
项目目录:
resources:
config
application.yml
项目目录:
resources:
application.yml5.4 yml配置文件语法
配置普通数据:
语法: key: value
如:
注意: : 和 value间一定要有空格name: xiaoming
配置对象或map数据:
语法: key:
key1: value1
key2: value2
或: key: {key1: value1,key2: value2}
如:
person:
name: xiaoming
first: 1
age: 3
注意: 相同缩进代表同一级别. name和age同一级别都是person的属性
如果属性有了值就不能在有下级属性
配置集合数组:
语法: key:
- value1
- value2
或: key: [value1,value2]
注意: -符号 一定要在同一级别,并且和属性间要有空格
配置对象集合
语法: key:
- key1: value
key2: value
- key1: value
key2: value
或: key: [{key1: value1,key2: value2},{key1: value1,key2: value2}]
注意: -符号 一定要在同一级别,并且和属性间要有空格
5.5 如何读取自定义的配置
@Value ```java @Value(“${name}”)
String name;
@Value("${person.name}")
String personName;
@Value("${city[0]}")
String cityFirst;
- @ConfigurationProperties(prefix = "xxl.job")
```java
@Data
@Component
@ConfigurationProperties(prefix = "config-attributes")
public class ConfigModel {
private String val;
private int[] valArr;
private List<String> cityList;
private Map valMap;
private List<Map> valMapList;
}
5.6 如何定义多环境配置
如果所有环境配置都写到一个配置文件中,不好维护
可以定义多个环境配置文件的方式来实现:
如:
application-{环境名称}.yml
application-dev.yml
application-test.yml
application-prod.yml
spring:
profiles:
active: prod 激活生产环境的配置