0%

Spring MVC 核心流程与配置详解:从请求到响应的完整链路

Spring MVC 是基于 MVC 设计模式的 Web 框架,其核心是通过 DispatcherServlet(前端控制器) 统一调度所有请求,配合 HandlerMapping、HandlerAdapter、ViewResolver 等组件完成 “请求处理→业务逻辑→视图渲染” 的全流程。,从 “执行流程拆解→核心组件职责→Java 配置方式” 三个维度,彻底讲透 Spring MVC 的工作原理。

Spring MVC 核心执行流程(图文解析)

执行流程

Spring MVC 的执行流程围绕 DispatcherServlet 展开,共 8 个关键步骤,每个步骤由特定组件协作完成,确保请求按规范流转:

步骤 1:用户发送请求,DispatcherServlet 接收请求

  • 触发点:用户通过浏览器 / 客户端发送 HTTP 请求(如 GET /user/list),请求首先到达 Web 容器(如 Tomcat);
  • DispatcherServlet 角色:作为 Spring MVC 的 “前端控制器”,是所有请求的统一入口,负责全局流程控制,自身不处理具体业务逻辑;
  • 底层机制:Web 容器通过 web.xml 或 Java 配置,将所有请求(或指定 URL 模式)映射到 DispatcherServlet(如配置 url-pattern: / 拦截所有请求)。

步骤 2:DispatcherServlet 调用 HandlerMapping,获取 HandlerExecutionChain

  • HandlerMapping 职责:根据请求 URL 找到对应的 “处理器(Handler,通常是 Controller 的方法)”,并返回 HandlerExecutionChain 对象;
  • HandlerExecutionChain 组成:包含两部分:
    1. Handler:具体的业务处理器(如 UserControllergetUserList() 方法);
    2. HandlerInterceptor 数组:请求处理前后的拦截器(如日志拦截器、权限拦截器);
  • 核心设计模式策略模式。Spring 提供多种 HandlerMapping 实现(如 RequestMappingHandlerMapping 用于注解式 Controller),可灵活切换映射策略(如按 URL 路径、按请求参数映射)。
阅读全文 »

Java 字符串详解:String、StringBuilder 与 StringBuffer

字符串是 Java 中最常用的数据类型之一,Java 提供了 StringStringBuilderStringBuffer 三个类用于处理字符串,但它们的特性和适用场景有显著差异。本文将深入解析这三个类的底层实现、核心特性及最佳实践。

String 类:不可变的字符串

String 类是 Java 中最基础的字符串类,其核心特性是不可变性,这一特性深刻影响了它的使用方式和性能表现。

不可变性的底层实现

String 类被 final 修饰,且其底层存储字符的数组 value 也被 final 修饰,因此字符串对象一旦创建,其内容不可修改。

1
2
3
4
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[]; // 存储字符串的字符数组(final修饰)
// ... 其他代码
}
  • 不可变性的含义:字符串的任何修改操作(如拼接、替换)都会创建新的String对象,原对象内容保持不变。

    1
    2
    String s = "hello";
    s += " world"; // 生成新对象"hello world",原对象"hello"仍存在

字符串常量池:优化内存占用

为减少重复字符串的内存消耗,Java 引入了字符串常量池(位于方法区),用于存储编译期确定的字符串常量。

两种创建字符串的方式对比:
创建方式 原理 示例
直接赋值(字面量) 优先从常量池查找,若存在则返回引用;否则创建常量并放入池,再返回引用。 String s = "abc";
new String() 构造器 始终在堆中创建新对象,若常量池无对应常量则同时创建并放入池。 String s = new String("abc");
字节码分析:new String("xyz") 的对象创建
阅读全文 »

多线程问题集锦

一、如何保证 T1→T2→T3 的执行顺序?

要确保 T2 在 T1 完成后执行,T3 在 T2 完成后执行,最直观的方案是使用Thread.join()方法。join()的核心作用是让当前线程阻塞,等待目标线程执行完毕后再继续运行,从而强制线程按顺序执行。

实现示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class ThreadOrder {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("T1 执行中...");
try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("T1 执行完毕");
}, "T1");

Thread t2 = new Thread(() -> {
try {
t1.join(); // 等待T1执行完毕
} catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("T2 执行中...");
try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("T2 执行完毕");
}, "T2");

Thread t3 = new Thread(() -> {
try {
t2.join(); // 等待T2执行完毕
} catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("T3 执行中...");
try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("T3 执行完毕");
}, "T3");

// 启动顺序不影响最终执行顺序
t3.start();
t2.start();
t1.start();
}
}

执行结果:

阅读全文 »

线程简介

进程与线程的核心区别

进程和线程是操作系统中两个核心的执行单元,二者的区别主要体现在资源分配和执行方式上:

对比维度 进程 线程
资源分配 操作系统分配资源的基本单位(拥有独立的堆、方法区等) 进程内的执行单元,共享进程的资源(堆、方法区),但有私有栈和程序计数器
独立性 独立运行环境,一个进程崩溃不影响其他进程 依赖进程存在,同一进程内线程崩溃可能导致整个进程崩溃
调度单位 操作系统调度资源的单位,但调度成本高 CPU 调度的基本单位,调度成本低(上下文切换开销小)
数量关系 一个进程可包含多个线程 线程是进程的一部分,不能独立存在

线程的内存结构

当 JVM 创建一个线程时,会为其分配特殊的内存区域,包含以下关键部分:

  • 程序计数器:记录线程下一条要执行的 JVM 字节码指令,确保线程在 CPU 时间片切换后能恢复执行(线程私有)。
  • Java 栈:跟踪 Java 方法的调用关系,每个方法调用对应一个栈帧,存储局部变量、参数、返回值等(线程私有)。
  • 本地方法栈:支持 native 代码的执行,与 Java 栈类似但用于本地方法调用(线程私有)。
  • 线程本地存储(Thread Local):存储线程私有变量,避免多线程共享带来的并发问题(线程私有)。
  • 状态管理变量:控制线程的生命周期(如状态标记、中断标志等)。

注意:线程的栈帧中,基本类型变量直接存储在栈上,而对象引用存储在栈上,对象本身存储在堆中(堆为进程共享资源)。

线程的创建方式

Java 提供了 4 种创建线程的方式,各有适用场景:

阅读全文 »

Java 注解(Annotation)详解:从元注解到注解处理器

注解(Annotation)是 Java 5 引入的一种元数据(metadata)机制,用于为代码添加额外信息(如配置、约束、说明等),不直接影响代码逻辑,但可通过工具(编译器、框架)解析并产生实际作用(如编译检查、代码生成)。本文将从基础概念、元注解、注解处理器等方面全面解析 Java 注解。

注解的基本概念

什么是注解?

注解是一种特殊的 “标记”,可附加在类、方法、字段等程序元素上,格式为 @注解名(属性=值)。例如:

1
2
3
4
5
6
7
// 方法上的@Override注解(标记方法重写)
@Override
public void run() { ... }

// 类上的@Deprecated注解(标记类已过时)
@Deprecated
public class OldClass { ... }

注解的作用

  • 编译检查:如 @Override 确保方法正确重写父类方法,编译器会校验正确性。
  • 代码生成:如 Lombok 的 @Data 自动生成 getter/setter 方法,减少模板代码。
  • 运行时处理:如 Spring 的 @Autowired 实现依赖注入,框架在运行时通过反射解析注解。
  • 文档说明:如 @Deprecated 标记过时元素,javadoc 会包含该信息。

元注解:注解的注解

元注解(Meta Annotation)是用于修饰 “注解” 的注解,定义了注解的适用范围、生命周期、继承性等基本特性。Java 内置了 5 种元注解:

@Target:指定注解的适用位置

用于限制注解可附加的程序元素(如类、方法、字段等),属性为 ElementType 数组,常见取值:

ElementType 取值 含义 示例
TYPE 类、接口、枚举 @Service 用于类
METHOD 方法 @Override 用于方法
FIELD 字段(成员变量) @Autowired 用于字段
PARAMETER 方法参数 @RequestParam 用于参数
CONSTRUCTOR 构造器 自定义注解用于构造器
LOCAL_VARIABLE 局部变量 较少使用
ANNOTATION_TYPE 注解类型本身 元注解修饰其他注解
PACKAGE 用于包信息配置
TYPE_PARAMETER(Java 8+) 类型参数(如泛型 <T> @NonNull <T> T get()
TYPE_USE(Java 8+) 任何类型使用处(如变量声明) List<@NonNull String>
阅读全文 »