Spring 中的 @Value 注解

1、概览 本文将带你了解 Spring 中的 @Value 注解。它用于向 Spring 管理的 Bean 中的字段注入值,可在字段或构造函数/方法上注解。 2、应用设置 创建一个简单的 Spring 应用来介绍该注解的不同用法。 首先,在 Properties 文件中定义想用 @Value 注解注入的值。 Properties 文件如下: value.from.file=Value got from the file priority=high listOfValues=A,B,C 然后,在配置类中定义一个 @PropertySource,指定 Properties 文件的名称以导入该 Properties 文件。 3、示例 通过注解注入 String(这种行为完全是多此一举,这里只是为了演示): @Value("string value") private String stringValue; 使用 @PropertySource 注解导入了 Properties 文件后,就可以使用 @Value 注解处理 Properties 文件中的值。 如下,注入 value.from.file 属性值: @Value("${value.from.file}") private String valueFromFile; 也可以使用相同的语法从系统属性中设置值。 假设定义了一个名为 systemValue 的系统属性,如下: @Value("${systemValue}") private String systemValue; 可以给未定义的属性提供默认值,如下: @Value("${unknown.param:some default}") private String someDefault; 如果同一个属性在系统属性和 Properties 文件中都被定义了,那么系统属性将会生效。

Spring Boot 中的 @ConfigurationProperties 注解

