实况:老菜鸟自力更生从零开始重学spring目标是画出一张唬人大图(三、spring启动的入口搭建源码阅读环境)

news2024/9/23 5:33:39

前情提要:源码下载&编译

速览

  • 设计一个最简单的spring项目
  • spring的入口在哪?怎么启动的?
  • 搭建源码阅读环境
    • 新建module并添加依赖
    • 编写一个微型spring项目(配置bean->获取bean->使用bean)
  • 不打无准备之仗:源码一周目简单规划

设计一个最简单的spring项目

  这个问题貌似简单,但实际上绝对不是一个菜鸟能随便搞定的。首先我需要知道spring需要哪些最基本的配置(要对源码学习最有利),其次最好尽量不引入其它更多的框架(例如查询db时用到的持久化框架)。所以我想了下,这个简单的spring项目最好就是配置、创建、使用一个bean。在我肤浅的认知里,这正是spring的基础功能:管理bean。

spring的入口在哪?怎么启动的?

  有个关键问题需要想明白:spring是怎么“启动”的。回想一下,我平时工作时对spring的使用都是“无痕”的:在idea里写完业务代码,然后直接在idea内部启动tomcat后就可以本地调试了——这说明spring是被tomcat启动的。在此前的实况记录中也提到过,说到底spring本身也是一个java程序,所以入口是也就是个main方法。tomcat能通过某种方式执行main方法,那我通过某段代码也执行这个main方法不就“启动”了spring嘛。
  解下来的问题反而简单了,我只需写一个main方法“启动”spring即可。当然了,所谓的spring“启动”是做了哪些事,目前肯定是不知道的,这不是还没开始看源码么。那怎么写main方法呢?网上搜现成的呗,例如这篇,或者这篇,或者直接百度“搭建spring源码阅读环境”。里面都有类似“编写一个简单的applicationContext用于获取bean”的内容,其中的main方法的内容,其实就是在启动spring了。解下来就是本老鸟的操作。

搭建源码阅读环境

新建module并添加依赖

  此时,映入我眼帘的是编译后的spring源码,并且已经导入到了idea中:在这里插入图片描述
这时我有了一个不知当讲不当讲的问题,我的main方法写在哪呢?网上搜搜吧:
在spring-framework项目上右键:new>module,然后左边选Gradle,右边选Java,再补全命名信息后finish即可:
在这里插入图片描述  这时我新建的module是作为一个子module出现在spring-framework路径下的,编辑子模块的build.gradle文件,添加spring核心模块的依赖:
在这里插入图片描述
但此时我这里出现了报错:Could not find method compile() for arguments [project ‘:spring-context’] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.
这个问题是由于gradle版本问题导致的,不再支持"compile"语法,改为"api"问题消失。此时又出现了新问题:Could not find method testCompile() for arguments [{group=junit, name=junit, version=4.12}] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler. 看来"testCompile"语法也不支持了,改成“testImplementation”之后成功build。
在这里插入图片描述

编写一个微型spring项目(配置bean->获取bean->使用bean)

  发现刚才新建的module下并没有src路径,那就自己建:直接右键然后New》directory,最终建立一个简单的项目,目录结构就按照旧例:
在这里插入图片描述
其中Department和User分别是两个bean,将分别用两种方式进行配置和注入。前者通过xml方式配置,而后者通过注解方式配置:

package com.dustin.dto;


/**
 * @author qimy
 * @version 1.0
 * @Description
 * @date 2024/7/18
 */
public class User {
	private Long id;
	private String name;
	private int age;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public User(){}

	public User(Long id, String name, int age){
		this.id = id;
		this.name = name;
		this.age = age;
	}

	@Override
	public String toString() {
		return "User{" +
				"id=" + this.id +
				", name='" + this.name + '\'' +
				", age=" + this.age +
				'}';
	}
}
package com.dustin.dto;

/**
 * @author dustin
 * @version 1.0
 * @Description
 * @date 2024/7/18
 */
