教程

Spring 核心注解

1、概览 我们可以通过 org.springframework.beans.factory.annotation 和 org.springframework.context.annotation 包中的注解来使用 Spring DI 引擎的功能。 通常把这些注解称为 “Spring 核心注解”。 2、和 DI 相关的注解 2.1、@Autowired 可以在构造函数、Setter 方法或字段注入中使用 @Autowired 注解,Spring 会解析并注入依赖。 构造函数注入: class Car { Engine engine; @Autowired Car(Engine engine) { this.engine = engine; } } Setter 注入: class Car { Engine engine; @Autowired void setEngine(Engine engine) { this.engine = engine; } } 字段注入: class Car { @Autowired Engine engine; } @Autowired 有一个名为 required 的 boolean 参数,默认值为 true。当 Spring 找不到合适的 Bean 进行注入时,它会控制 Spring 的行为。当值为 true 时,会抛出异常,反之则不会。

REST 查询语言 - 实现 OR 操作

1、概览 本文将扩展 上一篇文章 中实现的高级搜索操作,在 REST API 查询语言中加入基于 OR 的搜索条件。 2、实现方法 以前,search 查询参数中的所有条件都是由 AND 运算符组成的条件。 现在,使用标志来指示必须使用 OR 运算符组合条件。 http://localhost:8080/users?search=firstName:john,'lastName:doe 注意,这里用单引号标记了条件 lastName,以示区别。我们会在条件值对象 SpecSearchCriteria 中捕获 OR 运算符的 Predicate: public SpecSearchCriteria( String orPredicate, String key, SearchOperation operation, Object value) { super(); this.orPredicate = orPredicate != null && orPredicate.equals(SearchOperation.OR_PREDICATE_FLAG); this.key = key; this.operation = operation; this.value = value; } 3、改进 UserSpecificationBuilder 修改 UserSpecificationBuilder,以在构建 Specification<User> 时考虑 OR 条件: public Specification<User> build() { if (params.size() == 0) { return null; } Specification<User> result = new UserSpecification(params.

REST 查询语言 - 高级搜索操作

1、概览 前面几篇文章介绍了三种 REST 查询语言的实现方式,分别是:JPA Criteria、Spring Data JPA Specification 和 QueryDSL。 本文将扩展前面几篇文章中开发的 REST 查询语言,使其包含更多搜索操作,如:等于、不等于、大于、小于、前缀、后缀、包含和类似(Like)。 本文选择使用 Specification,因为它是一种清晰而灵活的表示操作的方式。 2、SearchOperation 枚举 首先,通过枚举来更好地定义各种支持的搜索操作: public enum SearchOperation { EQUALITY, NEGATION, GREATER_THAN, LESS_THAN, LIKE, STARTS_WITH, ENDS_WITH, CONTAINS; public static final String[] SIMPLE_OPERATION_SET = { ":", "!", ">", "<", "~" }; public static SearchOperation getSimpleOperation(char input) { switch (input) { case ':': return EQUALITY; case '!': return NEGATION; case '>': return GREATER_THAN; case '<': return LESS_THAN; case '~': return LIKE; default: return null; } } } 有两组操作:

使用 Spring Data JPA 和 Querydsl 构建 REST 查询语言

