浅析 SpringMVC 中返回对象的循环引用问题

问题发现

@RestController、@ResponseBody 等注解是我们在写 Web 应用时打交道最多的注解了,我们经常有这样的需求:返回一个对象给前端,SpringMVC 帮助我们序列化成 JSON 对象。而今天我要分享的话题也不是什么高深的内容,可能大家多多少少也都遇到过,那就是返回对象中存在循环引用时的问题,分享我的一些思考。

该问题非常简单容易复现,直接上代码。

准备两个循环引用的对象:

1
2
3
4
5
6
7
8
9
10
11
@Data
public class Person {
private String name;
private IdCard idCard;
}

@Data
public class IdCard {
private String id;
private Person person;
}

警惕 Spring Boot Actuator 引发的安全漏洞

前言

一年一度的 HW 行动开始了,最近也是被各种安全漏洞搞的特别闹心,一周能收到几十封安全团队扫描出来的漏洞邮件,这其中有一类漏洞很容易被人忽视,但影响面却极广,危害也极大,我说出它的名字你应该也不会感到陌生,正是 Spring Boot Actuator

写这篇文章前,我跟我的朋友做了一个小调查,问他们对 Spring Boot Actuator 的了解,结果惊人的一致,大家都知道 Spring Boot 提供了 spring-boot-starter-actuator 的自动配置,但却很少有人真正用到它相关的特性。在继续往下面看这篇文章时,大家也可以先思考下几个问题:

  1. 检查下你开发的项目中有引入 spring-boot-starter-actuator 依赖吗?
  2. 你在项目中有真正用到 spring-boot-starter-actuator 的有关功能吗?
  3. 你知道 spring-boot-starter-actuator 的安全风险和正确配置方式吗?

Spring 中的 XML schema 扩展机制

前言

很久没有写关于 Spring 的文章了,最近在系统梳理 Dubbo 代码的过程中发现了 XML schema 这个被遗漏的知识点。由于工作中使用 SpringBoot 比较多的原因,几乎很少接触 XML,此文可以算做是亡羊补牢,另一方面,也为后续的 Dubbo 源码解析做个铺垫。

XML schema 扩展机制是啥?这并不是一块很大的知识点,翻阅一下 Spring 的文档,我甚至没找到一个贯穿上下文的词来描述这个功能,XML Schema Authoring 是文档中对应的标题,简单来说:


从 Spring-Session 源码看 Session 机制的实现细节

