只是不愿随波逐流 ...|

lidongdongdong~

园龄:2年7个月粉丝:14关注:8

5、mini-spring-study

手把手带你写一个 MiniSpring

1、IOC

image
image

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 是纠缠在一起创建的,我们不能简单地先后 "独立" 创建它们,而是要作为一个 "整体" 来创建
image

2.3、Setter 注入和构造器注入

通过 Setter 注入的方式能解决循环依赖的问题,原理是通过缓存的方式解决的,这里的关键点是:"注入属性" 在 "bean 实例化" 之后 "独立" 进行

而构造器注入不能解决循环依赖问题,"注入属性" 和 "bean 实例化" 是 "同时" 进行的
在创建 bean 时,就需要将依赖的 bean 传入到构造函数中,如果依赖的 bean 尚未创建完成,就不能传入到构造函数中,循环依赖就无法解决

Spring 6 已经开始限制了循环依赖
循环依赖是一种非常糟糕的设计,往往意味着写出这段代码的程序员没有理清层级关系,没有设计好上下层的依赖
Spring 对于循环依赖的支持,反而导致了程序员写出了坏味道代码而不自知,或许从一开始 Spring 就不该支持循环依赖
所以 Spring 官方也建议大家使用构造器注入,一个是避免写出这种层级依赖不清晰的糟糕代码,二是也方便了后续单元测试的编写

3、IOC 类图

image
image

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

image
image
image

5、Bean 生命周期

image

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(); // 关闭事务
}
}
}
posted @   lidongdongdong~  阅读(41)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开