1、概览 在前两篇文章中,我们使用 JPA Criteria 和 Spring Data JPA Specification 构建了相同的搜索/过滤功能。 本文将带你了解如何使用 Spring Data JPA 和 Querydsl 构建 REST API 查询语言。 2、Querydsl 配置 首先,在 pom.xml 中添加以下依赖: <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <version>4.2.2</version> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-jpa</artifactId> <version>4.2.2</version> </dependency> 还需要配置 APT(注解处理工具)插件: <plugin> <groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.1.3</version> <executions> <execution> <goals> <goal>process</goal> </goals> <configuration> <outputDirectory>target/generated-sources/java</outputDirectory> <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor> </configuration> </execution> </executions> </plugin> 该插件会根据实体类生成 Q 开头的查询类。 关于如何在 Spring Boot 中使用 QueryDSL,你可以参阅 这篇文章。 3、MyUser 实体 定义 MyUser 实体,用于在 API 中进行搜索: @Entity public class MyUser { @Id @GeneratedValue(strategy = GenerationType.

使用 Spring Data Specification 构建 REST 查询语言

1、概览 上一篇文章 介绍了一个基于 JPA Criteria 的查询语言解决方案。 本文将带你了解如何使用 Spring Data JPA 和 Specification 构建一个搜索/过滤 REST API。 2、 User 实体 创建一个 User 实体类,用于检索 API: @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String firstName; private String lastName; private String email; private int age; // get、set 方法 } 3、使用 Specification 进行过滤 来看看如何使用自定义 Spring Data JPA Specification 进行查询。 创建一个实现 Specification 接口的 UserSpecification,并传入自己的约束条件来构建实际查询: public class UserSpecification implements Specification<User> { private SearchCriteria criteria; @Override public Predicate toPredicate (Root<User> root, CriteriaQuery<?

使用 Spring 和 JPA Criteria 构建 REST 查询语言

1、概览 在接下来的一系列文章中,我将带你了解一种用于 REST API 的简单查询语言。 为什么要使用查询语言(Query Language)?因为对于任何足够复杂的 API 来说,仅仅通过简单的字段来搜索/过滤资源是远远不够的。查询语言更加灵活,可以准确过滤选出所需的资源。 2、User 实体 首先,创建一个 User 实体,用于在过滤/搜索 API 中使用: @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String firstName; private String lastName; private String email; private int age; } 3、使用 CriteriaBuilder 过滤 构建查询抽象是一个平衡问题。一方面,需要足够的灵活性,另一方面,需要保持可控的复杂性。高级查询的功能很简单 - 输入一些约束条件,然后得到一些结果。 来看看如何使用: @Repository public class UserDAO implements IUserDAO { @PersistenceContext private EntityManager entityManager; @Override public List<User> searchUser(List<SearchCriteria> params) { CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery<User> query = builder.

Spring Kafka 的 “Trusted Packages” 特性

1、概览 本文将带你了解 Spring Kafka 中的 “Trusted Packages” 功能,了解其背后的动机以及用法。 2、先决条件 一般来说,Spring Kafka 模块允许我们指定一些关于发送的 POJO 的元数据。它通常采用 Kafka Message Header 的形式。 例如,可以这样配置 ProducerFactory: @Bean public ProducerFactory<Object, SomeData> producerFactory() { JsonSerializer<SomeData> jsonSerializer = new JsonSerializer<>(); jsonSerializer.setAddTypeInfo(true); return new DefaultKafkaProducerFactory<>( producerFactoryConfig(), new StringOrBytesSerializer(), jsonSerializer ); } @Data @AllArgsConstructor static class SomeData { private String id; private String type; private String status; private Instant timestamp; } 然后,使用上面用 producerFactory 配置的 KafkaTemplate 在一个 Topic 中生产一条新消息: public void sendDataIntoKafka() { SomeData someData = new SomeData("1", "active", "sent", Instant.

使用 Spring Security OAuth2 实现 SSO 单点登录

1、概览 本文将带你了解如何使用 Spring Security OAuth 和 Spring Boot 以及 Keycloak 作为授权服务器来实现单点登录(SSO)。 我们会使用 4 个不同的应用: 授权服务器 - 中央认证机制 资源服务器 - Foo 资源的提供者 两个客户端应用 - 使用 SSO 的应用 简单地说,当用户试图通过一个客户端应用访问资源时,他们会被重定到授权服务器进行身份认证。Keycloak 会对用户进行登录,在登录第一个应用后,如果使用同一浏览器访问第二个客户端应用,用户无需再次输入凭据。 使用 OAuth2 的授权码(Authorization Code)模式。 Spring Security 将此功能称为 OAuth 2.0 登录,而 Spring Security OAuth 将其称为 SSO。 2、授权服务器 以前,通过 Spring Security OAuth 可以将授权服务器设置为 Spring 应用。 不过,Spring Security OAuth 已被 Spring 弃用,现在可以使用 Keycloak 作为授权服务器。 因此,这次我们在 Spring Boot 应用中把授权服务器设置为嵌入式 Keycloak 服务器。 在 预配置 中,我们将定义两个客户端,即 ssoClient-1 和 ssoClient-2,分别对应每个客户端应用。

使用 Spring Security OAuth2 实现 SSO 单点登录(传统技术栈)

1、概览 本文将带你了解如何使用 Spring Security OAuth 和 Spring Boot 实现单点登录(SSO)。 我们会使用三个不同的应用: 授权服务器 - 中央认证机制 两个客户端应用:使用 SSO 的应用 简单来说,当用户试图访问客户端应用中受保护的页面时,他们会被重定向到过身份认证服务器进行身份验。使用 OAuth2 的 “授权码(Authorization Code)模式”。 注:本文使用的是 Spring OAuth 传统技术。如果你想查看新版 Spring Security 的版本,请参阅《使用 Spring Security OAuth2 实现 SSO 单点登录》。 2、客户端应用 从客户端应用开始。当然,使用 Spring Boot 构建。 2.1、Maven 依赖 在 pom.xml 中加入以下依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> <version>2.0.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity4</artifactId> </dependency> 2.2、Security 配置 接下来是最重要的部分,即客户端应用的 Security 配置:

Java 中的 UndeclaredThrowableException 异常

1、概览 本文将带你了解 Java 抛出 UndeclaredThrowableException 异常的原因。 2、UndeclaredThrowableException 从理论上讲,当我们尝试抛出一个未声明的受检异常时,Java 会抛出一个 UndeclaredThrowableException 异常。也就是说,我们没有在 throws 子句中声明受检异常,但却在方法体中抛出了该异常。 受检异常 - 指的是必须要调用者用 try/catch 语句处理或者是再次 throws 出去的异常(即非 RuntimeException 子类)。 有人可能会说这是不可能的,因为 Java 编译器会通过编译错误来防止这种情况发生。 例如,如果我们尝试编译: public void undeclared() { throw new IOException(); } Java 编译器提示的失败信息如下: java: unreported exception java.io.IOException; must be caught or declared to be thrown 尽管在编译时可能不会抛出未声明的受检异常,但在运行时仍有可能发生。 例如,一个运行时代理拦截一个不抛出任何异常的方法: public void save(Object data) { // 省略 } 如果代理本身抛出了受检异常,从调用者的角度来看,save 方法也会抛出受检异常。调用者可能对该代理一无所知,因此会将该异常归咎于 save 方法。 在这种情况下,Java 会将实际已检查异常封装在 UndeclaredThrowableException 中,然后抛出 UndeclaredThrowableException。而 UndeclaredThrowableException 本身就是一个 非受检异常(RuntimeException)。