public class Department {
	private Long id = 456L;
	private String name = "CC";

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Department{" +
				"id=" + id +
				", name='" + name + '\'' +
				'}';
	}
}

ContextConfig是配置类,通过扫描注解的方式进行bean的注入,本例中User这个bean是通过此配置类注入的:

package com.dustin.config;

import com.dustin.dto.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author dustin
 * @version 1.0
 * @Description
 * @date 2024/7/18
 */
@Configuration
@ComponentScan
public class ContextConfig {
	@Bean
	public User user(){
		return new User(123L, "dq", 18);
	}
}

config.xml是配置文件,放在resources目录下,本例中Department这个bean是通过这个配置文件注入的:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="department" class="com.dustin.dto.Department"></bean>
</beans>

最后这个ContextApplication类就是spring启动的入口了,在main方法中分别通过扫描注解和读取xml的方式,获取到User和Department两个bean,最后使用这两个bean(打印bean信息):

package com.dustin;

import com.dustin.config.ContextConfig;
import com.dustin.dto.Department;
import com.dustin.dto.User;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author dustin
 * @version 1.0
 * @Description
 * @date 2024/7/18
 */
public class ContextApplication {
	public static void main(String[] args) {
		// 通过读注解的方式获取bean
		AnnotationConfigApplicationContext annotationContext = new AnnotationConfigApplicationContext(ContextConfig.class);
		User user = annotationContext.getBean(User.class);
		System.out.println("User:" + user.toString());
		// 通过读xml的方式获取bean
		ClassPathXmlApplicationContext xmlApplicationContext = new ClassPathXmlApplicationContext("config.xml");
		Department department = xmlApplicationContext.getBean(Department.class);
		System.out.println("Department:" + department.toString());
	}
}

  运行main方法,看看这个配置bean-》获取bean-》使用bean的微型spring项目是否运行正常:
在这里插入图片描述
可以看到,两个bean的信息都成功打印出来了,一切正常。接下来就可以真正开始与源码0距离接触了!可以预料到的是,这必将是一场神秘坎坷但又充满宝藏的旅途!好比金庸的悬崖谷底,虽然遍体鳞伤但总能收获神功秘籍~~

不打无准备之仗:源码一周目简单规划

  激动之余也要清醒,面对我们完全未知的领域,盲干是不行的,何不问问已经归来的水手?以下内容来自知乎,主要是关于spring源码学习方法论的一些观点:

  • 从上至下全部通读的方式,个人不太推荐,这是建立在很熟悉的基础上的,当我们对某个框架已经比较熟悉了,再从上至下进行通读,彻底了解,这是我认为正确的方式;但是从不熟悉到熟悉这个过程,个人不推荐全部通读,而是推荐使用idea进行断点局部追踪。
  • 不要为了看源码而看源码,一定要带着问题去看源码,这样比较容易带入进去。Spring整个框架非常复杂,不建议直接去读,从Spring的上层框架入手会更好。比如你可以读Spring MVC的源码,这部分的源码就简单了很多,读源码的时候都给自己提一些问题,这样能集中精力去读源码,比如:一个http请求怎么路由到具体controller?spring mvc怎么处理的参数映射,前端提交的Json数据,如何映射到我们的VO里面的?为什么spring mvc能返回rest接口,也能返回视图?Spring Mvc如何根据自己的需求进行扩展?

  其实这里就是一个简单的spring学习路径了(始终牢记着我的源码学习终极目标:画一张spring的唬人大图)。结合我一个老菜鸡的工作经验、spring源码同好者、诸多“专升本成人自考级”spring付费课程的总结,我简单规划了下学习计划,其中主要参考了这位好兄弟的文章:spring源码深度解析。谢谢你,因为有你,温暖了司机~~