去年我曾经写过几篇和 Spring Session 相关的文章,从一个未接触过 Spring Session 的初学者视角介绍了 Spring Session 如何上手,如果你未接触过 Spring Session,推荐先阅读下「从零开始学习 Spring Session」系列(https://www.cnkirito.moe/categories/Spring-Session/) Spring Session 主要解决了分布式场景下 Session 的共享问题,本文将从 Spring Session 的源码出发,来讨论一些 Session 设计的细节。


浅析分布式下的事件驱动机制(PubSub 模式)

上一篇文章《浅析 Spring 中的事件驱动机制》简单介绍了 Spring 对事件的支持。Event 的整个生命周期,从 publisher 发出,经过 applicationContext 容器通知到 EventListener,都是发生在单个 Spring 容器中,而在分布式场景下,有些时候一个事件的产生,可能需要被多个实例响应,本文主要介绍分布式场景下的事件驱动机制,由于使用了 Redis,ActiveMQ,也可以换一个名词来理解:分布式下的发布订阅模式。

JMS 规范

在日常项目开发中,我们或多或少的发现一些包一些类位于 java 或 javax 中,他们主要提供抽象类,接口,提供了一种规范,如 JPA,JSR,JNDI,JTA,JMS,他们是由 java 指定的标准规范,一流企业做标准、二流企业做品牌、三流企业做产品,虽然有点调侃的意味,但也可以见得它的重要意义。而 JMS 就是 java 在消息服务上指定的标准

The Java Message Service (JMS) API is a messaging standard that allows application components based on the Java Platform Enterprise Edition (Java EE) to create, send, receive, and read messages. It enables distributed communication that is loosely coupled, reliable, and asynchronous.

JMS(JAVA Message Service,java 消息服务)API 是一个消息服务的标准或者说是规范,允许应用程序组件基于 JavaEE 平台创建、发送、接收和读取消息。它使分布式通信耦合度更低,消息服务更加可靠以及异步性。

消息中间件有非常多的实现,如 ActiveMQ,RabbitMQ,RocketMQ,而他们同一遵循的接口规范,便是 JMS。在下文中即将出现的 ConnectionFactory,Destination,Connection,Session,MessageListener,Topic,Queue 等等名词,都是 JMS 核心的接口,由于本文的初衷并不是讲解 MQ&JMS,所以这些机制暂且跳过。

定义分布式事件需求

在上一个项目中,我们对接了外网的 http 接口,而安全性的保障则是交给 OAuth2 来完成,作为 OAuth2 的客户端,我们需要获取服务端返回的 token,而 token 接口的获取次数每个月是有限制的,于是我们选择使用 Redis 来保存,定时刷新。由于每次发起请求时都要携带 token,为了更高的性能减少一次 redis io,我们在 TokenService 中使用了本地变量缓存 token。于是形成如下的 token 获取机制:

token 获取流程

这个图并不复杂,只是为了方便描述需求:首先去本地变量中加载 token,若 token==null,则去 Redis 加载,若 Redis 未命中(token 过期了),则最终调用外部的 http 接口获取实时的 token,同时存入 redis 中和本地变量中。

这个需求设计到这样一个问题:大多数情况下是单个实例中发现 redis 中的 token 为空,而它需要同时获取最新 token,并通知其他的实例也去加载最新的 token,这个时候事件广播就可以派上用场了。

由于 token 缓存在了 Redis 中,我们首先介绍 Redis 的发布订阅机制。


浅析 Spring 中的事件驱动机制

今天来简单地聊聊事件驱动,其实写这篇文章挺令我挺苦恼的,因为事件驱动这个名词,我没有找到很好的定性解释,担心自己的表述有误,而说到事件驱动可能立刻联想到如此众多的概念:观察者模式,发布订阅模式,消息队列 MQ,消息驱动,事件,EventSourcing… 为了不产生歧义,笔者把自己所了解的这些模棱两可的概念都列了出来,再开始今天的分享。

  • 在设计模式中,观察者模式可以算得上是一个非常经典的行为型设计模式,猫叫了,主人醒了,老鼠跑了,这一经典的例子,是事件驱动模型在设计层面的体现。
  • 另一模式,发布订阅模式往往被人们等同于观察者模式,但我的理解是两者唯一区别,是发布订阅模式需要有一个调度中心,而观察者模式不需要,例如观察者的列表可以直接由被观察者维护。不过两者即使被混用,互相替代,通常不影响表达。
  • MQ,中间件级别的消息队列(e.g. ActiveMQ,RabbitMQ),可以认为是发布订阅模式的一个具体体现。事件驱动 -> 发布订阅 ->MQ,从抽象到具体。
  • java 和 spring 中都拥有 Event 的抽象,分别代表了语言级别和三方框架级别对事件的支持。
  • EventSourcing 这个概念就要关联到领域驱动设计,DDD 对事件驱动也是非常地青睐,领域对象的状态完全是由事件驱动来控制,由其衍生出了 CQRS 架构,具体实现框架有 AxonFramework。
  • Nginx 可以作为高性能的应用服务器(e.g. openResty),以及 Nodejs 事件驱动的特性,这些也是都是事件驱动的体现。

Re:从零开始的 Spring Session(二)

上一篇文章介绍了一些 Session 和 Cookie 的基础知识,这篇文章开始正式介绍 Spring Session 是如何对传统的 Session 进行改造的。官网这么介绍 Spring Session:

Spring Session provides an API and implementations for managing a user’s session information. It also provides transparent integration with:

  • HttpSession - allows replacing the HttpSession in an application container (i.e. Tomcat) neutral way. Additional features include:
    • Clustered Sessions - Spring Session makes it trivial to support clustered sessions without being tied to an application container specific solution.
    • Multiple Browser Sessions - Spring Session supports managing multiple users’ sessions in a single browser instance (i.e. multiple authenticated accounts similar to Google).
    • RESTful APIs - Spring Session allows providing session ids in headers to work with RESTful APIs
  • WebSocket - provides the ability to keep the HttpSession alive when receiving WebSocket messages

其具体的特性非常之多,具体的内容可以从文档中了解到,笔者做一点自己的总结,Spring Session 的特性包括但不限于以下:

  • 使用 GemFire 来构建 C/S 架构的 httpSession(不关注)
  • 使用第三方仓储来实现集群 session 管理,也就是常说的分布式 session 容器,替换应用容器(如 tomcat 的 session 容器)。仓储的实现,Spring Session 提供了三个实现(redis,mongodb,jdbc),其中 redis 使我们最常用的。程序的实现,使用 AOP 技术,几乎可以做到透明化地替换。(核心)
  • 可以非常方便的扩展 Cookie 和自定义 Session 相关的 Listener,Filter。
  • 可以很方便的与 Spring Security 集成,增加诸如 findSessionsByUserName,rememberMe,限制同一个账号可以同时在线的 Session 数(如设置成 1,即可达到把前一次登录顶掉的效果)等等

介绍完特性,下面开始一步步集成 Spring Session


Re:从零开始的 Spring Session(一)

Session 和 Cookie 这两个概念,在学习 java web 开发之初,大多数人就已经接触过了。最近在研究跨域单点登录的实现时,发现对于 Session 和 Cookie 的了解,并不是很深入,所以打算写两篇文章记录一下自己的理解。在我们的应用集成 Spring Session 之前,先补充一点 Session 和 Cookie 的关键知识。

由于 http 协议是无状态的协议,为了能够记住请求的状态,于是引入了 Session 和 Cookie 的机制。我们应该有一个很明确的概念,那就是 Session 是存在于服务器端的,在单体式应用中,他是由 tomcat 管理的,存在于 tomcat 的内存中,当我们为了解决分布式场景中的 session 共享问题时,引入了 redis,其共享内存,以及支持 key 自动过期的特性,非常契合 session 的特性,我们在企业开发中最常用的也就是这种模式。但是只要你愿意,也可以选择存储在 JDBC,Mongo 中,这些,spring 都提供了默认的实现,在大多数情况下,我们只需要引入配置即可。而 Cookie 则是存在于客户端,更方便理解的说法,可以说存在于浏览器。Cookie 并不常用,至少在我不长的 web 开发生涯中,并没有什么场景需要我过多的关注 Cookie。http 协议允许从服务器返回 Response 时携带一些 Cookie,并且同一个域下对 Cookie 的数量有所限制,之前说过 Session 的持久化依赖于服务端的策略,而 Cookie 的持久化则是依赖于本地文件。虽然说 Cookie 并不常用,但是有一类特殊的 Cookie 却是我们需要额外关注的,那便是与 Session 相关的 sessionId,他是真正维系客户端和服务端的桥梁。


解析 Spring 中的 ResponseBody 和 RequestBody

spring,restful,前后端分离这些关键词都是大家耳熟能详的关键词了,一般 spring 常常需要与前端、第三方使用 JSON,XML 等形式进行交互,你也一定不会对 @RequestBody 和 @ResponseBody 这两个注解感到陌生。


Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×