1、简介 Spring Boot 有许多有用的功能,包括外部化配置和轻松访问 Properties 文件中定义的属性。上一篇文章 介绍了实现这一点的各种方法。 本文将带你了解 Spring Boot 中的 @ConfigurationProperties 注解。 2、项目设置 按照惯例,在 pom.xml 中将 spring-boot-starter-parent 添加为 <parent>: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.0</version> <relativePath/> </parent> 为了验证文件中定义的属性,还需要一个 JSR-380 实现,hibernate-validator 就是其中之一,它由 spring-boot-starter-validation 依赖提供。 把它也添加到 pom.xml 中: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> 更多详细信息可参阅 Hibernate Validator 入门。 3、示例属性 官方文档建议将配置属性隔离到单独的 POJO 中。 如下: @Configuration @ConfigurationProperties(prefix = "mail") public class ConfigProperties { private String hostName; private int port; private String from; //Get、Set 方法 } 通过 @Configuration 注解,Spring 会在 Application Context 中创建一个 Spring Bean。

Spring 和 Spring Boot 中的属性配置

1、概览 本文将带你了解如何通过 Java 配置和 @PropertySource 在 Spring 中设置和使用 Properties,以及 Properties 在 Spring Boot 中的工作原理。 2、通过注解注册 Properties 文件 Spring 3.1 引入了新的 @PropertySource 注解,作为一种方便的机制,用于将属性源添加到环境中。 可以将此注解与 @Configuration 注解结合使用: @Configuration @PropertySource("classpath:foo.properties") public class PropertiesWithJavaConfig { //... } 另一种非常有用的注册新 Properties 文件的方法是使用占位符,它允许在运行时动态选择不同的文件: @PropertySource({ "classpath:persistence-${envTarget:mysql}.properties" }) ... 2.1、定义多个属性位置 @PropertySource 注解是可重复的(Java 8 的可重复注解特性)。因此,如果使用的是 Java 8 或更高版本,就可以使用此注解来定义多个属性位置: @PropertySource("classpath:foo.properties") @PropertySource("classpath:bar.properties") public class PropertiesWithJavaConfig { //... } 当然,也可以使用 @PropertySources 注解并指定一个 @PropertySource 数组。这不仅适用于 Java 8 或更高版本,也适用于任何受支持的 Java 版本: @PropertySources({ @PropertySource("classpath:foo.properties"), @PropertySource("classpath:bar.properties") }) public class PropertiesWithJavaConfig { //.

Spring Boot 中的测试

1、概览 本文将带你了解如何使用 Spring Boot 中的框架支持来编写测试,包括可以独立运行的单元测试,以及在执行测试之前加载 Spring Application Context 的集成测试。 2、项目设置 本文中的示例项目是一个 “雇员管理 API”,提供了对 Employee 资源的一些操作。是一个典型的MVC三层架构,从 Controller 到 Service 最后到持久层。 3、Maven 依赖 首先,添加测试依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <version>2.5.0</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>test</scope> </dependency> spring-boot-starter-test 是主要的依赖,它包含了测试所需的大部分依赖。 H2 DB 是内存数据库,非常方便用于测试。 3.1、JUnit 4 从 Spring Boot 2.4 开始,JUnit 5 的 Vintage 引擎已从 spring-boot-starter-test 中移除。如果仍想使用 JUnit 4 编写测试,则需要添加以下 Maven 依赖: <dependency> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> </exclusion> </exclusions> </dependency> 4、使用 @SpringBootTest 进行集成测试 顾名思义,集成测试的重点是集成应用的不同层。这也意味着不涉及模拟(Mock)。

手动写入响应后对 @ResponseBody 注解的影响

问题回溯 2023 年 Q2 某日运营反馈一个问题,商品系统商家中心某批量工具模板无法下载,导致功能无法使用(因为模板是动态变化的) 商家中心报错如下(JSON串): {"code":-1,"msg":"失败"} 负责的同事看到失败后立即与我展开讨论(因为不是关键业务,所以不需要回滚,修复即可),我们发现新功能模板下载的代码与之前的代码有所不同,恰好之前的功能又可以正常运行,所以同事对现有代码进行改造然后预发布测试完成后再次上线。 其他业务代码: /** * 模板下载 */ @RequestMapping("/doBatchWareSetAd") public void doBatchWareSetAd(@RequestParam MultipartFile file, HttpServletResponse response) { wareBatchBusiness.doBatchWareSetAd(file, response, getLongOrgCode(), getCurrentUserPin(), getCurrentUserId()); } 上线的结果是:仍然无法使用。 其实也正常:因为两种代码在预发布都可以正常运行,在线上出错只可能是因为其他原因,只不过我们不了解底层原理,害怕它 “可能” 有问题罢了,最终查询得到的结论是权限系统管理员在线上环境没有给我们配置相应的文件,导致请求为空,导致请求失败。 探索 @ResponseBody 与主动写入响应流的关系 我们都知道 @ResponseBody 注解可以帮助我们把返回对象转化为 JSON,方便展示和交互。 那它到底是如何工作的呢,请看下面的讲解: 代码案例 1 @RequestMapping("/test1") @ResponseBody public Map<String, String> test1(HttpServletResponse response) { Map<String, String> map = new HashMap<>(); map.put("1", "1"); return map; } // 响应 JSON报文 Debug 代码,发现其核心处理类为:RequestResponseBodyMethodProcessor。 方法:org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#handleReturnValue 会处理其相关返回值。 真正的核心处理方法:org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#writeWithMessageConverters。 关键 DEBUG 记录如下图所示:

Spring Security - OAuth2 登录

1、概览 Spring Security 5 开始,引入了一个新的 OAuth2LoginConfigurer 类,可以用它来配置外部授权服务器(Authorization Server)。 本文主要带你了解 oauth2Login() 方法的一些可用配置选项。 2、Maven 依赖 在 Spring Boot 项目中,只需添加 spring-boot-starter-oauth2-client Starter 即可: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> <version>2.3.3.RELEASE</version> </dependency> 在非 Spring Boot 项目中,除了标准的 Spring 和 Spring Security 依赖外,还需要显式添加 spring-security-oauth2-client 和 spring-security-oauth2-jose 依赖: <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-oauth2-client</artifactId> <version>5.3.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-oauth2-jose</artifactId> <version>5.3.4.RELEASE</version> </dependency> 3、客户端设置 在 Spring Boot 项目中,只需为每个要配置的客户端添加几个标准属性即可。 接下来,我们要配置使用 Google 和 Facebook 作为 Authentication Provider 注册的客户端登录。 3.1、获取客户端凭证 要获取 Google OAuth2 身份认证的客户端凭证,请访问 Google API 控制台 的 “Credentials” 部分。

Spring Security 方法安全(Method Security)简介

1、概览 简而言之,Spring Security 支持方法级别的授权语义。可以通过限制哪些角色可以执行特定方法等方式来确保 Service 层的安全。 2、启用 Method Security 要使用 Spring Method Security,需要添加 spring-security-config 依赖: <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> </dependency> 可以在 Maven Central 上找到它的最新版本。 如果使用的是 Spring Boot,可以添加 spring-boot-starter-security 依赖,其中包括 spring-security-config: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> 同样,它的最新版本也可以在 Maven Central 中找到。 接下来,需要启用全局 Method Security: @Configuration @EnableGlobalMethodSecurity( prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration { } prePostEnabled 属性可启用 Spring Security Pre/Post 注解。 securedEnabled 属性决定是否启用 @Secured 注解。 jsr250Enabled 属性允许使用 @RoleAllowed 注解。 3、应用 Method Security 3.

使用 Spring ResponseEntity 处理 HTTP 响应

1、概览 本文将带你了解如何使用 ResponseEntity 设置 HTTP 响应的 Body、Status 和 Header。 2、ResponseEntity ResponseEntity 表示整个 HTTP 响应:状态码、Header 和 Body。因此,可以用它来完全配置 HTTP 响应。只需从端点返回它,Spring 就会处理接下来的所有事情。 ResponseEntity 是一个泛型类。因此,可以使用任何类型作为响应体: @GetMapping("/hello") ResponseEntity<String> hello() { return new ResponseEntity<>("Hello World!", HttpStatus.OK); } 通过编程式,可以针对不同情况返回不同的 HTTP 状态码: @GetMapping("/age") ResponseEntity<String> age( @RequestParam("yearOfBirth") int yearOfBirth) { if (isInFuture(yearOfBirth)) { return new ResponseEntity<>( "Year of birth cannot be in the future", HttpStatus.BAD_REQUEST); // 400 } return new ResponseEntity<>( "Your age is " + calculateAge(yearOfBirth), HttpStatus.OK); // 200s } 还可以设置 HTTP 响应头:

Spring Boot 日志配置

1、概览 本文将带你了解 Spring Boot 应用中的日志配置。 2、初始设置 通过 Spring Initializr 初始化一个 Spring Boot 应用: 创建唯一的类 LoggingController: @RestController public class LoggingController { Logger logger = LoggerFactory.getLogger(LoggingController.class); @RequestMapping("/") public String index() { logger.trace("A TRACE Message"); logger.debug("A DEBUG Message"); logger.info("An INFO Message"); logger.warn("A WARN Message"); logger.error("An ERROR Message"); return "Howdy! Check out the Logs to see the output..."; } } 启动应用后,只需访问 http://localhost:8080/,就能触发这些日志输出。 3、日志零配置 就日志记录而言,Spring Boot 唯一必须的依赖是 Apache Commons Logging。它是由 Spring 的 spring-jcl 模块提供的。 如果使用的是 Spring Boot Starter(几乎总是使用 Spring Boot Starter),就根本不用担心导入 spring-jcl 的问题。这是因为每个 Starter,比如 spring-boot-starter-web,都依赖于 spring-boot-starter-logging,它已经导入了 spring-jcl。

Spring 中的 @RequestParam 注解

1、概览 本文将带你了解 Spring 中 @RequestParam 注解的用法。 简单地说,可以使用 @RequestParam 从请求中提取查询参数、表单参数甚至是多个参数。 2、示例端点 假设我们有一个端点 /api/foos,它接受一个名为 id 的查询参数: @GetMapping("/api/foos") @ResponseBody public String getFoos(@RequestParam String id) { return "ID: " + id; } 在本例中,使用 @RequestParam 来提取 id 查询参数。 通过 GET 请求来调用 getFoos: http://localhost:8080/spring-mvc-basics/api/foos?id=abc ---- ID: abc 接下来,看看注解的属性:name、value、required 和 defaultValue。 3、指定请求参数名称 在上一个示例中,变量名和参数名都是相同的。 如果变量名称和参数名称不同,可以使用 name 属性配置 @RequestParam 名称: @PostMapping("/api/foos") @ResponseBody public String addFoo(@RequestParam(name = "id") String fooId, @RequestParam String name) { return "ID: " + fooId + " Name: " + name; } 也可以使用 @RequestParam(value = "id") 或直接使用 @RequestParam("id")。