第一步:spring启动时是如何解析配置文件并注册bean的,可以聚焦于这个过程来阅读ApplicationContext容器的源码内容,重要的是找到读源码的感觉;
第二步:在上一步的学习中,应该能自然而然的引出BeanFactory。a.k.a. bean容器,也是spring基础中的基础;
第三步:spring提供了很多后门对bean进行操作,而AOP事务等其实就是利用后门对相关逻辑进行了封装,所以这一步要去探索spring怎样实现AOP和管理事务的;
第四步:在上面三步之后,spring的基础已经已经真正意义上有所掌握了,那么此时就可以回归应用层,看看spring(MVC)是如何配合tomcat,一起帮助我们实现业务逻辑的!
第五步:神功初成,可考虑二周目了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1934175.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

windows下使用make编译C/C++程序 gcc编译 MinGW编译器

文章目录 1、概要2、编译环境搭建3、创建工程目录结构4、 编写程序4.1 编写头文件4.2 编写源文件 5、编写makefile及相关文件5.1 编写清理编译生成文件的批处理文件&#xff0c;供makefile调用5.2 编写makefile文件 6、编译工程6.1 打开命令行6.2 使用make命令编译程序6.3 编译…

【数据结构】二叉树———Lesson2

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

QT CNA上位机报错 解决方案

QT编译报错: -lControlCAN 解决方案 更换三个文件&#xff0c;即可解决(QT 自带的是32位库&#xff0c;应使用64位库文件)

Linux系统编程---Make/MakeFile

认识make/makefile make是一个命令 makefile是一个当前目录下的文件。 make会自动推导makefile中的依赖关系&#xff0c;栈式结构。形成可执行文件 如何清理 在makefile文件里加上clean&#xff0c;运行时直接make clean&#xff0c;建议clean放在后面。 在没有改变源文件的…

深入学习STL标准模板库

C STL standard template libaray 标准模板库 目录 C STL standard template libaray 标准模板库 一、标准容器顺序容器vectordequelistvector deque list对比 容器适配器stackqueuepriority_queue 关联容器unordered_setunordered_multisetunordered_mapunordered_multimapset…

【C++基础】类和对象(4)

目录 一、再探构造函数 1.1 构造函数体赋值 1.2 初始化列表 二、类型转换 三、static成员 概念&#xff1a; 特性&#xff1a; 四、友元 4.1 友元函数 4.2 友元类 五、内部类 六、匿名对象 七、对象拷贝时的编译器优化 一、再探构造函数 1.1 构造函数体赋值 …

Admin.NET源码学习(2:安装并运行前端)

根据Admin.NET的GitHub主页介绍&#xff0c;前端运行步骤需要运行pnpm命令。百度pnpm的话&#xff0c;需要支持npm相关的命令支持。   根据参考文献4&#xff0c;安装Node.js后会提供npm命令支持&#xff08;npm是Node.js的软件包管理器&#xff0c;用于安装、发布和共享Jav…

在Linux系统安装MySQL有多简单

MySQL 是一种流行的开源关系数据库管理系统&#xff0c;广泛应用于各种类型的应用程序和服务。在安装TitanIDE​​​​​​​以后是没有MySQL服务的&#xff0c;我们需要单独安装安装MySQL。本文将介绍在 Linux 上安装 MySQL 的多种方式&#xff0c;包括离线安装、使用 Docker …

纤丝龙:强根固发,滋养头皮的天然秘诀

在我国传统中医学中&#xff0c;头发被视为“血之余&#xff0c;肾之华”&#xff0c;其生长与健康状况直接反映了人体的精神面貌和生命力。然而&#xff0c;现代生活节奏加快&#xff0c;压力不断增大&#xff0c;导致许多人出现头发干枯、分叉、脱落等问题。纤丝龙&#xff0…

“轻、灵、画、韵”,TCL第三代艺术电视引领艺术生活

7月17日&#xff0c;TCL第三代艺术电视A300系列正式上市&#xff0c;其中&#xff0c;A300 Pro配备B&O音响&#xff0c;共有65/75/85三个尺寸&#xff0c;首发到手价分别为8999元、11999元和15999元。不带Soundbar的A300W&#xff0c;它有55/65/75/85四个尺寸&#xff0c;首…

volatile关键字解析

