Spring 相关
- Spring IOC 和 DI
- IOC(控制反转):简单说就是把原来程序里自己创建对象、管理对象调用的权力,交给了 Spring 容器。原来我们在代码里直接 new 对象,现在由容器来负责对象的创建、装配和管理,对象的控制权从代码转移到了外部容器。
- DI(依赖注入):在创建对象的时候,容器会把这个对象所依赖的其他属性(比如其他对象、值等)自动填充到类里面。
- 依赖注入的实现方式
- 有接口注入、Setter 方法注入和构造器注入这三种。
- 构造器依赖注入:容器通过调用类的构造器来实现,构造器里的每个参数都代表着对其他类的依赖。
- Setter 方法注入:容器先通过无参构造器或者无参的 static 工厂方法创建出 bean,然后调用这个 bean 的 setter 方法来完成注入。
- Spring 中 bean 的作用域(Scope)
- Spring 支持五种作用域:
- singleton:在每个 Spring IOC 容器里,这个 bean 只有一个实例。
- prototype:一个 bean 定义可以有多个实例。
- request:每次 HTTP 请求都会创建一个新的 bean,只在基于 web 的 Spring ApplicationContext 里有效。
- session:在一个 HTTP Session 中,一个 bean 定义对应一个实例,同样只在基于 web 的 Spring ApplicationContext 里有效。
- global-session:在全局的 HTTP Session 中,一个 bean 定义对应一个实例,也是仅在基于 web 的 Spring ApplicationContext 里有效。
- Spring 单例 bean 的线程安全性
- 单例 bean 本身不是线程安全的,因为 Spring 框架没有对单例 bean 做多线程的封装处理。不过我们平时用单例 bean 时,一般不会设置共享数据,所以通常不会有线程安全问题,从这方面看也能说它是线程安全的。
- Spring 自动装配 bean 的方式
- 在 xml 配置里有五种自动装配方式:
- byName:根据 bean 的名称来自动装配,如果一个 bean 的 property 和另一个 bean 的 name 相同,就会进行装配。
- byType:按照参数的数据类型来自动装配。
- constructor:利用构造函数进行装配,而且构造函数的参数是通过 byType 来匹配的。
- Spring 事务的实现
- 底层是基于数据库事务和 AOP 机制实现的。
- 对于加了 @Transactional 注解的 Bean,Spring 会创建一个代理对象。当调用代理对象的方法时,会先判断方法上有没有 @Transactional 注解。
- 如果有的话,就用事务管理器创建一个数据库连接,并且把这个连接的 autocommit 属性设为 false,禁止自动提交。
- 然后执行当前方法里的 SQL,执行完如果没出现异常就提交事务,出现需要回滚的异常就回滚,否则还是提交。
- 事务的隔离级别和数据库的隔离级别对应,而传播机制是 Spring 自己实现的,基于数据库连接,一个连接对应一个事务,要是传播机制要求新开事务,就新建一个数据库连接来执行 SQL。
- Spring 事务失效的场景
- 因为事务是基于代理实现的,所以加了 @Transactional 的方法只有被代理对象调用时才生效,被代理对象自己调用的话会失效。
- 如果方法是 private 的,@Transactional 也会失效,因为 CGLIB 代理是基于父子类,子类不能重载父类的 private 方法,没法很好地代理。
- 业务中如果自己捕获了异常,Spring 感知不到异常,事务也会失效。
- Spring 事务的传播行为
- PROPAGATION_REQUIRED:当前没事务就新建一个,有就加入,这是最常用的。
- PROPAGATION_SUPPORTS:支持当前事务,有就加入,没有就以非事务方式执行。
- PROPAGATION_MANDATORY:必须要有当前事务,有就加入,没有就抛异常。
- PROPAGATION_REQUIRES_NEW:不管当前有没有事务,都新建一个事务。
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行,有当前事务就把它挂起。
- PROPAGATION_NEVER:以非事务方式执行,有当前事务就抛异常。
- PROPAGATION_NESTED:当前有事务就在嵌套事务里执行,没有就按 REQUIRED 的规则来。
- JDK 动态代理和 CGLIB 动态代理的区别
- 这是 Spring AOP 里的两种动态代理方式:
- JDK 动态代理:只支持对接口的代理,用 Proxy.newProxyInstance (类加载器,代理对象实现的所有接口,代理执行器) 来创建代理。
- CGLIB 动态代理:通过继承的方式实现,要是类被标记为 final,就不能用它来代理了,用 Enhancer.create (父类的字节码对象,代理执行器) 来创建代理。
- AOP 及项目中的使用
- AOP(面向切面编程):作为面向对象的补充,把那些和业务无关但对多个对象有影响的公共逻辑(比如日志、权限等)抽取出来做成一个可重用的 “切面”,这样能减少重复代码,降低模块耦合度,方便维护。
- 项目中自己写 AOP 的情况不多,但很多框架功能底层都用到了,比如权限认证、日志记录、事务处理等。
SpringMVC 相关
- SpringMVC 的执行流程
- 用户发请求给前端控制器 DispatcherServlet。
- DispatcherServlet 调用 HandlerMapping 处理器映射器获取 Handler。
- 处理器映射器根据请求 url 找到具体处理器,生成处理器对象和拦截器(如果有的话)返回给 DispatcherServlet。
- DispatcherServlet 调用 HandlerAdapter 处理器适配器。
- HandlerAdapter 适配后调用具体的处理器(Handler)。
- Handler 执行完返回 ModelAndView。
- HandlerAdapter 把 ModelAndView 返回给 DispatcherServlet。
- DispatcherServlet 把 ModelAndView 传给 ViewResolver 视图解析器解析。
- ViewResolver 解析后返回具体的 View。
- DispatcherServlet 对 View 进行渲染(填充模型数据)。
- 最后 DispatcherServlet 响应给用户。
- Spring MVC 常用的注解
- @RequestMapping:处理请求 url 映射,可用于类或方法,类上的话就是父路径。
- @RequestBody:接收 http 请求的 json 数据,把 json 转成 java 对象。
- @ResponseBody:把控制器方法返回的对象转成 json 响应给客户。
- @Controller:标记控制器,属于表现层,不能用其他注解代替。
- @RestController:是 @Controller + @ResponseBody 的组合注解。
- @GetMapping、@PostMapping、@PutMapping、@DeleteMapping等:对应不同 HTTP 方法的请求映射。
- @PathVariable:接收请求路径中的变量。
- @RequestParam:接收请求参数。
MyBatis 相关
- Mybatis #{} 和 ${} 的区别
- #{} 是占位符,会做预编译处理;${} 是拼接符,做字符串替换,不预编译。
- MyBatis 处理 #{} 时,会把 SQL 里的 #{} 换成?,用 PreparedStatement 的 set 方法赋值,能防止 SQL 注入,安全性高,变量替换在数据库系统中进行。
- ${} 不能防止 SQL 注入,变量替换在数据库系统外进行。
- Mybatis 获取生成的主键
- 有两种方式:
- 在 insert 标签上用 useGeneratedKeys="true" 和 keyProperty="userId"。
- 在 insert 标签内部用 selectKey 标签,里面用 select last_insert_id () 查询生成的 ID 返回。
- 实体类属性名和表字段名不一样的解决办法
- 第一种:在查询 SQL 里给字段名起别名,让别名和实体类属性名一致。
- 第二种:通过 ResultMap 来映射字段名和实体类属性名。
- Mybatis 实现多表查询的方式
- 第一种:写多表关联查询的 SQL,用 ResultMap 建立结果集映射,里面用 association(一对一)和 collection(一对多)标签。
例如:
- xml
<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> <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 的定义去执行,结果会自动封装。
例如:
- xml
<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> <select id="findByIdWithUser" resultMap="Account_User_Map"> select * from account where id = #{id} </select>
- Mybatis 的动态 sql 及执行原理
- 动态 sql 是在 Xml 映射文件里用标签写的,能做逻辑判断和动态拼接 sql,有 trim、where、set、foreach、if、choose、when、otherwise、bind 这 9 种标签。
- 执行原理是用 OGNL 从 sql 参数对象里计算表达式的值,根据值来动态拼接 sql。
- Mybatis 对延迟加载的支持
- 只支持 association(一对一)和 collection(一对多)的延迟加载,在配置文件里通过设置 lazyLoadingEnabled=true|false 来启用或关闭。
- Mybatis 实现批量插入
- 用 foreach 标签,它能迭代集合,属性有:
- collection:要遍历的集合。
- item:迭代时每个元素的别名。
- index:迭代位置,不常用。
- open:语句的开始部分。
- separator:迭代元素之间的分隔符。
- close:语句的结束部分。
- Mybatis 批量插入能否返回主键
- 可以,返回的主键会封装在传入集合的每个对象的属性里。
- Mybatis 的一级、二级缓存
- 一级缓存:基于 SqlSession 级别,默认开启。
- 二级缓存:基于 SqlSessionFactory 的 NameSpace 级别,默认不开启,需要手动开启:
- 在配置文件里设置 cacheEnabled 为 true:
- xml
<settings> <setting name="cacheEnabled" value="true"/> </settings>
- 在映射配置文件里配置 cache 相关:
- xml
<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache>