WASNL-学习笔记
type: channel
link: https://note.wasnl.com/
description: 个人学习笔记
items: Java接口（Interface）与抽象类（Abstract Class）
items: 数组操作
items: 02-Java面向对象编程
items: 01-Java基础语法
items: Spring MVC 知识地图
items: Spring MVC 请求生命周期
items: java
items: 反射 + AOP + 注解
items: Spring JDBC + JdbcTemplate
items: 未命名文档

Java接口（Interface）与抽象类（Abstract Class）
type: item
date: 2026-07-07T15:11:19+08:00
description: 这是一份为你定制的Java接口（Interface）与抽象类（Abstract Class）终极对比笔记。这份笔记整合了从Java 7到Java 17的所有语法变迁，并深入到设计哲学和底层JVM机制，适合面试冲刺或作为案头速查手册。📌 第一部分：核心定义（先定调）抽象类（Abstract Class）：“不完整的实体”。它用来描述一类事物公共的“本质”（状态+行为）。它可以有构造器、有属性、有已实现的方法，但因为不够具体，所以不允许被new出来。接口（Interface）：“纯粹的契约/能力”。它用来定义一种“行为规范”。它不管实现者是谁（可以是鸟、飞机、超人），只要签署了这份契约（implements），就必须具备这个能力。📌 第二部分：终极语法速查表（Java 8+ 特别版）对比维度抽象类（Abstract Class）接口（Interface）关键字abstract classinterface实例化（new）不能（无法创建对象）不能（无法创建对象）构造方法可以有（用于子类初始化父类属性）绝对没有（因为无实例变量需初始化）成员变量可以是任何类型（private/protected/public 实例变量，可以有状态）默认且只能是 public static final（常量），变量必须显式赋初值普通具体方法可以有（直接写带{}的方法体）Java 8+ 通过 default 和 static 可以有；Java 9+ 通过 private 可以有抽象方法使用 abstract 关键字，子类必须实现（除非子类也是抽象类）默认隐含 public abstract，实现类必须实现（除非实现类是抽象类）继承/实现关系单继承（一个类只能 extends 一个父类）多继承/多实现（一个接口可以 extends 多个接口；一个类可以 implements 多个接口）访问修饰符限制方法可以是 public/protected/private/default接口中的抽象方法隐含且只能是 public（不能是private或protected，否则编译报错）静态方法可以有，子类可以继承调用可以有（Java 8+），但只能通过接口名调用，实现类无法继承final 修饰可以有 final 方法（禁止子类重写）接口中的方法不能声明为 final（因为抽象/默认方法天生需要被重写或覆盖）📌 第三部分：方法家族全览（最易混淆点详解）接口到底能写哪些方法？这是面试高频扣分点：方法类型是否允许（接口）是否允许（抽象类）备注抽象方法（无方法体）✅ 允许（隐含public abstract）✅ 允许接口只干这事（Java 7之前）实例具体方法（有方法体）❌ 不允许（直到Java 8）✅ 允许抽象类主打的功能default 方法（Java 8+）✅ 允许—用于接口版本升级，避免老实现类报错static 方法（Java 8+）✅ 允许✅ 允许接口的static不可继承；抽象类的static可被子类继承调用private 方法（Java 9+）✅ 允许✅ 允许接口内用于抽取default方法的公共逻辑，不对外暴露📌 第四部分：设计哲学（本质区别，面试必答）抽象类：本质是“是什么”（Is-A）。侧重于对象本身。子类和父类必须有血缘关系（例如：Dog extends Animal）。用于捕获公共的状态（属性）和通用的基础逻辑。例子：所有订单都有订单号、金额，创建时都要打印日志。这些状态和步骤必须由抽象类来定义，接口做不到。接口：本质是“能做什么”（Can-Do / Like-A）。侧重于对象拥有的能力。完全不相关的类可以通过实现同一个接口获得相同能力（例如：Bird、Plane、Superman 都能实现 Flyable）。用于定义行为契约，不关心实现者的内部状态。例子：Comparable（可比较）、Runnable（可运行）、Serializable（可序列化）。📌 第五部分：经典协作模式（源码级的黄金搭档）在顶级框架（如Spring、JDK集合框架）中，它们从不“单打独斗”，而是按照 “接口定义标准 + 抽象类提供骨架 + 具体类实现业务” 三层结构配合： [List 接口] ← 定义契约（能添加、能删除、能遍历） ↑ (implements) [AbstractList 抽象类] ← 提供骨架（把 indexOf、equals 等通用逻辑先写一半） ↑ (extends) ┌──────────┴──────────┐ [ArrayList] [LinkedList] ← 具体实现（各自实现最核心的差异化逻辑）结论：用接口应对多态和扩展，用抽象类应对代码复用和封装状态。📌 第六部分：面试高频陷阱题（查漏补缺）抽象类能实现接口吗？ ✅ 可以。且抽象类实现接口时可以不…

