HashMap Initialization 初始化写法

不同的初始化写法在可读性、序列化兼容性和性能上有一些差异,本文记录双括号初始化写法及其注意事项。本文原本是 HashMap 的一部分,因主题独立而拆分为单独文章。 双括号初始化 HashMap 是一种常用的数据结构,一般用来做数据字典或者 Hash 查找的容器。普通写法一般会这么初始化: HashMap<String, String> map = new HashMap<String, String>(); map.put("Name", "June"); map.put("QQ", "2572073701"); 看完这段代码,很多人都会觉得这么写太啰嗦了,对此,还有一种更紧凑的写法: HashMap<String, String> map = new HashMap<String, String>() { { put("Name", "June"); put("QQ", "2572073701"); } }; 看起来优雅了不少,一步到位。这里的双括号到底是什么意思呢?看看下面的代码就知道了: public class Test { /* private static HashMap<String, String> map = new HashMap<String, String>() { { put("Name", "June"); put("QQ", "2572073701"); } }; */ public Test() { System.out.println("Constructor called"); // constructor called } static { System.out.println("Static block called"); // static block called } { System.out.println("Instance initializer called"); // instance initializer called } public static void main(String[] args) { new Test(); System.out.println("======================="); new Test(); } } 输出: ...

Created 3036-07-03 · Updated 3036-07-03 · 2 min · 405 words · -

ConcurrentHashMap

ConcurrentHashMap ConcurrentHashMap 是一个线程安全的Hash Table,它的主要功能是提供了一组和HashTable功能相同但是线程安全的方法。ConcurrentHashMap可以做到读取数据不加锁,并 且其内部的结构可以让其在进行写操作的时候能够将锁的粒度保持地尽量地小,不用对整个ConcurrentHashMap加锁。 ConcurrentHashMap 由 JDK 5(2004 年)随 java.util.concurrent 包一起引入(JSR 166,Doug Lea 主导),是最早一批加入的并发容器之一,用于替代性能受限的 Hashtable 和外部同步的 HashMap。 与 HashMap 的区别 ConcurrentHashMap 和 HashMap 都基于数组 + 链表(JDK 1.8 后含红黑树)实现,核心区别在于线程安全性和并发设计: 维度 HashMap ConcurrentHashMap 线程安全 否,多线程并发写会导致死循环(1.7 扩容)或数据丢失 是,专为并发场景设计 加锁粒度 无锁(需要外部自行同步,如 Collections.synchronizedMap) 1.7:Segment 分段锁;1.8:CAS + 对单个桶 synchronized,粒度远小于对整表加锁 null 支持 key、value 都允许 null key、value 都不允许 null(避免 get() 返回 null 时无法区分键不存在和值为 null 的二义性) 读操作 无锁,但非并发安全 无锁(val/next 用 volatile 修饰保证可见性),且并发安全 典型场景 单线程或已有外部同步的场景 多线程共享同一个 Map 的场景 简单说:HashMap 用来替代非线程安全场景下的 Hashtable,ConcurrentHashMap 用来替代线程安全场景下的 Hashtable——二者是 Hashtable 拆分出的两条演进路线,而不是互相替代的关系。历史背景与 Hashtable 的详细对比见 HashMap 与 Hashtable 的区别。 ...

Created 12126-08-12 · Updated 3036-07-03 · 8 min · 1620 words · -

HashMap 与 Hashtable 的区别

HashMap 与 Hashtable 的区别 Hashtable 的应用非常广泛,HashMap 是新框架中用来代替 Hashtable 的类,也就是说建议使用 HashMap,不要使用 Hashtable。可能你觉得 Hashtable 很好用,为什么不用呢?这里简单分析它们的区别。 Hashtable 的方法是同步的,HashMap 未经同步,所以在多线程场合要手动同步 HashMap,这个区别就像 Vector 和 ArrayList 一样。 Hashtable 不允许 null 值(key 和 value 都不可以),HashMap 允许 null 值(key 和 value 都可以)。 HashMap 去掉了 Hashtable 的 contains(Object value) 方法,但是加上了 containsValue() 和 containsKey() 方法。 Hashtable 使用 Enumeration,HashMap 使用 Iterator。以上只是表面的不同,它们的实现也有很大的不同。 Hashtable 中 hash 数组默认大小是 11,增加的方式是 old * 2 + 1。HashMap 中 hash 数组的默认大小是 16,而且一定是 2 的指数。 哈希值的使用不同,Hashtable 直接使用对象的 hashCode: int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; HashMap 重新计算 hash 值,并用位与运算代替求模: ...

Created 21216-09-21 · Updated 3036-07-03 · 2 min · 337 words · -

Spring Security Remember Me 实现机制

登录之外,很多系统还需要"关闭浏览器后仍保持登录",这就是 remember-me(记住我)。Spring Security 提供两种实现,机制和安全性差别很大。SecurityFilterChain 整体认证流程见 Spring Security。 常见误解:不是"让 session 活得更久" 容易望文生义地以为 remember-me 就是把普通 session 的过期时间设得很长,下次访问时顺便刷新一下 session id。实际上标准做法是两套凭证分层: 登录后先发一个短期 session(比如 30 分钟)。 勾选了 remember-me,再额外发一个长期凭证(cookie 里的 series:token)。 下次访问时如果短期 session 已失效,服务端用长期凭证验证通过后,重新建立一个新的 session,而不是延长旧 session 的寿命。 这样做的原因:短期 session 追求高频、低延迟访问(常放内存/Redis),长期凭证追求低频、可撤销、可审计(放数据库即可)。如果把两者合并成"一个超长有效期的 session",会导致服务端要长期保留大量会话状态,且缺乏按设备单独撤销的能力(下文详述)。 简单哈希令牌(TokenBasedRememberMeServices,默认方式) Cookie 内容: username:expirationTime:md5(username + ":" + expirationTime + ":" + password + ":" + key) 优点:无状态,不查数据库。 缺陷:服务端无法单独让某一台设备的 cookie 失效——除非改密码或改全局 key,那样会让所有设备同时下线;且签名依赖密码本身,密码不变、cookie 一旦泄露就能一直重放。 持久化令牌(PersistentTokenRepository) 数据库维护一张 persistent_logins 表,每一行代表一次登录(一个设备/浏览器): series username token last_used Cookie 里存的是 series:token(两个随机值),不含密码信息。 每次带着 remember-me cookie 的请求认证成功后,token 值会轮换,但 series 不变。判定逻辑发生在服务端:先用 series 查到数据库里当前记录的 token 值,再和客户端发来的 token 比对——相等则正常轮换;series 能查到但 token 不相等,说明这个值是已经被换掉的旧值又出现了一次,PersistentTokenBasedRememberMeServices 会判定为 cookie 窃取(cookie theft),删除该 series 的记录并强制重新登录。 因为每个设备对应独立的 series 行,服务端可以按行删除来单独踢掉一个设备,不影响其他设备的登录状态。这就是"跨设备失效控制":本质是把认证状态从"无状态签名"换成了"以设备为粒度的数据库记录"。 需要澄清一点:series 并不是浏览器指纹或设备硬件 id,它只是"这一次 remember-me 登录时生成、存在这台设备 cookie 里的随机值"。它不校验设备身份,如果 cookie 原样被复制到另一台机器,series 照样验证通过——真正防重放靠的是上面的轮换 + 比对机制,不是设备识别。另外这个 cookie 和普通 cookie 一样,只要没限制 path,每次请求都会被发送,传输频率跟 session cookie 无异;真正低频的是服务端"校验/轮换"这个动作的触发频率(只在 session 失效时才用得上)。 跨设备失效控制的三个能力 可枚举:按 username 查询 persistent_logins 表,能看到该用户当前所有"记住我"的设备。 可单点撤销:删除某一行 series,对应设备下次请求 token 校验失败,强制走正常登录。 可全量撤销:改密码时清空该用户所有 series 记录,实现"改密码后所有设备强制下线"。 与 JWT refresh token 的对照 OAuth2 / JWT 体系里常见的 refresh token,跟这里的长期凭证是同一类角色:都是"低频使用、用来重建短期凭证"的东西。但安全性取决于实现方式: ...

Created 26266-12-26 · Updated 2026-07-02 · 2 min · 258 words · -

HikariCP: 连接池参数与泄漏排查

HikariCP 是 Spring Boot 2.x 及以上版本的默认数据库连接池,spring-boot-starter-jdbc/spring-boot-starter-data-jpa 在类路径上检测到它时会自动创建对应的 DataSource,见 spring-boot-relational-persistence.md。 核心设计取向 HikariCP 相比 DBCP、C3P0 等老连接池,主打「轻量、少锁竞争」:连接获取路径尽量避免同步阻塞,字节码层面做了不少微优化(如去掉不必要的包装类)。这也是它成为默认选型的主要原因——同等硬件下吞吐更高、延迟更低。 关键参数 参数 作用 建议 maximumPoolSize 连接池最大连接数 不是越大越好,经验公式 连接数 = (核心数 * 2) + 有效磁盘数;通常 10-20 已足够,过大会导致数据库端连接争用、上下文切换开销上升 minimumIdle 最小空闲连接数 建议和 maximumPoolSize 设成一致,避免连接数动态伸缩带来的抖动(官方推荐固定池大小) connectionTimeout 从池中获取连接的等待超时 默认 30s,业务上一般调到 3-5s,超时快速失败优于线程一直阻塞 idleTimeout 空闲连接被回收前的最长空闲时间 默认 10 分钟,仅在 minimumIdle < maximumPoolSize 时生效 maxLifetime 连接最大生命周期 默认 30 分钟,必须比数据库或负载均衡器的连接超时(如 MySQL wait_timeout)短,否则会用到已被服务端关闭的连接 leakDetectionThreshold 连接泄漏检测阈值 默认 0(关闭);生产建议设 30000-60000ms,超过该时间连接还未归还池会打印 WARN 日志和借出时的调用栈 application.yml 配置示例: spring: datasource: hikari: maximum-pool-size: 15 minimum-idle: 15 connection-timeout: 3000 max-lifetime: 1500000 leak-detection-threshold: 60000 maxLifetime 为什么防不住连接泄漏 容易有的误解是:既然配了 maxLifetime,超时的连接应该会被自动回收,不会一直泄漏下去。实际上 maxLifetime 只对归还回池中的空闲连接生效,对被业务代码借出、一直没有 close() 的连接不生效。 ...

Created 2026-07-02 · Updated 2026-07-02 · 1 min · 187 words · -

Servlet Filter

Servlet Filter Filter 是 Servlet 规范里的一种组件,位于容器和目标资源(Servlet/JSP)之间,可以在请求到达目标资源之前、响应离开目标资源之后做统一处理。Filter 本身不产生响应内容,只负责预处理和后处理,典型用途包括: 身份校验、权限校验 请求/响应日志记录 请求头、响应头改写 内容压缩、编码转换 多个 Filter 可以按声明顺序串成一条链(Filter Chain),这也是 责任链模式(Chain of Responsibility) 在 Servlet 规范里的经典实现;Spring Security 的 SecurityFilterChain 就是在这套机制上封装出来的应用。 Filter 接口 public interface Filter { default void init(FilterConfig filterConfig) throws ServletException { } void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; default void destroy() { } } init(FilterConfig):容器创建 Filter 实例后调用一次,用于读取初始化参数、保存 FilterConfig。 doFilter(request, response, chain):每次请求都会调用,是 Filter 的核心方法。 destroy():容器销毁 Filter 前调用一次,用于释放资源。 FilterConfig 提供访问 Filter 名称、初始化参数、ServletContext 的方法。 ...

Created 10106-06-10 · Updated 1016-07-01 · 2 min · 293 words · -

Spring Security

SecurityFilterChain SecurityFilterChain 是 Spring Security 的核心抽象,代表一条作用于 HTTP 请求的安全过滤器链。每个进入应用的请求都会依次经过链中的若干 Filter,完成认证(Authentication)、授权(Authorization)、CSRF 防护、会话管理等工作。 底层机制:从 Servlet Filter 到 Spring Security Servlet 规范本身有 Filter 接口和 FilterChain 接口。Spring Security 通过以下两层代理接入这套机制: DelegatingFilterProxy:在 Servlet 容器(Tomcat)层面注册的 Filter,作用是把请求桥接到 Spring 容器中的 Bean。它本身不做安全逻辑,只是一个"入口代理"。 FilterChainProxy:Spring 容器中的真正实现,也是一个特殊的 Filter。它持有所有注册的 SecurityFilterChain Bean,收到请求后,按顺序匹配第一条能处理该 URL 的链,然后依次执行链中的每个 Filter。 HTTP 请求 → Tomcat FilterChain → DelegatingFilterProxy(Servlet Filter,桥接到 Spring,Bean 名固定为 springSecurityFilterChain) → FilterChainProxy(Spring Bean,收集所有 SecurityFilterChain) → SecurityFilterChain 1(匹配 /api/**) UsernamePasswordAuthenticationFilter BearerTokenAuthenticationFilter ExceptionTranslationFilter AuthorizationFilter ... → SecurityFilterChain 2(匹配 /**) ... ← 链中最后一个 Filter 放行(chain.doFilter()),控制权交回 DelegatingFilterProxy → Tomcat FilterChain 继续执行其余 Filter(如果有) → DispatcherServlet → HandlerMapping 找到目标 Controller 方法 → Controller SecurityFilterChain Bean 如何被收集并接入请求 在 @Configuration 类里写的 @Bean public SecurityFilterChain filterChain(...) 方法,只是往 IoC 容器里注册了一个数据对象——具体实现类是 DefaultSecurityFilterChain,内部就是一个 Filter 列表加一个 RequestMatcher。真正让这份数据接入请求处理流程,还需要另外两步: ...

Created 23236-12-23 · Updated 1016-07-01 · 4 min · 781 words · -

Spring Boot

Spring Boot 是什么 Spring Boot 是 Pivotal (现为 VMware/Broadcom)在 2014 年推出的框架,建立在 Spring Framework 之上,目标是简化整个 Spring 生态的开发、配置和部署。它不只是简化 Spring MVC,同样简化了 Spring Data(数据库访问)、Spring Security(认证授权)、Spring Messaging(消息队列)等全家桶的配置。 核心理念是 “约定优于配置”(Convention over Configuration):框架提前定好"默认约定",只要遵循这些约定,就不需要写任何配置;只有想偏离默认行为时,才需要显式配置。 例如,classpath 里有 spring-boot-starter-web,Spring Boot 就约定你要启动一个 Web 服务,自动完成:注册 DispatcherServlet、内嵌 Tomcat 监听 8080 端口、注册 Jackson 用于 JSON 序列化。你什么都不写,java -jar app.jar 就能跑起来。需要覆盖默认行为时,只在 src/main/resources/application.yml 中写偏离默认值的部分: server: port: 9090 # 只需声明偏离默认值的配置 也可通过命令行参数临时覆盖:java -jar app.jar --server.port=9090,优先级高于 application.yml。 出现背景 Spring Boot(2014)与微服务架构的兴起同期出现。2014 年 Martin Fowler 发表了微服务架构的奠基性文章,微服务要求每个服务独立部署、快速启动、进程隔离。传统的 WAR 包 + 外部 Tomcat 的部署方式与这一需求存在明显矛盾,Spring Boot 正是为了解决这一问题而生。 ...

Created 11116-05-11 · Updated 30306-06-30 · 9 min · 1853 words · wiloon

Spring CGLIB 动态代理

CGLIB(Code Generation Library)是一个运行期生成字节码的代码生成库,能在不实现接口的情况下,为一个类动态创建子类代理。Spring 内部大量依赖它做方法拦截(method interception)——无论是 AOP 代理还是 @Configuration 类的单例保证,背后用的都是 Spring 自己维护的一份 CGLIB fork(org.springframework.cglib),而不是外部独立发布的 cglib.jar。本文以 Spring 里的 CGLIB 为主线;原始独立 CGLIB 项目的来历、维护现状作为背景信息放在文末。 为什么需要 CGLIB:JDK 动态代理的限制 JDK 自 1.3 起自带动态代理(java.lang.reflect.Proxy),使用简单,但有一个硬性限制:被代理的类必须实现一个或多个接口,代理对象本质上是这些接口的另一份实现。如果要代理一个没有实现任何接口的普通类,JDK 动态代理就无能为力了——这正是 CGLIB 存在的原因。 CGLIB 的做法是:在运行期通过字节码处理框架 ASM,动态生成目标类的一个子类,重写其中的非 final 方法,在方法体里插入拦截逻辑,再调用原方法。因为是继承实现,所以不要求目标类实现接口,但也带来限制:类和方法不能是 final(无法被继承和重写),构造函数会被调用(生成的子类需要调用父类构造器)。 CGLIB 和 ASM 的分工:CGLIB 只关心「给目标类生成一个什么样的子类代理」这个业务语义层面的问题——该重写哪些方法、方法体里怎么织入拦截逻辑、生成的类怎么加载成 Class 对象并实例化;至于「怎么把这份子类描述翻译成合法的 .class 字节码」,全部委托给 ASM 完成。可以理解成:CGLIB 决定生成一个什么样的子类,ASM 负责把这个子类真正写成字节码——CGLIB 是构建在 ASM 之上、面向「动态生成子类代理」这个具体场景的应用层库,ASM 才是真正操作字节码指令的底层工具。 JDK 动态代理 CGLIB 实现方式 实现目标接口 生成目标类的子类 前提条件 目标类必须实现接口 目标类和方法不能是 final 底层机制 反射(InvocationHandler) ASM 生成字节码 典型使用方 Spring AOP(默认,接口存在时) Spring AOP(无接口时)、Hibernate 延迟加载、EasyMock/jMock Spring 如何使用 CGLIB:两个典型场景 1. Spring AOP 方法拦截 Spring AOP 优先用 JDK 动态代理;只有当目标类没有实现任何接口时,才回退(或显式配置 proxyTargetClass=true)使用 CGLIB 生成子类代理,在方法调用前后插入切面逻辑(比如 @Transactional 开启/提交事务)。 ...

Created 15156-12-15 · Updated 30306-06-30 · 2 min · 408 words · -

Spring MVC

Spring MVC 是 Spring Framework 中负责处理 HTTP 请求的 Web 框架,实现了 MVC 设计模式。它是一个功能模块,而非独立框架。 本文专注 HTTP 处理与现用实践(注解映射、REST、校验等)。Spring Framework 整体定位、非 Web 场景(如 CLI)、早期 XML 配置、与 JDK 5 注解的关系、Web 层历史演变,见 Spring。 Spring MVC 与 Spring Boot 的关系 这两者经常被混淆,理清层次是关键。 层次关系 Spring Framework(基础框架) └── Spring MVC(Web 层模块,属于 Spring Framework 的一部分) Spring Boot(构建在 Spring Framework 之上的脚手架/工具) └── spring-boot-starter-web(包含 Spring MVC + 内嵌 Tomcat + Jackson) Spring MVC 是 Spring Framework 中负责处理 HTTP 请求的 Web 框架,实现了 MVC 设计模式。它是一个功能模块。 Spring Boot 是一个构建工具/脚手架,它并不替代 Spring MVC,而是让配置 Spring MVC(以及其他模块)变得更简单。 对比:传统 Spring MVC vs Spring Boot 方面 传统 Spring MVC Spring Boot 配置方式 XML(web.xml、applicationContext.xml)或 Java Config 自动配置,几乎零 XML 部署方式 打包为 WAR,部署到外部 Tomcat 打包为可执行 JAR,内嵌 Tomcat 依赖管理 手动指定每个依赖及版本,容易版本冲突 Starter 统一管理,版本经过验证 启动入口 通过 Servlet 容器启动 @SpringBootApplication 主类 + main() 方法 学习曲线 较陡,需理解大量 XML 配置 较平,约定优于配置 底层请求处理链路 无论是传统 Spring MVC + WAR,还是 Spring Boot + JAR,处理 HTTP 请求的调用链从未改变: ...

Created 30306-11-30 · Updated 27276-06-27 · 4 min · 810 words · -

Spring WebFlux: 响应式 Web 框架

Spring WebFlux 是 Spring 5(2017)引入的响应式 Web 框架,与基于 Servlet 的 Spring MVC 并列。Boot 总览见 Spring Boot;若只需提升并发能力而不改编程模型,可先看 Java Virtual Threads。 解决的问题 传统 Spring MVC 基于 Servlet 模型,每个请求占用一个线程,调用数据库或外部 API 时线程阻塞等待: 请求进来 → 分配线程 → 调用数据库(等待 100ms,线程阻塞)→ 调用外部 API(等待 200ms,线程阻塞)→ 返回 Tomcat 默认线程池约 200 个线程,高并发下线程池耗尽,后续请求排队。 Spring WebFlux 引入响应式编程模型,底层使用 Netty(非 Servlet 容器),少量线程处理大量并发: 请求进来 → 少量线程(CPU 核数)→ 发起数据库查询(注册回调,线程立刻去处理其他请求)→ 数据返回时触发回调继续处理 编程模型 WebFlux 使用 Reactor 库的 Mono<T>(0 或 1 个异步结果)和 Flux<T>(0 到 N 个异步结果): // Spring MVC(同步阻塞) @GetMapping("/users/{id}") public User getUser(@PathVariable Long id) { return userService.findById(id); // blocks on DB } // Spring WebFlux(异步非阻塞) @GetMapping("/users/{id}") public Mono<User> getUser(@PathVariable Long id) { return userService.findById(id); // returns a deferred result } WebFlux 的局限 编程模型与传统命令式完全不同,调试困难,学习成本高 需要支持响应式的驱动(R2DBC 替代 JDBC),不是所有库都有响应式版本 Java 21 虚拟线程的出现大幅削弱了 WebFlux 在普通业务系统里的核心优势(见 Java Virtual Threads) 何时仍选 WebFlux SSE 实时推送 需要背压控制(消费者告知生产者降速)的流式处理 已有 WebFlux 项目:继续用即可,不必为并发改回 MVC Java 网络层全景 主流 Java Web 栈最终归为两大谱系: ...

Created 26266-06-26 · Updated 27276-06-27 · 2 min · 283 words · wiloon

Spring

Spring 通常指 Spring Framework——一个以 IoC 容器为核心的 Java 企业应用基础框架。它不是专门为 Web 而生的;Web 处理只是其众多模块之一。要理解 Spring MVC、Spring Boot 在生态里的位置,需要先弄清 Spring Framework 本身解决什么问题、各模块如何分工,以及它在 J2EE 时代是如何演进来的。 Spring Framework 是什么 Spring Framework 提供: IoC 容器:创建对象、装配依赖、管理生命周期(详见 Spring IoC,依赖注入) AOP:横切关注点(日志、事务、安全等)与业务代码分离(详见 aop) 数据访问与事务:JDBC 抽象、@Transactional、与 Hibernate / JPA / MyBatis 集成 Web 层:Spring MVC(详见 Spring MVC) 集成能力:消息、邮件、调度任务等 典型分层关系: Spring Framework(基础框架) ├── Core(IoC、AOP、事件、资源抽象) ├── Data Access(JDBC、ORM 集成、事务) ├── Spring MVC(Web 层:DispatcherServlet、Controller) ├── Integration(JMS、邮件等) └── 以及 Security、Batch 等扩展项目 Spring Boot(2014,构建在 Spring Framework 之上) └── 自动配置 + Starter,简化上述模块的装配与部署(详见 [Spring Boot](./spring-boot.md)) Spring 与 Spring MVC 的关系 Spring MVC 是 Spring Framework 里的 Web 模块,不是与 Spring 平级的独立框架。 ...

Created 24246-06-24 · Updated 26266-06-26 · 6 min · 1207 words · -

Spring Boot 3.x Migration: 关键特性与约束

Spring Boot 3.0(2022 年底发布)底层依赖 Spring Framework 6.x。本文汇总从 2.x 升级到 3.x 时最常见的破坏性变更与约束。框架总览见 Spring Boot。 Spring Boot 与 Spring Framework 版本对应 Spring Boot Spring Framework 3.0.x 6.0.x 3.2.x 6.1.x 3.3.x / 3.4.x 6.1.x / 6.2.x 3.5.x 6.2.x 1. Java 版本要求 Spring Boot 3.x 最低要求 Java 17。 2. javax → jakarta 命名空间迁移(最大变更) Java EE 已捐赠给 Eclipse 基金会,品牌更名为 Jakarta EE,所有包名从 javax.* 改为 jakarta.*。这是代码层面影响最广的变更,几乎所有用到 Servlet、JPA、Bean Validation 的代码都要修改: 旧包名(javax) 新包名(jakarta) javax.servlet.* jakarta.servlet.* javax.persistence.* jakarta.persistence.* javax.validation.* jakarta.validation.* javax.transaction.* jakarta.transaction.* javax.annotation.* jakarta.annotation.* import jakarta.servlet.http.HttpServletRequest; import jakarta.persistence.Entity; 3. Spring Security 6.x:WebSecurityConfigurerAdapter 彻底移除 WebSecurityConfigurerAdapter 已彻底移除,必须改为注册 SecurityFilterChain Bean,详见 Spring Security。 ...

Created 26266-06-26 · Updated 26266-06-26 · 2 min · 214 words · wiloon

Java Virtual Threads: 虚拟线程与高并发

Java 21(2023)正式引入虚拟线程(Project Loom)。对多数业务系统,Spring MVC + 虚拟线程是比 Spring WebFlux 更低成本的并发方案。Boot 里一行配置即可开启,见下文。 什么是虚拟线程 虚拟线程由 JVM 管理,数量可达百万级。阻塞时 JVM 自动挂起虚拟线程、释放底层 OS 线程去处理其他任务,数据返回后再恢复执行——对应用代码完全透明。 这与 Go 语言的协程(goroutine)是同一思想:用户态轻量级并发,阻塞时不占用 OS 线程。Go 从 2009 年就原生支持,Java 晚了约 14 年。 Java 虚拟线程 Go 协程(goroutine) 出现时间 2023(Java 21) 2009(Go 1.0) 调度器 JVM 内置 Go runtime 初始栈内存 约 1KB 约 2KB 与现有代码兼容 ✅ 完全兼容,API 不变 — 对应用层完全透明 虚拟线程的复杂性封装在 JVM 内部,应用层代码写法与传统线程一模一样: // platform thread pool ExecutorService executor = Executors.newFixedThreadPool(200); // virtual threads: same API, one-line change ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); executor.submit(() -> { User user = db.findById(id); // looks blocking; JVM parks the virtual thread return user; }); Spring Boot 3.2 开启虚拟线程只需一行配置,业务代码不用改: ...

Created 26266-06-26 · Updated 26266-06-26 · 1 min · 162 words · wiloon

Java Annotation(注解)

Annotation 是 Java 5 引入的语言特性,中文一般叫注解。它提供了一种类似注释的机制,用来将元数据(metadata)与程序元素(类、方法、成员变量等)关联起来。 更通俗地说,注解为程序元素加上与业务逻辑无关的说明信息,供框架或工具在编译期、类加载期或运行期读取和处理。注解本身不会改变程序的执行逻辑;JVM 执行字节码时会忽略注解,只有通过反射或注解处理器才能访问其中的信息。 框架中的注解(以 Spring 为例) JDK 5 提供的是 语言机制(@interface、@Retention、反射 API);框架上的 @Autowired、@Controller 等是 框架用 @interface 自定义的注解,不是 JDK 内置。容器在启动时扫描类路径、读取注解元数据,再决定注册哪些 Bean、如何注入、如何映射 URL。 时期 典型配置方式 Spring 1.x(2004 起) 几乎全 XML(<bean>、<property>);不要求 JDK 5 Spring 2.5(2007) XML 骨架 + @Autowired、@Component / @Service、@Controller、@RequestMapping 等 Spring 3.0(2009) @Configuration + @Bean(Java Config),技术上可零 XML,但需手写各基础设施 Spring Boot(2014) 自动配置 + application.yml,常见场景几乎不用 XML 或大量 @Configuration 时间上有先后:JDK 5 GA(2004-09) 早于 Spring 大规模注解化(2006–2007);Spring 1.0(2004-03)发布时 JDK 5 尚未 GA,1.x 自然以 XML 为主。整体脉络见 Spring §与 JDK 5 注解的关系。 ...

Created 26266-12-26 · Updated 24246-06-24 · 5 min · 896 words · -

openjdk

OpenJDK OpenJDK 源码项目与 JDK 发行版(Temurin、Corretto、Dragonwell 等)说明见 OpenJDK JDK 发行版。下文主要为各平台安装命令备忘。 https://openjdk.org/projects/jdk/ https://jdk.java.net windows install openjdk https://github.com/ojdkbuild/ojdkbuild winget install Microsoft.OpenJDK.21 linux https://github.com/ojdkbuild/contrib_jdk8u-ci https://github.com/ojdkbuild/ojdkbuild/releases/download/java-1.8.0-openjdk-1.8.0.242-1.b08/java-1.8.0-openjdk-1.8.0.242-1.b08.ojdkbuild.windows.x86_64.msi ## archlinux install openjdk # openjdk sudo pacman -S jdk-openjdk sudo pacman -S openjdk-src # jdk 17 sudo pacman -S jdk17-openjdk openjdk17-doc openjdk17-src # jdk 21 sudo pacman -S jdk21-openjdk openjdk21-doc openjdk21-src # jdk8 sudo pacman -S jdk8-openjdk openjdk8-src sudo pacman -S jdk11-openjdk 切换 jdk 版本 archlinux-java help archlinux-java status archlinux-java set java-14-openjdk # 查看当前激活的版本 java -version ubuntu openjdk # 安装默认版本, 最新的 TLS 版本 sudo apt update sudo apt install default-jdk # 验证安装 java -version javac -version sudo apt install openjdk-21-jdk sudo apt install openjdk-8-jdk sudo apt install openjdk-8-source # openjdk 17, depends libc6 (>= 2.33) sudo sudo apt update && sudo apt install openjdk-17-jdk openjdk-17-source # 默认目录 ls -l /usr/lib/jvm/ # 查看 sudo update-java-alternatives -l # 切换 jdk sudo update-alternatives --config java 手动安装 openjdk download jdk JDK ...

Created 21216-04-21 · Updated 21216-06-21 · 2 min · 291 words · -

Java Bean

Java Bean 一、 javabean 是什么? Bean的中文含义是"豆子",顾名思义,JavaBean是指一段特殊的Java类, 就是有默然构造方法,只有get,set的方法的java类的对象. 最初,JavaBean的目的是为了将可以重复使用的软件代码打包标准。特别是用与帮助厂家开发在IDE下使用的java软件部件。这些包括如Grid控件,用户可以将该部件拖放到开发环境中。从此,JavaBean就可以扩展为一个 java web 应用的标准部件。 专业点解释是: JavaBean定义了一组规则 JavaBean就是遵循此规则的平常的Java对象 满足这三个条件: 1.执行java.io.Serializable 接口 2.提供无参数的构造器 3.提供getter 和 setter方法访问它的属性. 简单地说,JavaBean是用Java语言描述的软件组件模型,其实际上是一个类。这些类遵循一个接口格式,以便于使函数命名、底层行为以及继承或实现的行为,可以把类看作标准的JavaBean组件进行构造和应用。 JavaBean一般分为可视化组件和非可视化组件两种。可视化组件可以是简单的GUI元素,如按钮或文本框,也可以是复杂的,如报表组件;非可视化组件没有GUI表现形式,用于封装业务逻辑、数据库操作等。其最大的优点在于可以实现代码的可重用性。JavaBean又同时具有以下特性。 易于维护、使用、编写。 可实现代码的重用性。 可移植性强,但仅限于Java工作平台。 便于传输,不限于本地还是网络。 可以以其他部件的模式进行工作。 对于有过其他语言编程经验的读者,可以将其看作类似微软的ActiveX的编程组件。但是区别在于JavaBean是跨平台的,而ActiveX组件则仅局限于Windows系统。总之,JavaBean比较适合于那些需要跨平台的、并具有可视化操作和定制特性的软件组件。 2 使用JavaBean的原因 程序中往往有重复使用的段落,JavaBean就是为了能够重复使用而设计的程序段落,而且这些段落并不只服务于某一个程序,而且每个JavaBean都具有特定功能,当需要这个功能的时候就可以调用相应的JavaBean。从这个意义上来讲,JavaBean大大简化了程序的设计过程,也方便了其他程序的重复使用。 JavaBean传统应用于可视化领域,如AWT (窗口工具集) 下的应用。而现在,JavaBean更多地应用于非可视化领域,同时,JavaBean在服务器端的应用也表现出强大的优势。非可视化的JavaBean可以很好地实现业务逻辑、控制逻辑和显示页面的分离,现在多用于后台处理,使得系统具有更好的健壮性和灵活性。JSP + JavaBean和JSP + JavaBean + Servlet成为当前开发Web应用的主流模式。 3 JavaBean的开发 在程序设计的过程中,JavaBean不是独立的。为了能够更好地封装事务逻辑、数据库操作而便于实现业务逻辑和前台程序的分离,操作的过程往往是先开发需要的JavaBean,再在适当的时候进行调用。但一个完整有效的JavaBean必然会包含一个属性,伴随若干个get/set (只读/只写) 函数的变量来设计和运行的。JavaBean作为一个特殊的类,具有自己独有的特性。应该注意以下3个方面。 JavaBean类必须有一个没有参数的构造函数。 JavaBean类所有的属性最好定义为私有的。 JavaBean类中定义函数setXxx() 和getXxx()来对属性进行操作。其中Xxx是首字母大写的私有变量名称。 维护记录 时间 修改内容 原因 2026-06-20 补充 EJB 与 JavaBean 面试速记 承接自 java-basic 移出的 JavaEE 段落 2026-06-20 删除 EJB 与 JavaBean 对比及相关段落 EJB 已边缘化,与 JavaBean 专题无关

Created 16166-10-16 · Updated 20206-06-20 · 1 min · 75 words · -

JDK 26 相对 JDK 25 的变化

JDK 26 于 2026 年 3 月 17 日 GA,是紧接 JDK 25 LTS 之后的短期特性版本(非 LTS)。OpenJDK 为其提供约六个月的支持,至 2026 年 9 月 JDK 27 发布为止。生产环境仍以 JDK 25 LTS 为主;JDK 26 适合尝鲜新特性或验证升级路径。 参考:JDK 26 项目页、JDK 25 项目页、JDK 26 Release Notes。 版本概览 项目 JDK 25 JDK 26 发布日期 2025-09-16 2026-03-17 LTS 是 否 JSR JSR 400 JSR 401 Class File 版本 69.0 70.0 Unicode 16.0.0 17.0.0 官方 JEP 数量 18 10 JDK 26 新增或正式交付的 JEP JEP 500:Prepare to Make Final Mean Final 对通过深层反射修改 final 字段的行为发出警告,为后续在 JVM 层面严格禁止此类修改做准备。JDK 26 尚未完全封死,但应开始排查依赖 setAccessible 篡改 final 的代码。 ...

Created 19196-06-19 · Updated 19196-06-19 · 3 min · 446 words · -

Java 泛型(Generics)

泛型是 JDK 5 引入的类型机制,本质是将类型参数化。可以把类型参数看作占位符:在定义类、接口或方法时声明 T、K、V 等类型变量,在使用时填入具体类型(如 String、Integer)。 泛型带来的主要好处: 可读性:从声明即可看出集合或容器中的元素类型 类型安全:非法类型在编译期报错,而非运行时才抛出 ClassCastException 消除强制转换:List<String> 的 get() 直接返回 String 为什么类型参数不能是 primitive 类型参数只能是引用类型(类、接口、枚举、数组、annotation),不能是 基本数据类型(primitive)。根本原因是:Java 泛型在语言规范和实现上,都只面向引用类型。 语言规范直接规定 JLS 要求类型参数的边界必须是引用类型。int、double、boolean 等 primitive 不是类,不能作为 T 填入: List<int> list; // ❌ 编译错误 List<Integer> list; // ✅ 用包装类代替 泛型靠类型擦除实现,擦除后是 Object Java 泛型在编译后会被擦除:List<String> 运行时其实就是 List,元素按 Object 处理。引用类型可以统一向上转型到 Object,而 primitive 不是 Object 的子类型,也不能直接赋给 Object 变量(必须先装箱): Object o = "hello"; // ✅ Object o = 42; // ❌ int 不能赋给 Object Object o = Integer.valueOf(42); // ✅ 装箱后才是引用类型 泛型容器在字节码层面按「对象引用」运作,primitive 放不进去这套模型。 ...

Created 26266-12-26 · Updated 19196-06-19 · 4 min · 713 words · -

Java 基本数据类型(Primitive Types)

Java 提供 8 种基本数据类型(primitive type):4 种整数型、2 种浮点型、1 种字符型、1 种布尔型。它们不是对象,直接存数值;与之相对的是引用类型(如 String、自定义类),详见 Java 引用类型与基本类型。 类型一览 类型 字节 位数 取值范围 / 说明 默认值 byte 1 8 -128 ~ 127 0 short 2 16 -32,768 ~ 32,767 0 int 4 32 -2,147,483,648 ~ 2,147,483,647 0 long 8 64 -2^63 ~ 2^63 - 1 0L float 4 32 约 ±3.4E38(7 位有效数字) 0.0f double 8 64 约 ±1.7E308(15 位有效数字) 0.0d char 2 16 \u0000 ~ \uffff(0 ~ 65,535) \u0000 boolean — — true / false(JVM 规范未规定精确字节数) false 未显式声明时,整型字面量默认为 int,浮点字面量默认为 double。 ...

Created 19196-09-19 · Updated 19196-06-19 · 2 min · 271 words · -