一、volatile介绍 volatile是Java提供的一种轻量级的同步机制&#xff0c;在并发编程中&#xff0c;它也扮演着比较重要的角色。同synchronized相比&#xff08;synchronized通常称为重量级锁&#xff09;&#xff0c;volatile更轻量级&#xff0c;相比使用synchronized所带来的…

Python 读取esxi上所有主机的设备信息

&#xff08;主要是为了统计所有虚拟机的设备名称和所属主机&#xff09; 代码&#xff1a; from pyVim import connect from pyVmomi import vim import ssldef get_vm_devices(vm):devices []try:if vm.config is not None and hasattr(vm.config, hardware) and hasattr(v…

JavaWeb入门程序解析(Spring官方骨架、配置起步依赖、SpringBoot父工程、内嵌Tomcat)

3.3 入门程序解析 关于web开发的基础知识&#xff0c;我们可以告一段落了。下面呢&#xff0c;我们在基于今天的核心技术点SpringBoot快速入门案例进行分析。 3.3.1 Spring官方骨架 之前我们创建的SpringBoot入门案例&#xff0c;是基于Spring官方提供的骨架实现的。 Sprin…

gite+picgo+typora打造个人免费笔记软件

文章目录 1️⃣个人笔记软件2️⃣ 配置教程2.1 使用软件2.2 node 环境配置2.3 软件安装2.4 gite仓库设置2.5 配置picgo2.6 测试检验2.7 github教程 &#x1f3a1; 完结撒花 1️⃣个人笔记软件 最近换了环境&#xff0c;没有之前的生产环境舒适&#xff0c;写笔记也没有劲头&…

R语言实现对模型的参数优化与评价KS曲线、ROC曲线、深度学习模型训练、交叉验证、网格搜索

目录 一、模型性能评估 1、数据预测评估 2、概率预测评估 二、模型参数优化 1、训练集、验证集、测试集的引入 2、k折线交叉验证 2、网格搜索 一、模型性能评估 1、数据预测评估 ### 数据预测评估 #### 加载包&#xff0c;不存在就进行在线下载后加载if(!require(mlben…

VMware Vsphere创建虚拟机

作者&#xff1a;红米 一、上传系统镜像 1、打开数据中心 2、新建文件夹&#xff0c;存放镜像 3、点击上传文件按钮 4、找到本地镜像上传 二、安装虚拟机 1、创建虚拟机 2、选择创建类型 3、为虚拟机命名并选择虚拟机安装的所在位置 4、选择计算资源 5、选择存储 6、选择兼容…

微服务

微服务 SpringCloud的五大组件 eureka服务注册和发现 nacos的工作流程 nacos和eureka的区别 负载均衡 ribbon负载均衡策略 如何自定义负载策略 服务雪崩 服务熔断 为服务端监控 项目中的限流 seata xa模式 AT模式 tcc模式 分布式服务接口幂等 分布式任务调度

2024年CSP-J暑假冲刺训练营(1):分析往年真题

考纲大览 一、往年真题1. 2019-2023 真题2. 整体分析 二、类型分析三、押题 一、往年真题 1. 2019-2023 真题 2. 整体分析 首先大家一定要明确&#xff0c;CSP-J 是不会给大家占便宜的&#xff0c;所以大家可以看到&#xff0c;即使被标注了"入门"难度的题目&#…

【性能测试】第二节.loadrunner工具介绍(LR)

文章目录 前言一、VUG&#xff1a;虚拟用户发生器 1.1 实现作用 1.2 创建一个新的性能测试脚本 1.3 打开LR自带的web系统 1.4 编写性能测试脚本流程方法 1.5 性能测试脚本的增强二、Controller 2.1 基础功能介绍 2.2 Design 2.3 Run三…

<Qt> 信号和槽

目录 一、信号和槽概述 二、信号和槽的使用​​​​​​ &#xff08;一&#xff09;connect函数 &#xff08;二&#xff09;实现一个点击按钮关闭窗口的功能 &#xff08;三&#xff09;再谈connect 三、自定义槽函数 四、自定义信号 五、带参数的信号和槽 六、信号…