数组操作
type: item
date: 2026-07-07T10:02:12+08:00
description: 在 Spring Boot 2.7 的企业实际开发中，数组操作主要依赖 Java 标准库（java.util.Arrays）和 Spring 框架内置工具类（org.springframework.util 包），两者结合基本能覆盖所有场景。⚙️ 数组操作核心方法速查操作类型核心方法所属类/库关键说明排序Arrays.sort(array)java.util.Arrays对数组进行原地升序排序。二分查找Arrays.binarySearch(array, key)java.util.Arrays必须在已排序数组中使用。数组拷贝Arrays.copyOf(original, newLength)Arrays.copyOfRange(original, from, to)java.util.Arrays灵活复制数组全部或部分内容。数组填充Arrays.fill(array, value)java.util.Arrays将数组所有或指定范围元素设为同一个值。数组转字符串Arrays.toString(array)Arrays.deepToString(array)java.util.Arrays方便打印一维或多维数组内容。数组比较Arrays.equals(array1, array2)Arrays.deepEquals(array1, array2)java.util.Arrays比较一维或多维数组内容是否相等。集合判空CollectionUtils.isEmpty(collection)CollectionUtils.isNotEmpty(collection)org.springframework.util.CollectionUtils安全地检查集合是否为 null 或空。数组转集合CollectionUtils.arrayToList(array)org.springframework.util.CollectionUtils支持基本类型数组转换。数组合并到集合CollectionUtils.mergeArrayIntoCollection(array, collection)org.springframework.util.CollectionUtils将数组元素合并到现有集合中。集合空值兜底CollectionUtils.emptyIfNull(collection)org.springframework.util.CollectionUtils如果集合为 null，返回一个空集合，避免 NPE。集合包含检查CollectionUtils.contains(collection.iterator(), element)org.springframework.util.CollectionUtils检查集合是否包含某元素。对象/数组判空ObjectUtils.isEmpty(obj)org.springframework.util.ObjectUtils可检查对象、数组、集合等是否为 null 或空。获取首个非空对象ObjectUtils.firstNonNull(obj1, obj2, ...)org.springframework.util.ObjectUtils从多个对象中返回第一个非 null 的。数组转分隔字符串StringUtils.arrayToCommaDelimitedString(arr)org.springframework.util.StringUtils将数组元素用逗号连接成字符串。🚀 数组与集合互转（高频场景）这是日常开发中最常见的操作之一，需要特别注意不同方法的区别。数组 转 ListArrays.asList(T... a)：最常用，但返回的 List 是固定长度的，不能进行 add 或 remove 操作。且不支持基本类型数组。// 引用类型数组 -> 正确 String[] strArray = {"a", "b"}; List<String> list = Arrays.asList(strArray); // 基本类型数组 -> 错误！ list 实际上是一个包含 int[] 对象的 List int[] intArray = {1, 2}; List<int[]> list = Arrays.asList(intArray); new ArrayList<>(Arrays.asList(array))：创建一个可变的 ArrayList，可以自由增删元素。String[] strArray = {"a", "b"}; List<String> list = new ArrayList<>(Arrays.asList(strArray)); list.add("c"); // 可以…

