5、mini-spring-study
1、IOC
2、循环依赖
2.1、解决问题
循环依赖问题:Abean -> Bbean -> Cbean -> Abean
创建 Bean 的过程
- 根据 Bean 定义生成 BeanDefinition
- 根据定义加载 Bean 进行实例化
- 最后注入属性
在注入属性之前,其实这个 Bean 的实例已经生成出来了
只不过此时的实例还不是一个完整的实例,它还有很多属性没有值,而我们现在讨论的 Bean 之间的依赖是在 "注入属性" 这一阶段
因此我们可以在 "实例化" 与 "注入属性" 这两个阶段之间增加一个环节,确保给 Bean 注入属性的时候,Spring 内部已经准备好了 Bean 的实例
Spring 的做法是在 BeanFactory 中引入一个结构:earlySingletonObjects,存放的就是没有 "已经实例化" But "没有注入属性" 的 Bean 实例
创建 Bean 实例的时候,不用等到所有步骤完成,而是可以在属性还没有注入之前,就先把不完整的 Bean 实例先保存起来,供 "注入属性" 时使用
2.2、解决示例
- 实例化 ABean,此时它是不完整的实例,好多属性还没被赋值
将实例放到 earlySingletonObjects 中备用,然后给 ABean 注入属性,发现它依赖 BBean - 实例化 BBean,此时它也是不完整的实例
将实例放到 earlySingletonObjects 中备用,然后给 BBean 注入属性,发现它依赖 CBean - 实例化 CBean,此时它仍然是不完整的实例
将实例放到 earlySingletonObjects 中备用,然后给 CBean注入属性,发现它反过来依赖 ABean - 从 earlySingletonObjects 结构中找到 ABean 的不完整的实例,取出来给 CBean 注入属性,这样就先创建好了 CBean
- 程序控制流回到第二步,完成 BBean 的属性注入
- 程序控制流回到第一步,完成 ABean 的属性注入
通过上述过程可以知道,这一系列的 Bean 是纠缠在一起创建的,我们不能简单地先后 "独立" 创建它们,而是要作为一个 "整体" 来创建
2.3、Setter 注入和构造器注入
通过 Setter 注入的方式能解决循环依赖的问题,原理是通过缓存的方式解决的,这里的关键点是:"注入属性" 在 "bean 实例化" 之后 "独立" 进行
而构造器注入不能解决循环依赖问题,"注入属性" 和 "bean 实例化" 是 "同时" 进行的
在创建 bean 时,就需要将依赖的 bean 传入到构造函数中,如果依赖的 bean 尚未创建完成,就不能传入到构造函数中,循环依赖就无法解决
Spring 6 已经开始限制了循环依赖
循环依赖是一种非常糟糕的设计,往往意味着写出这段代码的程序员没有理清层级关系,没有设计好上下层的依赖
Spring 对于循环依赖的支持,反而导致了程序员写出了坏味道代码而不自知,或许从一开始 Spring 就不该支持循环依赖
所以 Spring 官方也建议大家使用构造器注入,一个是避免写出这种层级依赖不清晰的糟糕代码,二是也方便了后续单元测试的编写
3、IOC 类图
4、SpringMVC
listener、filter、servlet 的加载次序
为何 Spring MVC 可获取到方法参数名,而 MyBatis 却不行
所有请求都会执行 DispatcherServlet.service(HttpServletRequest, HttpServletResponse) 处理器映射器 HandlerMapping 扫描所有 Controller 类中,被 @RequestMapping 标注的方法,并进行映射 url -> ControllerObj + Method 处理器适配器 HandlerAdapter 通过 HttpServletRequest 获取方法参数 通过 ControllerObj + Method 执行方法 如果方法被 @ResponseBody 标注,就将结果写入 HttpServletResponse,返回 null 否则:返回值如果是 String,创建 ModelAndView(viewName),返回 mv 视图解析器 ViewResolver 根据 viewName 创建视图对象 View 视图 View 将 model 中的数据放入 request 域中(HttpServletRequest.setAttribute) 通过 HttpServletRequest 请求转发到 URL
5、Bean 生命周期
6、JDBC
public class JDBCTest { public static void main(String[] args) throws Exception { Connection connection = null; PreparedStatement prepareStatement = null; ResultSet rs = null; try { // 加载驱动 Class.forName("com.mysql.cj.jdbc.Driver"); // 获取连接 String url = "jdbc:mysql://127.0.0.1:3306/ssmdemo"; String user = "root"; String password = "root"; connection = DriverManager.getConnection(url, user, password); // 获取 statement, preparedStatement String sql = "select * from tb_user where id = ?"; prepareStatement = connection.prepareStatement(sql); // 设置参数 prepareStatement.setLong(1, 1L); // 执行查询 rs = prepareStatement.executeQuery(); // 处理结果集 while (rs.next()) { System.out.println(rs.getString("user_name")); System.out.println(rs.getString("name")); System.out.println(rs.getInt("age")); System.out.println(rs.getDate("birthday")); } } finally { // 关闭连接, 释放资源 if (rs != null) rs.close(); if (prepareStatement != null) prepareStatement.close(); if (connection != null) connection.close(); } try { connection.setAutoCommit(false); // 开启事务 // 业务执行 ... connection.commit(); // 提交事务 } catch (Exception e) { connection.rollback(); // 回滚事务 } finally { connection.close(); // 关闭事务 } } }
本文来自博客园,作者:lidongdongdong~,转载请注明原文链接:https://www.cnblogs.com/lidong422339/p/17556930.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步