1 Spring
1.1 什么是Spring IOC 和DI ?
① 控制反转(IOC):Spring容器使用了工厂模式为我们创建了所需要的对象,我们使用时不需要自己去创建,直接调用Spring为我们提供的对象即可,这就是控制反转的思想。
② 依赖注入(DI):Spring使用Java Bean对象的Set方法或者构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程就是依赖注入的基本思想。
1.2 你用过哪些重要的Spring注解?
1、@Component- 用于服务类。
2、@Autowired - 用于在 spring bean 中自动装配依赖项。通过类型来实现自动注入bean。和@Qualifier注解配合使用可以实现根据name注入bean。
3、@Qualifier - 和@Autowired一块使用,在同一类型的bean有多个的情况下可以实现根据name注入的需求。
4、@Scope - 用于配置 spring bean 的范围。
5、@Configuration,@ComponentScan 和 @Bean - 用于基于 java 的配置。
6、@Aspect,@Before,@After,@Around,@Pointcut - 用于切面编程(AOP)
1.3 Spring中的事务是如何实现的
编程式事务控制:需要使用TransactionTemplate来进行实现,这种方式实现对业务代码有侵入性,因此在项目中很少被使用到。
声明式事务管理:声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在
目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过@Transactional 注解的方式,便可以将事务规则应用到业务逻辑中。
1.4 Spring中事务失效的场景?
因为Spring事务是基于代理来实现的,所以某个加了@Transactional的⽅法只有是被代理对象调⽤时, 那么这个注解才会⽣效 , 如果使用的是目标对象调用, 那么@Transactional会失效
同时如果某个⽅法是private的,那么@Transactional也会失效,因为底层cglib是基于⽗⼦类来实现 的,⼦类是不能重载⽗类的private⽅法的,所以⽆法很好的利⽤代理,也会导致@Transactianal失效
如果在业务中对异常进行了捕获处理 , 出现异常后Spring框架无法感知到异常, @Transactional也会失效
@Transactional中的配置的Rollback的默认是异常是:runTimeException,更改为Exception
1.5 说一下Spring的事务传播行为
1.6 ❤️什么是AOP , 你们项目中有没有使用到AOP
AOP一般称为面向切面编程,作为面向对象的一种补充,用于 将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模 块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时 提高了系统的可维护性。
在我们的项目中我们自己写AOP的场景其实很少 , 但是我们使用的很多框架的功能底层都是AOP , 例如 :
1、统一日志处理
2、spring中内置的事务处理
记录操作日志
需求:是谁,在什么时间,修改了数据(修改之前和修改之后),删除了什么数据,新增了什么数据
要求:方法命名要规范
实现步骤:
1,定义切面类
2,使用环绕通知,根据方法名和参数,记录到表中
1.7 JDK动态代理和CGLIB动态代理的区别
Spring中AOP底层的实现是基于动态代理进行实现的。
常见的动态代理技术有两种:JDK的动态代理和CGLIB。
两者的区别如下所示:
1、JDK动态代理只能对实现了接口的类生成代理,而不能针对类
2、Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法进行增强,但是因为采用的是继承,所以该类或方法最好不要声明为final,对于final类或方法,是无法继承的。
Spring如何选择是用JDK还是cglib?
1、当bean实现接口时,会用JDK代理模式
2、当bean没有实现接口,会用cglib实现
3、可以强制使用cglib
- 在springboot项目可以配置以下注解,强制使用cglib@EnableAspectJAutoProxy(proxyTargetClass = true)
2 SpringMVC
2.1 Spring MVC中的拦截器和Servlet中的filter有什么区别?
过滤器:依赖于servlet容器,在实现上基于函数回调,可以对几乎所有请求进行过滤
拦截器:依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架,属于面向切面编程(AOP)的一种运用。
2.2 SpringMVC的执行流程知道嘛
具体流程如下所示:
1、用户发送出请求到前端控制器DispatcherServlet。
2、DispatcherServlet收到请求调用HandlerMapping(处理器映射器)。
3、HandlerMapping找到具体的处理器(可查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
4、DispatcherServlet调用HandlerAdapter(处理器适配器)。
5、HandlerAdapter经过适配调用具体的处理器(Handler/Controller)。
6、Controller执行完成返回ModelAndView对象。
7、HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
8、DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
9、ViewReslover解析后返回具体View(视图)。
10、DispatcherServlet根据View进行渲染视图。
11、DispatcherServlet响应用户。
2.3 Spring MVC常用的注解有哪些?
1、@RequestMapping:用于映射请求路径,可以定义在类上和方法上。
2、@RequestBody:注解实现接收http请求的json数据,将json转换为java对象。
3、@RequestParam:指定请求参数的名称
4、@PathViriable:从请求路径下中获取请求参数(/user/{id}),传递给方法的形式参数
5、@ResponseBody:注解实现将controller方法返回对象转化为json对象响应给客户端。
6、@RequestHeader:获取指定的请求头数据
2.4 Spring MVC怎么处理异常?
可以直接使用Spring MVC中的全局异常处理器对异常进行统一处理,此时Contoller方法只需要编写业务逻辑代码,不用考虑异常处理代码。
开发一个全局异常处理器需要使用到两个注解:@Controlleradvice 、
3 Mybatis
3.1 Mybatis #{}和${}的区别
1、#{}是预编译处理,${}是字符串替换。
2、Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
3、Mybatis在处理${}时,就是把${}替换成变量的值。
4、使用#{}可以有效的防止SQL注入,提高系统安全性。
3.2 Mybatis 如何获取生成的主键
使用insert标签中的useGeneratedKeys和keyProperty 属性。使用方式如下所示:
<insert id = "saveUser" useGeneratedKeys = "true" keyProperty="id">
insert into tb_user(user_name,password,gender,addr) values (#{username},#{password},#{gender},#{addr})
</insert>
属性说明:
1、useGeneratedKeys:是够获取自动增长的主键值。true表示获取。
2、keyProperty :指定将获取到的主键值封装到哪儿个属性里
3.3 当实体类中的属性名和表中的字段名不一样 ,怎么办
第1种: 通过在查询的SQL语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。
<select id="getOrder" parameterType="int" resultType="com.heima.pojo.Order">
select order_id id,order_no orderno,order_price price from orders
</select>
第2种: 通过<resultMap>来映射字段名和实体类属性名的一一对应的关系。
<select id="getOrder" parameterType="int" resultMap = "orderResultMap">
select order_id id,order_no orderno,order_price price from orders
</select>
<resultMap type="com.heima.pojo.Order" id="orderResultMap">
<!-- 用id属性来映射主键字段 -->
<id property="id" column="order_id">
<!-- 用result属性来映射非主键字段,property为实体类属性名,column为数据库表中的属性 -->
<result property ="orderno" column="order_no" />
<result property ="price" column="order_price" />
</resultMap>
第3种,开启mybatis驼峰命名自动匹配映射
<settings>
<setting name="mapUnderscoreToCamelCase" value="true" /> <!-- 开启驼峰命名自动映射 -->
</settings>
3.4 Mybatis如何实现多表查询
Mybatis是新多表查询的方式也有二种 :
第一种是 : 编写多表关联查询的SQL语句 , 使用ResultMap建立结果集映射 , 在ResultMap中建立多表结果集映射的标签有association和collection
<resultMap id="Account_User_Map" type="com.heima.entity.Account">
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<association property="user">
<id property="id" column="uid"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
</association>
</resultMap>
<!--public Account findByIdWithUser(Integer id);-->
<select id="findByIdWithUser" resultMap="Account_User_Map">
select a.*, username, birthday, sex, address from account a , user u where a.UID = u.id and a.ID = #{id} ;
</select>
第二种是 : 将多表查询分解为多个单表查询, 使用ResultMap表的子标签association和collection标签的select属性指定另外一条SQL的定义去执行, 然后执行结果会被自动封装
<resultMap id="Account_User_Map" type="com.heima.entity.Account" autoMapping="true">
<id property="id" column="id"></id>
<association property="user" select="com.heima.dao.UserDao.findById" column="uid" fetchType="lazy"></association>
</resultMap>
<!--public Account findByIdWithUser(Integer id);-->
<select id="findByIdWithUser" resultMap="Account_User_Map">
select * from account where id = #{id}
</select>
3.5 Mybatis都有哪些动态sql?能简述一下动 态sql的执行原理吗?
Mybatis动态sql可以让我们在Xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态 拼接sql的功能,Mybatis提供了9种动态sql标签 trim|where|set|foreach|if|choose|when|otherwise|bind。
其执行原理为,使用OGNL从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql,以此 来完成动态sql的功能。
3.6 Mybatis是否支持延迟加载?
Mybatis仅支持association((a 塞 si A 什)关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。
<settings>
<setting name = "lazyLoadingEnabled" value = "true"> <!-- 开启延迟加载 -->
</settings>
3.7 如何使用Mybatis实现批量插入 ?
批量插入数据:
1、mybatis的接口方法参数需要定义为集合类型List
public abstract void saveUsers(List<User> users);
2、在映射文件中通过forEach标签遍历集合,获取每一个元素作为insert语句的参数值
<!-- 批量插入用户 -->
<insert id="savaUsers" parameterType="java.util.List">
insert into tb_user(user_name,password)
values
<foreach collection="list",item="user",index="index",separator=",">
(#{user.userName},#{user.password})
</foreach>
</insert>
3.8Mybatis的一级、二级缓存 ?
1、一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当Session进行flush或close之后,该Session中的所有Cache就将清空,默认打开一级缓存。如下所示:
使用同一个sqlSession对象获取两次UserMapper对象,进行了两次用户数据的查询。控制台的输出结果如下所示:
只执行了一次sql语句。说明第二次查询的时候使用的是缓存数据。
2、二级缓存:二级缓存是基于namespace和mapper的作用域起作用的,不是依赖于SQL session,默认也是采用 PerpetualCache,HashMap 存储。
如下代码:
当执行完sqlSession1.close()方法时一级缓存就一斤被清空掉了。再次获取了一个新的sqlSession对象,那么此时就需要再次查询数据,因此控制台的输
出如下所示:
可以看到进行了两次查询。
默认情况下二级缓存并没有开启,要想使用二级缓存,那么就需要开启二级缓存,如下所示:
① 全局配置文件
<settings>
<setting name="cacheEnabled" value="true"/> <!-- 开启二级缓存 -->
</settings>
② 映射文件
使用<cache/>标签让当前mapper生效二级缓存
<mapper namespace="com.itheima.mapper.UserMapper">
<cache/> <!-- 二级缓存生效 -->
<select id="selectAll" resultType="user">
select *
from tb_user;
</select>
<select id="selectById" resultType="user">
select *
from tb_user where id = #{id};
</select>
</mapper>
运行程序进行测试,控制台输出结果如下所示:
只进行了一次查询,那么就说明数据已经进入到了二级缓存中。
3、对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了新增、修改、删除操作后,默认该作用域下所有 select 中的缓存将被 clear。
注意事项:
1、二级缓存需要缓存的数据实现Serializable接口
2、只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中
3、可自定义存储源,如 Ehcache。