02-Java面向对象编程
type: item
date: 2026-06-22T10:59:13+08:00
description: 2.1 类与对象核心概念类是对象的模板，对象是类的实例。类定义了对象的属性（成员变量）和行为（成员方法），而对象是类的具体化表现。定义语法// 定义类 [访问修饰符] class 类名 { // 成员变量（属性） 数据类型 变量名; // 成员方法（行为） 返回类型 方法名(参数列表) { // 方法体 } }实例化语法类名 对象名 = new 类名();完整示例// 定义一个学生类 public class Student { // 成员变量 String name; int age; double score; // 成员方法 public void study() { System.out.println(name + " 正在学习"); } public void introduce() { System.out.println("我叫 " + name + "，今年 " + age + " 岁，成绩是 " + score); } } // 使用类创建对象 public class Main { public static void main(String[] args) { // 实例化：创建对象 Student s1 = new Student(); Student s2 = new Student(); // 为对象赋值 s1.name = "张三"; s1.age = 18; s1.score = 92.5; s2.name = "李四"; s2.age = 19; s2.score = 88.0; // 调用对象方法 s1.study(); // 输出：张三 正在学习 s1.introduce(); // 输出：我叫 张三，今年 18 岁，成绩是 92.5 s2.introduce(); // 输出：我叫 李四，今年 19 岁，成绩是 88.0 } }内存分析栈内存：存储局部变量和对象引用（引用变量名）。堆内存：存储 new 出来的对象实体（成员变量值）。元空间：静态变量存储在堆中，类元数据（方法、字段描述等）存储在元空间Student s1 = new Student(); // s1 在栈中，new Student() 在堆中注意事项一个 Java 文件中可以有多个类，但最多只有一个 public 修饰的类，且文件名必须与 public 类名一致。对象的默认值：数值类型为 0 / 0.0，boolean 为 false，引用类型为 null。使用 new 关键字的过程：加载类 → 在堆中分配内存 → 初始化默认值 → 执行构造方法 → 返回引用地址。2.2 方法定义语法[访问修饰符] [static] [final] 返回值类型 方法名(参数类型 参数名, ...) { // 方法体 [return 返回值;] }完整示例public class Calculator { // 实例方法：需要对象调用 public int add(int a, int b) { return a + b; } // 静态方法：通过类名直接调用 public static int subtract(int a, int b) { return a - b; } // 无返回值方法 public void printResult(String operation, int result) { System.out.println(operation + " 结果：" + result); } } // 调用示例 public class Main { public static void main(String[] args) { // 静态方法：类名调用 int diff = Calculator.subtract(10, 3); // 实例方法：对象调用 Calculator calc = new Calculator(); int sum = calc.add(10, 3); calc.printResult("加法", sum); } }静态方法 vs 实例方法对比对比维度静态方法 (static)实例方法 (非static)修饰符使用 static 修饰不使用 static调用方式类名直接调用 ClassName.method()必须通过对象调用 obj.method()内存位置方法区(就是静态的代码方法而已)元空间(就是静态的代码方法而已)访问实例成员不能直接访问实例变量和实例方法可以访问所有成员（静态 + 实例）访问静态成员可以访问（推荐用类名访问）可以访问this / super不能使用可以使用应用场景工具方法（如 Math.pow()）、独立功能与对象状态相关的方法参数传递详解Java 中所有参数传递都是值…

01-Java基础语法
type: item
date: 2026-06-22T10:58:00+08:00
description: 1.1 基本数据类型Java 是一门强类型语言，共有 8 种基本数据类型（Primitive Type）分为三大类：数值型（整型，浮点型）、字符型和布尔型类型总览分类类型字节数取值范围默认值备注整型byte1-128 ~ 1270 整型short2-32768 ~ 327670 整型int4-2^31 ~ 2^31-10默认整数类型整型long8-2^63 ~ 2^63-10L字面量需加 L 后缀浮点型float4±3.4E-38 ~ ±3.4E+380.0f字面量需加 f 后缀浮点型double8±1.7E-308 ~ ±1.7E+3080.0d默认浮点类型字符型char20 ~ 65535 (Unicode)'\u0000'表示单个 Unicode 字符布尔型boolean1位true / falsefalseJVM 中通常用 int 表示代码示例public class PrimitiveTypes { public static void main(String[] args) { // 整型 byte b = 100; // 1字节 short s = 10000; // 2字节 int i = 100000; // 4字节，默认 long l = 10000000000L; // 8字节，必须加 L 后缀 // 浮点型 float f = 3.14f; // 4字节，必须加 f 后缀 double d = 3.1415926535; // 8字节，默认 // 字符型 char c1 = 'A'; // 2字节 char c2 = '\u0041'; // Unicode 编码表示 'A' char c3 = 65; // ASCII 码表示 'A' // 布尔型 boolean flag = true; } }注意事项long 和 float 的字面量后缀不可省略：不加 L 的整数字面量默认是 int，不加 f 的浮点字面量默认是 double。char 可以存中文：因为 char 是 2 字节 Unicode，一个中文字符正好是 2 字节。boolean 占用空间：JVM 规范中 boolean 被当作 int 处理（4字节），但在 boolean 数组中每个元素占用 1 字节。默认值仅对成员变量有效：局部变量必须显式初始化，否则编译不通过。public class DefaultValues { // 实例变量：自动初始化为默认值 byte b; // 0 short s; // 0 int i; // 0 long l; // 0L float f; // 0.0f double d; // 0.0d char c; // '\u0000' boolean flag; // false public void test() { int local; // 局部变量 // System.out.println(local); // 编译错误！未初始化 } }1.2 变量类型Java 中有三种变量，按声明位置和修饰符区分。对比总览变量类型声明位置生命周期默认值存储位置修饰符局部变量方法、构造方法或代码块中方法执行期间无，必须显式初始化栈内存（方法栈帧）只能用 final实例变量类中、方法外与对象共存亡有默认值堆内存（对象中）访问控制修饰符类变量（静态变量）类中、方法外，static 修饰类加载时创建，类卸载时销毁有默认值方法区/元空间static + 访问控制修饰符内存模型 JDK8+┌─────────────────────────────────────────────┐ │ JVM 内存结构 │ ├───────────────┬───────────────┬─────────────┤ │ 虚拟机栈 │ 堆内存 │ 方法区 │ │ (Stack) │ (Heap) │ Metaspace(元空间) │ ├───────────────┼───────────────┼─────────────┤ │ 方法栈帧 │ 对象实例 │ 类元信息(类结构、字段、方法字节码) │ │ 局部变量表 │ 数组 │ 运行时常量池、符号表、Vtable虚方法表 │ │ 操作数栈 │ 实例变量 │ 即时编译代码、类加载信息 │ │ 方法返回地址 │ Class对象、静态变量、字符串常量池 │ │ 动态链接 │ │ │ 线程私有 │ 线程共享 │ 线程共享 │ └───────────────┴───────────────┴─────────────┘ 运行时常量池 → 永远在元空间（方法区） 方法栈帧：每调用一个方法，就会在虚拟机栈创建一块独立内存 = 栈帧 如调用 sayHello() → 生成 sayHello() …

Spring MVC 知识地图
type: item
date: 2026-06-10T18:36:08+08:00
description: 🌐 Spring MVC 知识地图一、核心架构与组件Spring MVC 核心 ├── DispatcherServlet（前端控制器） ├── HandlerMapping（处理器映射） │ ├── RequestMappingHandlerMapping （注解映射，最常用） │ ├── BeanNameUrlHandlerMapping │ └── SimpleUrlHandlerMapping ├── HandlerAdapter（处理器适配器） │ ├── RequestMappingHandlerAdapter │ ├── HttpRequestHandlerAdapter │ └── SimpleControllerHandlerAdapter ├── Handler / Controller（处理器） ├── ModelAndView（模型与视图封装） ├── ViewResolver（视图解析器） │ ├── InternalResourceViewResolver（JSP） │ ├── BeanNameViewResolver │ ├── XmlViewResolver │ ├── ContentNegotiatingViewResolver │ └── …… └── View（视图） ├── JSP / JSTL ├── Thymeleaf ├── FreeMarker └── ……二、请求处理完整流程客户端请求 → DispatcherServlet 接收 ├── 1. 通过 HandlerMapping 查找 Handler（返回 HandlerExecutionChain，包含拦截器） ├── 2. 通过 HandlerAdapter 执行 Handler（Controller） ├── 3. Handler 执行业务逻辑，返回 ModelAndView ├── 4. 通过 ViewResolver 解析 ModelAndView 得到 View 对象 └── 5. View 渲染，将模型数据填充到视图，生成响应三、控制器开发Controller 开发 ├── 注解 │ ├── @Controller / @RestController │ ├── @RequestMapping │ │ ├── @GetMapping │ │ ├── @PostMapping │ │ ├── @PutMapping │ │ ├── @DeleteMapping │ │ └── @PatchMapping │ └── @CrossOrigin（跨域） ├── 请求参数绑定 │ ├── @RequestParam │ ├── @PathVariable（路径变量） │ ├── @RequestHeader │ ├── @CookieValue │ ├── @MatrixVariable │ ├── @RequestBody（JSON/XML 等） │ ├── @ModelAttribute（表单/查询参数绑定到对象） │ ├── HttpServletRequest / HttpServletResponse │ └── MultipartFile（文件上传） ├── 返回类型 │ ├── String（视图名，配合 Model） │ ├── ModelAndView │ ├── void（直接操作 HttpServletResponse） │ ├── @ResponseBody / ResponseEntity（JSON/XML 响应） │ └── View、Model、Map 等 └── 异步处理 ├── Callable<T> ├── DeferredResult<T> └── ResponseBodyEmitter / SseEmitter / StreamingResponseBody四、数据绑定、类型转换与校验数据绑定与校验 ├── 类型转换 │ ├── PropertyEditor │ ├── Converter / ConverterFactory │ ├── Formatter（日期/数字等格式化） │ └── @InitBinder（自定义绑定规则，注册编辑器/校验器） ├── 数据校验 │ ├── JSR-380（Bean Validation）：@Valid / @Validated │ ├── BindingResult / Errors（校验结果） │ └── 自定义校验注解 └── WebDataBinder └── 控制绑定行为（允许/禁止字段等）五、RESTful Web 服务REST 支持 ├── @RestController = @Controller + @ResponseBo…

Spring MVC 请求生命周期
type: item
date: 2026-06-10T17:17:33+08:00
description: Spring MVC 请求生命周期SpringBoot 2.7 + Java8 企业级开发一、整体架构图 —— 一次请求的完整流水线HTTP Request -> http://你的项目/getUser?id=1 │ ▼ ┌─────────────────────┐ │ Tomcat │ 内嵌 Servlet 容器，最早接收请求 │ Servlet Container │ └─────────────────────┘ │ ▼ ┌─────────────────────┐ │ Filter 过滤器链 │ Servlet 级别的“门卫”，工作在 Spring MVC 之前 └─────────────────────┘ │ ▼ ┌─────────────────────┐ │ DispatcherServlet │ Spring MVC 的“总控”，分发请求的核心 └─────────────────────┘ │ ▼ ┌─────────────────────┐ │ HandlerInterceptor │ Spring 自己的拦截器，更懂 Controller │ preHandle │ 请求到达 Controller 前执行 └─────────────────────┘ │ ▼ ┌─────────────────────┐ │ Controller │ 你写的接口，@RestController / @GetMapping └─────────────────────┘ │ ▼ ┌─────────────────────┐ │ Service │ 业务逻辑层 └─────────────────────┘ │ ▼ ┌─────────────────────┐ │ DAO/JdbcTemplate │ 数据访问层，执行 SQL └─────────────────────┘ │ ▼ ┌─────────────────────┐ │ MySQL │ 数据库 └─────────────────────┘ │ ▼ Controller返回结果 返回 Java 对象 │ ▼ Interceptor.postHandle 所有拦截器的 postHandle │ ▼ ViewResolver / JSON序列化 前后端分离时转成 JSON 字符串 │ ▼ Interceptor.afterCompletion 资源清理、日志记录 │ ▼ Response → 浏览器二、企业项目真实执行顺序（必背）虚假的顺序：Tomcat -> Interceptor -> Controller实际严格顺序：Tomcat -> Filter -> DispatcherServlet -> Interceptor -> Controller -> Service -> DAO -> MySQL为什么是这个顺序？Filter 是 Servlet 规范 定义的，工作在容器级别，比 Spring 更底层；Interceptor 是 Spring MVC 内部 的，只有在 DispatcherServlet 接收请求之后才生效。也就是说：Filter 可以拦截所有请求（包括静态资源），而 Interceptor 只能拦截进入 Spring MVC 的请求。三、Filter 和 Interceptor 区别 —— 两个不同级别的“保安”1. Filter（Servlet 级别）定义方式：实现 javax.servlet.Filter 接口执行时机：Tomcat → Filter → Spring MVC特点：工作在容器层，不依赖 Spring，拿不到 Controller、方法等 Spring 上下文信息。典型用途：JWT 登录校验（直接解析 token，失败直接返回 401）XSS 过滤（清洗 等危险字符）请求日志（记录每个请求的耗时、URL）跨域处理（CorsFilter 统一设置响应头）2. Interceptor（Spring MVC 级别）定义方式：实现 org.springframework.web.servlet.HandlerInterceptor 接口执行时机：DispatcherServlet → Interceptor → Controller特点：能获取到 Handler（Controller 方法）、参数、返回结果等，是 Spring 生态内的拦截手段。典型用途：用户信息注入（从 token 解析用户信息，放入 UserContext）权限校验（admin / user 角色判断）操作日志（记录“谁在何时做了什么”）API 访问统计（PV / UV 计数）面试标准答案Filter 先执行，Interceptor 后执行。…

java
type: item
date: 2026-06-08T18:30:14+08:00
description: 1. 策略模式 ⭐⭐⭐⭐⭐消灭 if-else 的核心武器，基于 Spring 容器自动选择策略。步骤：定义策略接口public interface Strategy { String getType(); void execute(); }实现多个策略并用 @Component 注册@Component public class StrategyA implements Strategy { @Override public String getType() { return "A"; } @Override public void execute() { /* ... */ } } @Component public class StrategyB implements Strategy { @Override public String getType() { return "B"; } @Override public void execute() { /* ... */ } }创建策略选择器@Component public class StrategySelector { private final Map<String, Strategy> strategyMap = new HashMap<>(); // 4. Spring自动注入所有Strategy实现到Map（详见文档末尾“注入条件”） public StrategySelector(Map<String, Strategy> strategyMap) { // 遍历注入的Map，按业务key重组 strategyMap.values().forEach(s -> this.strategyMap.put(s.getType(), s)); } // 5. 选择策略 public Strategy getStrategy(String type) { Strategy strategy = strategyMap.get(type); if (strategy == null) { throw new IllegalArgumentException("No strategy for: " + type); } return strategy; } }2. 工厂模式 ⭐⭐⭐⭐⭐创建对象的“万能工厂”，通过 Spring 容器自动装配产品族。步骤：定义产品接口public interface Product { String getType(); void doSomething(); }多个产品实现并注册@Component public class ProductA implements Product { @Override public String getType() { return "A"; } @Override public void doSomething() { /*...*/ } } @Component public class ProductB implements Product { @Override public String getType() { return "B"; } @Override public void doSomething() { /*...*/ } }创建工厂类（Spring自动注入所有产品）@Component public class ProductFactory { private final Map<String, Product> productMap; // 构造器注入：Spring会将所有Product的实现注入到Map中（key=bean名称，默认类名首字母小写） public ProductFactory(Map<String, Product> productMap) { this.productMap = productMap; } public Product create(String type) { Product product = productMap.get(type); // 或自定义getType()映射 if (product == null) throw new IllegalArgumentException("Unknown product type"); return product; } }与策略模式的区别：工厂侧重于创建/获取对象，策略侧重行为互换，但 Spring 集成写法相似。3. 责任链模式 ⭐⭐⭐⭐⭐多个处理器依次尝试，直到有人处理。步骤：定义请求上下文public class RequestContext { private boolean…

反射 + AOP + 注解
type: item
date: 2026-06-03T16:33:01+08:00
description: 反射 + AOP + 注解 讲解目录注解（Annotation）什么是注解元注解与自定义注解如何在反射中读取注解反射（Reflection）反射的核心类：Class、Method、Field动态创建对象、调用方法、访问字段反射与注解的结合AOP（面向切面编程）AOP 的核心概念Spring AOP 的实现原理（代理）通知类型与切入点表达式如何在切面中使用反射和注解三剑合璧实战案例案例1：自定义 @Log 日志记录案例2：自定义 @RequirePermission 权限控制总结与关系图谱一、注解（Annotation）1.1 什么是注解？注解是 Java 5 引入的一种元数据，它本身不影响代码逻辑，但可以被编译、运行时工具或框架读取，从而赋予代码额外的语义或行为。常见注解如 @Override、@Deprecated、@Autowired 等。1.2 元注解定义注解时需要用到的注解，共有四个：元注解作用@Target指定注解能用在哪里（类、方法、字段等）@Retention指定注解的存活周期（源码、字节码、运行时）@Documented是否包含在 JavaDoc 中@Inherited是否允许子类继承父类的注解@Retention 的取值：RetentionPolicy.SOURCE：只在源码中存在，编译时丢弃（如 @Override）RetentionPolicy.CLASS：在字节码中存在，但运行时不可见（默认）RetentionPolicy.RUNTIME：运行时可通过反射读取（自定义注解必须选这个）1.3 自定义一个注解import java.lang.annotation.*; @Target(ElementType.METHOD) // 只能用在方法上 @Retention(RetentionPolicy.RUNTIME) // 运行时保留 public @interface Loggable { String value() default ""; // 注解属性 }使用：@Loggable("删除用户") public void deleteUser(Long id) { ... }1.4 在反射中读取注解无论是类、方法还是字段，反射 API 都提供了读取注解的方法：getAnnotation(Class<T> annotationClass) —— 获取指定类型的注解getAnnotations() —— 获取所有注解（含继承）getDeclaredAnnotations() —— 获取直接声明的注解（不含继承）isAnnotationPresent(Class<?> annotationClass) —— 判断是否存在某注解示例：读取方法上的 @LoggableMethod method = ...; // 通过反射拿到方法对象 Loggable log = method.getAnnotation(Loggable.class); if (log != null) { System.out.println("描述：" + log.value()); }二、反射（Reflection）2.1 反射的核心类类作用获取方式Class类的元信息对象.getClass()、类名.class、Class.forName("全类名")Method方法信息Class 对象的 getMethod(...)、getDeclaredMethods() 等Field字段信息Class 对象的 getField(...)、getDeclaredFields() 等Constructor构造器信息Class 对象的 getConstructor(...)、getDeclaredConstructors() 等2.2 获取 Class 对象的三种方式// 1. 通过类名.class Class<String> clz1 = String.class; // 2. 通过实例的 getClass() String s = "hello"; Class<?> clz2 = s.getClass(); // 3. 通过完全限定名字符串 Class<?> clz3 = Class.forName("java.lang.String");2.3 获取 Method 对象方法说明getMethod(name, paramTypes...)获取指定名称和参数的 public 方法（含继承）getMethods()获取所有 public 方法（含继承）getDeclaredMethod(name, paramTypes...)获取本类中声明的方法（不含继承，可访问私有）getDeclaredMethods()获取本类中声明的所有方法（不含继承）示例：获…

Spring JDBC + JdbcTemplate
type: item
date: 2026-06-03T11:29:00+08:00
description: Spring JDBC + JdbcTemplate 学习笔记（方法驱动格式）本笔记聚焦于 JdbcTemplate 中常用的数据库操作方法。每个方法均按以下结构介绍：方法：方法签名/名称作用：该方法解决什么问题参数：主要参数的含义和类型方法案例：代码示例（可直接运行）实例说明：对案例的详细解释，包括注意事项前置环境：Spring 6 + HikariCP + HSQLDB，配置了 DataSource 和 JdbcTemplate Bean。第一章：准备工作与环境配置1.1 Maven 依赖<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.0.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>6.0.0</version> </dependency> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>5.0.1</version> </dependency> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.7.1</version> </dependency>1.2 Spring 配置类（AppConfig）@Configuration @ComponentScan @PropertySource("jdbc.properties") public class AppConfig { @Value("${jdbc.url}") String jdbcUrl; @Value("${jdbc.username}") String jdbcUsername; @Value("${jdbc.password}") String jdbcPassword; @Bean DataSource dataSource() { HikariConfig config = new HikariConfig(); config.setJdbcUrl(jdbcUrl); config.setUsername(jdbcUsername); config.setPassword(jdbcPassword); return new HikariDataSource(config); } @Bean JdbcTemplate jdbcTemplate(DataSource dataSource) { return new JdbcTemplate(dataSource); } }1.3 数据库初始化（自动建表）@Component public class DatabaseInitializer { @Autowired JdbcTemplate jdbcTemplate; @PostConstruct void init() { jdbcTemplate.update("CREATE TABLE IF NOT EXISTS users (" + "id BIGINT IDENTITY PRIMARY KEY, " + "email VARCHAR(100) NOT NULL UNIQUE, " + "password VARCHAR(100), " + "name VARCHAR(100))"); } }第二章：核心方法详解（按格式组织）方法一：queryForObject() – 查询单行记录并映射为对象作用：执行查询 SQL，期望返回一条记录，并将结果集的第一行通过 RowMapper 转换成 Java 对象。若查询结果为空或大于一行则抛出异常。参数：String sql – SQL 语句（可使用 ? 占位符）RowMapper<T> rowMapper – 将 ResultSet 当前行映射为 T 对象的函数式接口Object... args – 可变参数，按顺序替换 SQL 中的 ?方法案例：根据 email 查询用户public User getUserByEmail(String email) { String sql = "SELECT id, email, password, name FROM users WHERE email = ?"; ret…

未命名文档
type: item
date: 2026-06-02T17:14:54+08:00
description: 注意：Java 8 环境下，javax.annotation（@PostConstruct、@PreDestroy、@Resource）是 JDK 自带的，无需额外添加依赖。Spring Boot 2.7 使用 Spring Framework 5.x，完美支持 JSR-250 规范。一、Bean 的作用域（Scope）Spring 中的 Bean 默认是 单例（Singleton），即整个容器内只有一个实例。也可以通过 @Scope 指定为 原型（Prototype），每次获取都创建新实例。1.1 Singleton（单例）—— 默认创建时机：容器启动时（除非设置为 @Lazy）。销毁时机：容器正常关闭时，会调用 @PreDestroy 方法。获取结果：每次注入或 getBean() 返回同一个对象。@Component // 默认 singleton public class MailService { // ... }1.2 Prototype（原型）行为：每次获取（@Autowired 或 getBean）都会创建一个全新的实例。生命周期差异：容器负责创建 → 依赖注入 → 调用 @PostConstruct。容器不负责销毁：即使你在原型 Bean 中写了 @PreDestroy，Spring 不会自动调用它。需要你自己写代码清理（例如手动调用销毁方法）。声明方式：@Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // 或 @Scope("prototype") public class MailSession { @PostConstruct public void init() { System.out.println("原型 Bean 初始化，每次都会调用"); } @PreDestroy public void destroy() { System.out.println("原型 Bean 销毁 — 不会被 Spring 自动调用"); } }最佳实践：如果没有特殊需求，优先使用 singleton。prototype 适合那些有状态且不宜共享的对象，但要记得手动管理资源释放。二、注入 List —— 自动收集同一类型的所有 Bean当你需要在一个地方使用某个接口的 所有实现类 时，可以直接注入 List<T>，Spring 会自动把该接口的所有 Bean 放进列表。示例：多个 Validator 实现public interface Validator { void validate(String email, String password, String name); } @Component // 自动被收集 public class EmailValidator implements Validator { @Override public void validate(String email, String password, String name) { // email 校验逻辑 } } @Component public class PasswordValidator implements Validator { @Override public void validate(String email, String password, String name) { // password 校验逻辑 } } @Component public class NameValidator implements Validator { @Override public void validate(String email, String password, String name) { // name 校验逻辑 } }注入 List@Component public class Validators { @Autowired private List<Validator> validators; // Spring 自动注入所有 Validator 实现 public void validate(String email, String password, String name) { for (Validator v : validators) { v.validate(email, password, name); } } }控制顺序 —— @Order默认顺序不确定，可以用 @Order 注解指定（数字越小优先级越高）。@Component @Order(1) public class EmailValidator impleme…