0%

注解大全

Spring 注解

和 DI 相关的注解

@Autowired

可以在构造函数、Setter 方法或字段注入中使用 @Autowired 注解,Spring 会解析并注入依赖。
构造函数注入:

1
2
3
4
5
6
7
8
class Car {
Engine engine;

@Autowired
Car(Engine engine) {
this.engine = engine;
}
}

Setter 注入:

1
2
3
4
5
6
7
8
class Car {
Engine engine;

@Autowired
void setEngine(Engine engine) {
this.engine = engine;
}
}

字段注入:

1
2
3
4
class Car {
@Autowired
Engine engine;
}

@Autowired 有一个名为 required 的 boolean 参数,默认值为 true。当 Spring 找不到合适的 Bean 进行注入时,它会控制 Spring 的行为。当值为 true 时,会抛出异常,反之则不会。
注意,如果使用构造函数注入,所有构造函数参数都是 必须的 。
从 Spring 4.3 开始,除非声明了至少两个构造函数,否则不需要用 @Autowired 明确注解构造函数。

@Bean

@Bean 用于标记工厂方法,用于实例化 Spring Bean:

1
2
3
4
@Bean
Engine engine() {
return new Engine();
}

生成的 Bean 的名称与工厂方法的名称相同。如果想使用不同的名称,可以使用此注解的 name 或 value 参数(value 是 name 的别名):

1
2
3
4
@Bean("engine")
Engine getEngine() {
return new Engine();
}

注意,所有注解为 @Bean 的方法必须位于 @Configuration 类中。

@Qualifier

在模糊情况下,使用 @Qualifier 注解与 @Autowired 注解一起,提供想要使用的 Bean 的 ID 或名称。

以下两个 Bean 实现了相同的接口:

1
2
3
class Bike implements Vehicle {}

class Car implements Vehicle {}

如果 Spring 需要注入一个 Vehicle Bean,最终会出现多个匹配的定义。在这种情况下,可以使用 @Qualifier 注解显式地提供 Bean 的名称。

构造函数注入:

1
2
3
4
@Autowired
Biker(@Qualifier("bike") Vehicle vehicle) {
this.vehicle = vehicle;
}

Setter 注入:

1
2
3
4
@Autowired
void setVehicle(@Qualifier("bike") Vehicle vehicle) {
this.vehicle = vehicle;
}

或者:

1
2
3
4
5
@Autowired
@Qualifier("bike")
void setVehicle(Vehicle vehicle) {
this.vehicle = vehicle;
}

字段注入:

1
2
3
@Autowired
@Qualifier("bike")
Vehicle vehicle;

@Required

@Required 用于 Setter 方法,以标记我们希望通过 XML 填充的依赖:

1
2
3
4
5
6
7
@Required
void setColor(String color) {
this.color = color;
}
<bean class="com.baeldung.annotations.Bike">
<property name="color" value="green" />
</bean>

否则,将抛出 BeanInitializationException 异常。

@Value

可以使用 @Value 向 Bean 注入属性值。它与构造函数、Setter 和字段注入兼容。

构造函数:

1
2
3
Engine(@Value("8") int cylinderCount) {
this.cylinderCount = cylinderCount;
}

Setter 方法:

1
2
3
4
@Autowired
void setCylinderCount(@Value("8") int cylinderCount) {
this.cylinderCount = cylinderCount;
}

或者:

1
2
3
4
@Value("8")
void setCylinderCount(int cylinderCount) {
this.cylinderCount = cylinderCount;
}

字段:

1
2
@Value("8")
int cylinderCount;

注入静态值并不实用。因此,可以在 @Value 中使用占位符字符串来注入外部资源中定义的值,例如 .properties 或 .yaml 文件中的值。

假设有以下 .properties 文件:

engine.fuelType=petrol
可以用下面的方法注入 engine.fuelType 的值:

1
2
@Value("${engine.fuelType}")
String fuelType;

甚至可以在 SpEL 中使用 @Value。

@DependsOn

可以使用此注解让 Spring 在注解的 Bean 之前初始化其他 Bean。通常情况下,这种行为是自动进行的,基于 Bean 之间的显式依赖关系。

只有当依赖关系是隐式的,例如 JDBC 驱动加载或静态变量初始化时,才需要此注解。

可以在依赖类上使用 @DependsOn 来指定依赖 Bean 的名称。注解的 value 参数需要一个包含依赖 Bean 名称的数组:

1
2
@DependsOn("engine")
class Car implements Vehicle {}

或者,如果我们使用 @Bean 注解定义 Bean,那么就应该在工厂方法上使用 @DependsOn 注解:

1
2
3
4
5
@Bean
@DependsOn("fuel")
Engine engine() {
return new Engine();
}

@Lazy

当我们希望延迟初始化 Bean 时,可以使用 @Lazy 注解。默认情况下,Spring 在 Application Context 启动时会立即创建所有 Singleton(单例)Bean。

不过,在某些情况下,我们需要在使用时创建 Bean,而不是在应用启动时。

这个注解会根据具体标注位置而有不同的表现,可以用在如下位置:

@Bean 注解的 Bean 工厂方法,可以延迟方法调用(从而延迟 Bean 的创建)。
@Configuration 类,包含的 @Bean 方法都将受到影响
@Component 类,而该组件类不是 @Configuration 类,则该 Bean 将被延迟地初始化
@Autowired 构造函数、Setter 方法或字段,用于延迟地(通过代理)加载依赖本身
该注解有一个名为 value 的参数,默认值为 true。它可以覆盖默认行为。

当全局设置为懒加载(lazy)时,可以标记要立即加载的 Bean,或者在使用 @Lazy 标记的 @Configuration 类中,配置特定的 @Bean 方法以进行立即加载:

1
2
3
4
5
6
7
8
9
10
@Configuration
@Lazy
class VehicleFactoryConfig {

@Bean
@Lazy(false) // 立即加载
Engine engine() {
return new Engine();
}
}

@Lookup

使用 @Lookup 注解的方法会告诉 Spring,在调用该方法时返回该方法返回类型的实例。

@Primary

有时,需要定义多个相同类型的 Bean。在这种情况下,注入将不会成功,因为 Spring 不知道需要注入哪个 Bean。

上文已经介绍了处理这种情况的方法:用 @Qualifier 标记所有注入点,并指定所需的 Bean 名称。

可以使用 @Primary 注解来简化这种情况:将最常用的 Bean 标记为 @Primary,它将在未标记 @Qualifier 的注入点上被选中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
@Primary
class Car implements Vehicle {}

@Component
class Bike implements Vehicle {}

@Component
class Driver {
@Autowired
Vehicle vehicle;
}

@Component
class Biker {
@Autowired
@Qualifier("bike")
Vehicle vehicle;
}

在前面的示例中,Car 注解了 @Primary。因此,在 Driver 类中,Spring 注入了一个 Car Bean。当然,在 Biker Bean 中,vehicle 字段的值将是 Bike 对象,因为它注解了 @Qualifier 并且指定了 Bean 名称。

@Scope

使用 @Scope 来定义 @Component 类或 @Bean 的 Scope(作用域),它可以是 singleton、prototype、request、session、globalSession 或某些自定义作用域。

例如:

1
2
3
@Component
@Scope("prototype")
class Engine {}

Context 配置注解

@Profile

如果我们希望 Spring 仅在特定 Profile(配置文件)处于活动状态时才启用 @Component 类或 @Bean 方法,可以使用 @Profile 对其进行标记。
使用注解的 value 参数配置 Profile 的名称:

1
2
3
@Component
@Profile("sportDay")
class Bike implements Vehicle {}

@Import

可以使用该注解来使用特定的 @Configuration 类,而无需进行组件扫描。
通过 @Import 注解的 value 参数来提供这些类:

1
2
@Import(VehiclePartSupplier.class)
class VehicleFactoryConfig {}

@ImportResource

可以使用该注解导入 XML 配置。用 locations 参数或其别名 value 参数指定 XML 文件的位置:

1
2
3
@Configuration
@ImportResource("classpath:/annotations.xml")
class VehicleFactoryConfig {}

@PropertySource

通过该注解,可以为应用设置定义属性文件:

1
2
3
4
5
6
7
8
9
@Configuration
@PropertySource("classpath:/annotations.properties")
class VehicleFactoryConfig {}
@PropertySource 利用了 Java 8 的重复注解功能,这意味着可以用它多次标记一个类:

@Configuration
@PropertySource("classpath:/annotations.properties")
@PropertySource("classpath:/vehicle-factory.properties")
class VehicleFactoryConfig {}

@PropertySources

可以使用此注解指定多个 @PropertySource 配置:

1
2
3
4
5
6
@Configuration
@PropertySources({
@PropertySource("classpath:/annotations.properties"),
@PropertySource("classpath:/vehicle-factory.properties")
})
class VehicleFactoryConfig {}

注意,自 Java 8 以来,可以通过上述重复注解的特性来实现同样的功能。

Spring MVC 注解

@Controller

@Controller 注解用于标识一个类为Spring MVC控制器,负责处理HTTP请求并返回视图。

1
2
3
4
@Controller
public class MyController {
// 控制器方法定义...
}

@RestController

@RestController 注解是@Controller和@ResponseBody的组合,用于RESTful Web服务的控制器。

1
2
3
4
@RestController
public class MyRestController {
// 控制器中的方法将直接返回响应体。
}

@RequestMapping

@RequestMapping 注解用于将HTTP请求映射到控制器的处理方法上。
注解属性介绍:

  • value: 请求的URL路径。
  • method: 支持的HTTP方法。
  • params: 请求参数条件。
  • headers: 请求头条件。

当value设置为”/users/{id}”,method为RequestMethod.GET,params为”active=true”,headers为”Accept=application/json”时:

1
2
3
4
5
6
7
GET /users/123?active=true
Accept: application/json

@RequestMapping(value = "/users/{id}", method = RequestMethod.GET, params = "active=true", headers = "Accept=application/json")
public ResponseEntity<List<User>> listActiveUsers(@PathVariable int id) {
// 根据用户ID获取激活用户的列表。
}

@GetMapping

@GetMapping 注解用于映射GET请求到控制器的方法上。
注解属性介绍:

  • value: 请求的URL路径。

当value设置为”/users/{id}”时,请求/users/123将映射到方法:

1
2
3
4
5
6
GET /users/123

@GetMapping("/users/{id}")
public User getUser(@PathVariable int id) {
// 根据路径变量{id}获取用户信息。
}

@PostMapping

@PostMapping 注解用于映射POST请求到控制器的方法上。
注解属性介绍:

  • value: 请求的URL路径。
    当value设置为”/users”时,带有JSON请求体的POST请求将映射到方法:
1
2
3
4
5
6
7
8
9
10
11
POST /users
Content-Type: application/json
{
"name": "John Doe",
"age": 30
}

@PostMapping("/users")
public User createUser(@RequestBody User user) {
// 使用JSON请求体中的数据创建新用户。
}

@PutMapping

@PutMapping 注解用于映射HTTP PUT请求到控制器的方法上,通常用于更新资源。

注解属性介绍

  • value: 请求的URL路径。
    当value设置为”/users/{id}”时,请求将映射到更新用户的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13


PUT /users/123
Content-Type: application/json

{
"name": "Jane Doe",
"age": 25
}
@PutMapping("/users/{id}")
public User updateUser(@PathVariable int id, @RequestBody User userDetails) {
// 使用请求体中的数据更新指定ID的用户信息。
}

@DeleteMapping

@DeleteMapping 注解用于映射HTTP DELETE请求到控制器的方法上,用于删除资源。
注解属性介绍

  • value: 请求的URL路径。

当value设置为”/users/{id}”时,请求将映射到删除用户的方法:

1
2
3
4
5
6
7
8
9



DELETE /users/123

@DeleteMapping("/users/{id}")
public ResponseEntity<?> deleteUser(@PathVariable int id) {
// 根据用户ID删除用户。
}

@PatchMapping

@PatchMapping 注解用于映射HTTP PATCH请求到控制器的方法上,通常用于部分更新资源。

注解属性介绍

  • value: 请求的URL路径。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


当value设置为"/users/{id}"时,请求将映射到部分更新用户的方法:

PATCH /users/123
Content-Type: application/json

{
"name": "Jane Doe"
}

@PatchMapping("/users/{id}")
public User patchUser(@PathVariable int id, @RequestBody Map<String, Object> updates) {
// 使用请求体中的部分数据更新指定ID的用户信息。
}

@RequestParam

@RequestParam 注解用于将请求参数绑定到控制器方法的参数上。
注解属性介绍

  • value: 请求参数的名称。
  • required: 指示参数是否必须。
  • defaultValue: 参数的默认值。

当请求包含查询参数”page”和”size”时:

1
2
3
4
5
6
7
8
9
10

GET /items?page=1&size=10

@GetMapping("/items")
public List<Item> listItems(
@RequestParam(value = "page", defaultValue = "1") int page,
@RequestParam(value = "size", defaultValue = "10") int size
) {
// 根据请求的页码和大小返回项目列表。
}

@PathVariable

@PathVariable 注解用于将URL中的模板变量绑定到控制器方法的参数上。

注解属性介绍

  • value: 模板变量的名称。
  • required: 指示变量是否必须。

当URL模板包含”{id}”时,请求/users/123将映射到方法:

1
2
3
4
5
6
GET /users/123

@GetMapping("/users/{id}")
public User getUserById(@PathVariable int id) {
// 根据URL路径变量{id}获取用户信息。
}

@RequestBody

@RequestBody 注解用于将请求体绑定到控制器方法的参数上。

1
2
3
4
5
6
7
8
9
10
11
12

POST /users
Content-Type: application/json
{
"name": "John Doe",
"age": 30
}

@PostMapping("/users")
public User createUser(@RequestBody User user) {
// 使用请求体中的数据创建新用户。
}

@RequestHeader

@RequestHeader 注解用于将请求头绑定到控制器方法的参数上。

注解属性介绍

  • value: 请求头的名称。
  • required: 指示请求头是否必须。

请求包含自定义请求头”X-Request-ID”:

1
2
3
4
5
6
7
8
9

GET /users
X-Request-ID: 123456789
注解业务案例

@GetMapping("/users")
public List<User> listUsers(@RequestHeader(value = "X-Request-ID", required = false) String requestId) {
// 根据请求头"X-Request-ID"记录日志或进行追踪。
}

@CookieValue

@CookieValue 注解用于将请求中Cookie的值绑定到控制器方法的参数上。

注解属性介绍

  • value: Cookie的名称。
  • required: 指示Cookie是否必须。

请求包含名为”sessionId”的Cookie:

1
2
3
4
5
6
7
8
9

GET /users
Cookie: sessionId=abc123def456
注解业务案例

@GetMapping("/users")
public List<User> listUsers(@CookieValue(value = "sessionId", required = false) String sessionId) {
// 根据Cookie中的"sessionId"进行会话管理。
}

@ModelAttribute

@ModelAttribute 注解用于将请求参数或表单数据绑定到模型属性上。
注解属性介绍

  • value: 模型属性的名称。
  • required: 指示模型属性是否必须。

一个包含用户信息的POST请求:

1
2
3
4
5
6
7
8
9
10
11
12

POST /users
Content-Type: application/x-www-form-urlencoded

name=John+Doe&age=30
注解业务案例

@PostMapping("/users")
public String addUser(Model model, @ModelAttribute User user) {
model.addAttribute("user", user);
// 将用户信息添加到模型中,以便在视图中展示。
}

@SessionAttribute

@SessionAttribute 注解用于将模型属性存储到HTTP会话中。
注解属性介绍

  • name: 会话属性的名称。
  • value: 会话属性的值。

将用户信息存储在会话中,以便跨请求访问:

1
2
3
4
5
6
7
8
9
10

GET /user/profile
Cookie: JSESSIONID=abc123; user=John Doe
注解业务案例

@SessionAttribute(name = "user")
public String showProfile(Model model) {
String user = (String) model.asMap().get("user");
// 从会话中获取用户信息。
}

@RequestAttribute

@RequestAttribute 注解用于将请求属性绑定到控制器方法的参数上。
注解属性介绍

  • value: 请求属性的名称。

从请求转发中携带属性:

1
2
3
4
5
6
7
8
9
10
11

POST /login
...

Redirect /user/profile?role=admin
注解业务案例

@GetMapping("/user/profile")
public String showProfile(@RequestAttribute("role") String role) {
// 根据请求属性"role"展示不同的用户信息。
}

@ResponseStatus

@ResponseStatus 注解用于指定HTTP响应的status。
注解属性介绍

  • value: 指定的HTTP状态。

当资源未找到时返回404状态:

1
2
3
4
5
6
7
8
9
10
11
12
13

GET /users/999
HTTP/1.1 404 Not Found
注解业务案例

@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ResponseEntity<Object> handleResourceNotFound() {
// 处理资源未找到的异常。
}
}

@ExceptionHandler

@ExceptionHandler 注解用于处理控制器中抛出的特定异常。
注解属性介绍

  • value: 指定异常的类型。

捕获并处理自定义的业务异常:

1
2
3
4
5
6
7
8
9
10
11
12
13

POST /users
...
HTTP/1.1 400 Bad Request
注解业务案例

@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<Object> handleBusinessException(BusinessException ex) {
// 处理业务异常。
}
}

@CrossOrigin

@CrossOrigin 注解用于启用跨源请求。

注解属性介绍

  • origins: 允许的来源。
  • methods: 允许的HTTP方法。
  • allowedHeaders: 允许的请求头。

允许特定域名进行跨域GET请求:

1
2
3
4
5
6
7
8
9
10
11

OPTIONS /api/data
Access-Control-Request-Method: GET
Origin: <http://example.com>
注解业务案例

@CrossOrigin(origins = "<http://example.com>", methods = {RequestMethod.GET})
@GetMapping("/api/data")
public List<Data> getAllData() {
// 返回数据列表,允许跨域GET请求。
}

@MatrixVariable

@MatrixVariable 注解用于从URL的矩阵变量中获取值。

注解属性介绍

  • pathVar: 指定矩阵变量所在的路径变量名称。

从URL中获取矩阵变量的值:

1
2
3
4
5
6
7
8

GET /api/users/123;role=admin
注解业务案例

@GetMapping("/api/users/{id}")
public User getUser(@MatrixVariable(name = "role", pathVar = "id") String role) {
// 根据矩阵变量"role"获取用户角色。
}

@InitBinder

@InitBinder 注解用于在控制器中初始化WebDataBinder,通常用于注册自定义属性编辑器。

1
2
3
4
5
6
7
8

@Controller
public class MyController {
@InitBinder
public void initBinder(WebDataBinder binder) {
// 注册自定义属性编辑器。
}
}

@ResponseStatus

@ResponseStatus 注解用于设置响应的HTTP状态码。
注解属性介绍

  • value: 指定的HTTP状态码。

当发生特定异常时,返回HTTP 503 Service Unavailable状态码。

1
2
3
4
5

@ResponseStatus(value = HttpStatus.SERVICE_UNAVAILABLE)
public class ServiceUnavailableException extends RuntimeException {
// 自定义异常类。
}

@Validated

@Validated 注解用于在类或方法级别指定验证组,增强验证逻辑。
注解属性介绍

  • groups: 指定验证组。

使用特定的验证组对用户注册信息进行验证。

1
2
3
4
5

@Validated(UserValidationGroup.class)
public class UserRegistrationController {
// 该控制器中的方法将使用UserValidationGroup验证组。
}

@Validated Annotation with Groups

@Validated 注解与groups属性结合使用,允许在方法级别指定多个验证组。
注解属性介绍

  • value: 指定的验证组数组。

对更新操作使用不同的验证组。

1
2
3
4
5

@Validated({UpdateGroup.class, AnotherUpdateGroup.class})
public void updateEntity(@Valid @NotNull MyEntity entity) {
// 使用多个验证组对实体进行验证。
}

@RequestBodyAdvice

@RequestBodyAdvice 注解用于在读取请求正文之前或之后对请求正文进行处理。
注解属性介绍

  • beforeBodyRead: 指定在读取请求正文之前的处理。
  • afterBodyRead: 指定在读取请求正文之后的处理。

对请求正文进行日志记录或修改。

1
2
3
4
5
6
7
8

@ControllerAdvice
public class MyRequestBodyAdvice {
@RequestBodyAdvice(beforeBodyRead = "logBeforeRead")
public void logRequestBody() {
// 在读取请求正文之前进行日志记录。
}
}

@ControllerAdvice

@ControllerAdvice 注解用于定义一个类,该类可以包含多个@ExceptionHandler、@InitBinder和@ModelAttribute方法。
注解属性介绍

  • basePackages: 指定要扫描的包。
1
2
3
4
5

@ControllerAdvice
public class MyGlobalControllerAdvice {
// 包含全局异常处理、属性初始化和模型属性的方法。
}

@MatrixParam

@MatrixParam 注解用于从URL的矩阵变量中获取值。
注解属性介绍

  • name: 矩阵变量的名称。

从URL中获取矩阵变量的值:

1
2
3
4
5
6
7
8

GET /users;role=admin/123
注解业务案例

@GetMapping("/users/{id}")
public User getUser(@MatrixParam(name = "role") String role) {
// 根据矩阵变量"role"获取用户角色。
}

@SessionAttributes

@SessionAttributes 注解用于将模型属性添加到HTTP会话中,以便在多个请求之间保持它们的值。

注解属性介绍

  • names: 要存储在会话中的属性名称数组。
  • types: 要存储在会话中的属性类型。

将用户信息存储在会话中,以便在用户会话期间保持状态:

1
2
3
4
5
6
7
8
9
10

GET /user/profile
Cookie: JSESSIONID=abc123; user=John Doe
注解业务案例

@Controller
@SessionAttributes("user")
public class UserController {
// 这个控制器中的模型属性"user"将被存储在会话中。
}

@RequestAttribute

@RequestAttribute 注解用于将请求属性绑定到控制器方法的参数上。
注解属性介绍

  • value: 请求属性的名称。

从请求中获取属性”searchQuery”:

1
2
3
4
5
6
7
8

GET /search?searchQuery=spring
注解业务案例

@GetMapping("/search")
public String search(@RequestAttribute("searchQuery") String query) {
// 使用请求属性"searchQuery"执行搜索。
}

@ModelAttribute

@ModelAttribute 注解用于将请求参数或会话属性绑定到模型对象上。
注解属性介绍

  • value: 模型对象的名称。

将表单提交的参数绑定到用户对象上:

1
2
3
4
5
6
7
8
9
10
11
12

POST /user/register
Content-Type: application/x-www-form-urlencoded

name=John+Doe&email=<john.doe@example.com>
注解业务案例

@PostMapping("/user/register")
public String registerUser(@ModelAttribute("user") User user) {
// 使用模型中的"user"对象注册新用户。
}

@CookieValue

@CookieValue 注解用于将请求中的Cookie值绑定到控制器方法的参数上。
注解属性介绍

  • value: Cookie的名称。

从请求中获取名为”sessionId”的Cookie值:

1
2
3
4
5
6
7
8
9

GET /user/profile
Cookie: sessionId=abc123
注解业务案例

@GetMapping("/user/profile")
public String showProfile(@CookieValue("sessionId") String sessionId) {
// 使用Cookie中的"sessionId"进行会话管理。
}

@RequestBody

@RequestBody 注解用于将请求正文绑定到控制器方法的参数上。

接收JSON格式的请求正文:

1
2
3
4
5
6
7
8
9
10
11
12
13

POST /api/data
Content-Type: application/json

{
"key": "value"
}
注解业务案例

@PostMapping("/api/data")
public DataItem createDataItem(@RequestBody DataItem data) {
// 使用请求正文中的数据创建新的数据项。
}

@ResponseBody

@ResponseBody 注解用于指示方法的返回值直接作为HTTP响应正文返回。

返回JSON格式的响应正文:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

GET /api/data/1
Content-Type: application/json

{
"id": 1,
"key": "value"
}
注解业务案例

@GetMapping("/api/data/{id}")
@ResponseBody
public DataItem getDataItem(@PathVariable int id) {
// 直接将数据项作为JSON响应正文返回。
}

@ResponseStatus

@ResponseStatus 注解用于设置响应的HTTP状态码。
注解属性介绍

  • value: 指定的HTTP状态码。
1
2
3
4
5
6

@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
// 自定义异常,用于当资源未找到时。
}

@ExceptionHandler

@ExceptionHandler 注解用于处理控制器抛出的特定异常。
注解属性介绍

  • value: 指定异常的类型。
1
2
3
4
5
6
7
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<String> handleResourceNotFound() {
// 处理资源未找到的异常。
}
}

@ControllerAdvice

@ControllerAdvice 注解用于定义一个类,该类可以包含多个@ExceptionHandler、@InitBinder和@ModelAttribute方法,用于全局异常处理和数据绑定。
注解属性介绍

  • basePackages: 指定要扫描的包。

注解业务案例

1
2
3
4
5

@ControllerAdvice
public class GlobalControllerAdvice {
// 包含全局异常处理、属性初始化和模型属性的方法。
}

@PostMapping with consumes Attribute

@PostMapping 注解与 consumes 属性结合使用,用于指定请求体的媒体类型。

注解属性介绍

  • consumes: 指定请求体的媒体类型,如 application/json。

发送POST请求创建新用户,请求体为JSON格式:

1
2
3
4
5
6
7
8
9
10
11
12
13

POST /users
Content-Type: application/json

{
"name": "John Doe",
"email": "<john.doe@example.com>"
}

@PostMapping(value = "/users", consumes = "application/json")
public ResponseEntity<User> createUser(@RequestBody User user) {
// 使用JSON请求体中的数据创建新用户。
}

@GetMapping with produces Attribute

@GetMapping 注解与 produces 属性结合使用,用于指定响应的媒体类型。
注解属性介绍

  • produces: 指定响应的媒体类型,如 application/json。

发送GET请求获取用户列表,期望响应为JSON格式:

1
2
3
4
5
6
7
8

GET /users
Accept: application/json

@GetMapping(value = "/users", produces = "application/json")
public ResponseEntity<List<User>> getUsers() {
// 以JSON格式返回用户列表。
}

@ExceptionHandler with responseBody Attribute

@ExceptionHandler 注解与 responseBody 属性结合使用,用于返回异常信息。
注解属性介绍

  • responseBody: 指定异常处理方法的返回值是否作为响应正文。

处理资源未找到的异常,并返回JSON格式的错误信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

GET /users/999
HTTP/1.1 404 Not Found
Content-Type: application/json

{"timestamp": "2023-10-10T12:00:00", "status": 404, "error": "Not Found"}
注解业务案例

@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseBody
public ResponseEntity<Object> handleResourceNotFound() {
// 返回JSON格式的错误信息。
}
}

@ControllerAdvice with basePackages

@ControllerAdvice 注解与 basePackages 属性结合使用,用于定义全局异常处理和数据绑定的类。
注解属性介绍

  • basePackages: 指定要扫描的包,这些包中的控制器将使用 @ControllerAdvice 类中定义的方法。
1
2
3
4
5

@ControllerAdvice(basePackages = "com.example.web")
public class GlobalControllerAdvice {
// 包含全局异常处理、属性初始化和模型属性的方法。
}

@SessionAttribute with name

@SessionAttribute 注解与 name 属性结合使用,用于从HTTP会话中获取属性。
注解属性介绍

  • name: 指定会话属性的名称。

从会话中获取名为”user”的属性:

1
2
3
4
5
6
7
8
9

GET /user/profile
Cookie: JSESSIONID=abc123; user=John Doe
注解业务案例

@GetMapping("/user/profile")
public String showProfile(@SessionAttribute("user") String user) {
// 使用会话中的"user"属性展示用户信息。
}

@RequestHeader

@RequestHeader 注解用于将特定的HTTP请求头值绑定到控制器方法的参数上。
注解属性介绍

  • value: 请求头的名称。
  • defaultValue: 当请求头不存在时使用的默认值。
1
2
3
4
5
6
7
8
9
10
11

获取User-Agent请求头的值:

GET /api/resource
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
注解业务案例

@GetMapping("/api/resource")
public ResponseEntity<?> handleRequest(@RequestHeader("User-Agent") String userAgent) {
// 根据User-Agent处理请求。
}

@Component

@Component 是Spring的通用注解,用于标识一个类为Spring组件,可以被自动扫描和注册为Spring应用上下文的Bean。
注解属性介绍

  • value: 指定组件的名称。
    注解业务案例
1
2
3
4
5

@Component("myComponent")
public class MyComponent {
// 这个类会被Spring自动注册为一个Bean。
}

@Service

@Service 是Spring的注解,用于标识一个类为服务层组件,通常包含业务逻辑。
注解业务案例

1
2
3
4
5

@Service
public class MyService {
// 服务层组件,包含业务逻辑。
}

@Repository

@Repository 是Spring Data的注解,用于标识一个类为数据访问对象,通常用于数据库访问。

注解业务案例

1
2
3
4
5

@Repository
public interface MyRepository extends JpaRepository<User, Long> {
// 定义数据访问方法。
}

@Profile

@Profile 注解用于根据激活的配置文件激活或禁用Bean。
注解属性介绍

  • value: 指定配置文件的名称。

注解业务案例

1
2
3
4
5
6

@Component
@Profile("dev")
public class DevConfig {
// 仅在"dev"配置文件激活时使用。
}

@RequestPart

@RequestPart 注解用于处理multipart/form-data请求中的部分,如文件上传。
注解属性介绍

  • value: 请求部分的名称。

处理包含文件的multipart请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

POST /api/upload
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="image.png"
Content-Type: image/png

[文件内容]

------WebKitFormBoundary--
注解业务案例

@PostMapping("/api/upload")
public ResponseEntity<String> handleFileUpload(
@RequestPart("file") MultipartFile file
) {
// 处理上传的文件。
}

Spring Boot 注解

@ConfigurationProperties

@ConfigurationProperties 是 Spring Boot 提供的一个注解,主要用于将配置文件(如 application.properties 或 application.yml)中的属性值绑定到 Java 类的字段上。通过该注解,可以将一组相关的配置属性封装到一个 Java 类中,方便管理和使用。

1
2
3
4
5
6
7

@Component
@ConfigurationProperties(prefix = "myapp")
public class MyAppProperties {
private String name;
private int port;
}
1
2
myapp.name=My Application
myapp.port=8080

@EnableConfigurationProperties

@EnableConfigurationProperties 用于启用 @ConfigurationProperties 注解的类。当使用 @ConfigurationProperties 注解标记一个类后,需要使用 @EnableConfigurationProperties 注解来告知 Spring Boot 去扫描并绑定这些配置属性类。

1
2
3
4
5
@Configuration
@EnableConfigurationProperties(MyAppProperties.class)
public class AppConfig {
// 配置类可以包含其他配置信息
}

@ConditionalOnProperty

@ConditionalOnProperty 是一个条件注解,用于根据配置文件中的属性值来决定是否加载某个 Bean 或配置类。只有当配置文件中指定的属性满足特定条件时,被该注解标记的 Bean 或配置类才会被创建和加载。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class ConditionalConfig {

@Bean
@ConditionalOnProperty(name = "myapp.feature.enabled", havingValue = "true")
public MyFeature myFeature() {
return new MyFeature();
}
}

class MyFeature {
// 自定义功能类
}

Spring Cloud 注解

Jackson

序列化注解

@JsonAnyGetter

该注解用于把可变的Map类型属性当做标准属性。
下例中,ExtendableBean实体有一个name属性和一组kay/value格式的可扩展属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ExtendableBean {
public String name;
public Map<String, String> properties;
@JsonAnyGetter
public Map<String, String> getProperties() {
return properties;
}
public ExtendableBean(String name) {
this.name = name;
properties = new HashMap<>();
}
public void add(String key, String value){
properties.put(key, value);
}
}

说明: name属性访问级别是public, 是为了省略get/set方法, 简化示例
下面是把ExtendableBean实体序列化的过程:

1
2
3
4
5
6
7
8
9
10
11
12
13

private static void whenSerializingUsingJsonAnyGetter_thenCorrect(){
ExtendableBean bean = new ExtendableBean("My bean");
bean.add("attr1", "val1");
bean.add("attr2", "val2");
String result = null;
try {
result = new ObjectMapper().writeValueAsString(bean);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
System.out.println(result);
}

序列化后的结果: {“name”:”My bean”,”attr2”:”val2”,”attr1”:”val1”}

@JsonGetter

该注解是@JsonProperty的两个作用中的一个, 用来标记一个方法是getter方法
下例中, 指定方法getTheName()是属性name属性的getter方法

1
2
3
4
5
6
7
8
9
10

public class MyBean {
public int id;
private String name;

@JsonGetter("name")
public String getTheName() {
return name;
}
}

下面是序列化过程:

1
2
3
4
5
6

public void whenSerializingUsingJsonGetter_thenCorrect()
throws JsonProcessingException {
MyBean bean = new MyBean(1, "My bean");
String result = new ObjectMapper().writeValueAsString(bean);
}

@JsonPropertyOrder

该注解可以指定实体属性序列化后的顺序

1
2
3
4
5
@JsonPropertyOrder({ "name", "id" })
public class MyBean {
public int id;
public String name;
}

序列化后的结果:{ “name”:”My bean”, “id”:1}
该注解有一个参数alphabetic, 如果为true, 表示按字母顺序序列化,此时输出结果:{ “id”:1, “name”:”My bean”}

@JsonRawValue

该注解可以让Jackson在序列化时把属性的值原样输出
下面的例子中, 我们给实体属性attrs赋值一个json字符串

1
2
3
4
5
6
7
8
9
10
11

public class RawBean {
public String name;
@JsonRawValue
public String attrs;
}
public void whenSerializingUsingJsonRawValue_thenCorrect()
throws JsonProcessingException {
RawBean bean = new RawBean("My bean", "{\"attr\":false}");
String result = new ObjectMapper().writeValueAsString(bean);
}

输出结果是: {“name”:”Mybean”,”attrs”:{“attr”:false}}

@JsonValue

该注解作用于一个方法, 并且只用被注解的方法序列化整个实体对象
把1.1的实体例修改如下:

1
2
3
4
5
6
7
8
9
10

class ExtendableBean {
...........
//把注解换成JsonValue
@JsonValue
public Map<String, String> getProperties() {
return properties;
}
..........
}

序列化过程不变, 则结果是: {“attr2”:”val2”,”attr1”:”val1”}
可见, 属性name没有被序列化

@JsonRootName

如果wrapping是使能(enabled), 那么该注解用来指定root wrapper的名称
wrapping(包装)的含义是如果序列化实体User的结果是

1
2
3
4
5

{
"id": 1,
"name": "John"
}

那么wrapping后的效果如下:

1
2
3
4
5
6
7

{
"User": {
"id": 1,
"name": "John"
}
}

下面看一个例子, 我们用该注解指明包装实体(wrapper entity)的包装器名称:

1
2
3
4
5
6

@JsonRootName(value = "user")
public class UserWithRoot {
public int id;
public String name;
}

包装器默认名称是实体类名, 这里就是UserWithRoot, 但是注解的value属性把包装器名称改为了user
序列化过程(和前面不同, 需要使能包装器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14

private static void whenSerializingUsingJsonRootName_thenCorrect(){
UserWithRoot user = new UserWithRoot();
user.id = 1;
user.name = "jackma";
try {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
String result = objectMapper.writeValueAsString(user);
System.out.println(result);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}

序列化的结果:{“user”:{“id”:1,”name”:”jackma”}}
从Jackson .2.4版本开始, 新增了一个可选参数namespace, 该属性对json没效果, 但是对xml起作用, 修改本例的实体例:

1
2
3
4
5
6

@JsonRootName(value = "user", namespace = "alibaba")
class UserWithRoot {
public int id;
public String name;
}

用XmlMapper序列化:

1
2
3
4
5
6

private static void whenSerializingUsingJsonRootName_thenCorrect(){
..............
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
..............

序列化结果:

1
2
3
4
<user xmlns="alibaba">
<id xmlns="">1</id>
<name xmlns="">jackma</name>
</user>

@JsonSerialize

该注解用于指定一个自定义序列化器(custom serializer)来序列化实体例的某属性
下例中, 用@JsonSerialize的参数using指明实体类属性eventDate的序列化器是CustomDateSerializer类:

1
2
3
4
5
6
7

public class Event {
public String name;

@JsonSerialize(using = CustomDateSerializer.class)
public Date eventDate;
}

下面是类CustomDateSerializer的定义:

1
2
3
4
5
6
7
8
9
10
11

public class CustomDateSerializer extends StdSerializer<Date> {
private static SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
public CustomDateSerializer() { this(null); }
public CustomDateSerializer(Class<Date> t) { super(t); }
@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider arg2)
throws IOException, JsonProcessingException {
gen.writeString(formatter.format(value));
}
}

序列化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

public void whenSerializingUsingJsonSerialize_thenCorrect(){
SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/DD hh:mm:ss");
String toParse = "2019/08/19 16:28:00";
Date date = null;
try {
date = df.parse(toParse);
} catch (ParseException e) {
e.printStackTrace();
}
Event event = new Event();
event.name = "party";
event.eventDate = date;
try {
String result = new ObjectMapper().writeValueAsString(event);
System.out.println(result);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}

序列化结果: {“name”:”party”,”eventDate”:”2019-08-19 04:28:00”}
而如果没有@JsonSerialize注解的序列化结果是: {“name”:”party”,”eventDate”:1566203280000}

反序列化注解

@JsonCreator

该注解可以调整反序列化时构造器/构造工厂的行为
当我们需要反序列化的Json字符串和目标实体类不完全匹配时, 这个注解非常有用
假设我们要反序列化下面的Json字符串:

1
2
3
4
5

{
"id":1,
"theName":"My bean"
}

但是, 我们的目标实体类并没有一个名为theName的属性. 现在, 我们不想改变实体类本身, 我们只需在数据导出时做一些控制, 方法就是在构造器中使用@JsonCreator和@JsonProperty注解:

1
2
3
4
5
6
7
8
9
10
11
12
13

public class BeanWithCreator {
public int id;
public String name;

@JsonCreator
public BeanWithCreator(
@JsonProperty("id") int id,
@JsonProperty("theName") String name) {
this.id = id;
this.name = name;
}
}

反序列化过程:

1
2
3
4
5
6

public void whenDeserializingUsingJsonCreator_thenCorrect() throws IOException {
String json = "{\"id\":1,\"theName\":\"My bean\"}";
BeanWithCreator bean = new ObjectMapper().readerFor(BeanWithCreator.class).readValue(json);
assertEquals("My bean", bean.name);
}

@JacksonInject

该注解指明一个属性的值是通过注入得到而不是从Json字符串反序列得到
下例的实体例属性id的值用注解标明是注入值:

1
2
3
4
5
6
7

public class BeanWithInject {
@JacksonInject
public int id;

public String name;
}

反序列化过程:

1
2
3
4
5
6
7
8

public void whenDeserializingUsingJsonInject_thenCorrect() throws IOException {
String json = "{\"name\":\"My bean\"}";
InjectableValues inject = new InjectableValues.Std().addValue(int.class, 1);
BeanWithInject bean = new ObjectMapper().reader(inject).forType(BeanWithInject.class).readValue(json);
assertEquals("My bean", bean.name);
assertEquals(1, bean.id);
}

@JsonAnySetter

该注解允许我们把一个可变的map属性作为标准属性, 在反序列过程中, 从Json字符串得到的属性值会加入到map属性中
实体例和注解:

1
2
3
4
5
6
7
8
9
10

public class ExtendableBean {
public String name;
private Map<String, String> properties;

@JsonAnySetter
public void add(String key, String value) {
properties.put(key, value);
}
}

准备反序列化的Json字符串:

1
2
3
4
5
6

{
"name":"My bean",
"attr2":"val2",
"attr1":"val1"
}

反序列化过程:

1
2
3
4
5
6
7

public void whenDeserializingUsingJsonAnySetter_thenCorrect() throws IOException {
String json = "{\"name\":\"My bean\",\"attr2\":\"val2\",\"attr1\":\"val1\"}";
ExtendableBean bean = new ObjectMapper().readerFor(ExtendableBean.class).readValue(json);
assertEquals("My bean", bean.name);
assertEquals("val2", bean.getProperties().get("attr2"));
}

@JsonSetter

该注解是@JsonProperty的另一个作用, 和@JsonGetter相对, 标记一个方法是setter方法
如果目标实体类没有和Json字符串数据完全匹配的方法时, 我们可以通过这个注解做一些调整让他们匹配
下例中指定方法setTheName()作为name属性的setter方法

1
2
3
4
5
6
7
8
9
10

public class MyBean {
public int id;
private String name;

@JsonSetter("name")
public void setTheName(String name) {
this.name = name;
}
}

反序列化过程:

1
2
3
4
5
6

public void whenDeserializingUsingJsonSetter_thenCorrect() throws IOException {
String json = "{\"id\":1,\"name\":\"My bean\"}";
MyBean bean = new ObjectMapper().readerFor(MyBean.class).readValue(json);
assertEquals("My bean", bean.getTheName());
}

@JsonDeserialize

该注解标明使用自定义反序列化器(custom deserializer)
实体类:

1
2
3
4
5
6
7

public class Event {
public String name;

@JsonDeserialize(using = CustomDateDeserializer.class)
public Date eventDate;
}

自定义反序列化器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

public class CustomDateDeserializer extends StdDeserializer<Date> {
private static SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
public CustomDateDeserializer() {
this(null);
}
public CustomDateDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Date deserialize(JsonParser jsonparser, DeserializationContext context) throws IOException {
String date = jsonparser.getText();
try {
return formatter.parse(date);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}

反序列过程:

1
2
3
4
5
6
7

public void whenDeserializingUsingJsonDeserialize_thenCorrect() throws IOException {
String json = "{"name":"party","eventDate":"20-12-2014 02:30:00"}";
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
Event event = new ObjectMapper().readerFor(Event.class).readValue(json);
assertEquals("20-12-2014 02:30:00", df.format(event.eventDate));
}

@JsonAlias

该注解在反序列化过程中为属性定义一个或多个别名
实体类:

1
2
3
4
5
6

public class AliasBean {
@JsonAlias({ "fName", "f_name" })
private String firstName;
private String lastName;
}

Json字符串中fName, f_name或firstName的值都可以被反序列到属性firstName

1
2
3
4
5
6

public void whenDeserializingUsingJsonAlias_thenCorrect() throws IOException {
String json = "{\"fName\": \"John\", \"lastName\": \"Green\"}";
AliasBean aliasBean = new ObjectMapper().readerFor(AliasBean.class).readValue(json);
assertEquals("John", aliasBean.getFirstName());
}

属性包含注解

@JsonIgnoreProperties

该注解是一个类级别的注解, 标记一个或多个属性被Jackson忽略
实体类:

1
2
3
4
5
6

@JsonIgnoreProperties({ "id" })
public class BeanWithIgnore {
public int id;
public String name;
}

序列化过程:

1
2
3
4
5
6
7

public void whenSerializingUsingJsonIgnoreProperties_thenCorrect() throws JsonProcessingException {
BeanWithIgnore bean = new BeanWithIgnore(1, "My bean");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, not(containsString("id")));
}

参数ignoreUnknown为true时, Json字符串如果有未知的属性名, 则不会抛出异常

@JsonIgnore

该注解用于属性级别, 用于标明一个属性可以被Jackson忽略
实体类:

1
2
3
4
5
6
7

public class BeanWithIgnore {
@JsonIgnore
public int id;

public String name;
}

序列化过程:

1
2
3
4
5
6
7
8
9
10
11
12

public void whenSerializingUsingJsonIgnore_thenCorrect()
throws JsonProcessingException {

BeanWithIgnore bean = new BeanWithIgnore(1, "My bean");

String result = new ObjectMapper()
.writeValueAsString(bean);

assertThat(result, containsString("My bean"));
assertThat(result, not(containsString("id")));
}

@JsonIgnoreType

该注解标记类型是注解作用的类型的属性都会被忽略
必须作用于类, 标明以该类为类型的属性都会被Jackson忽略
实体类:

1
2
3
4
5
6
7
8
9
10
11

public class User {
public int id;
public Name name;

@JsonIgnoreType
public static class Name {
public String firstName;
public String lastName;
}
}

序列化过程:

1
2
3
4
5
6
7
8
9

public void whenSerializingUsingJsonIgnoreType_thenCorrect() throws JsonProcessingException, ParseException {
User.Name name = new User.Name("John", "Doe");
User user = new User(1, name);
String result = new ObjectMapper().writeValueAsString(user);
assertThat(result, containsString("1"));
assertThat(result, not(containsString("name")));
assertThat(result, not(containsString("John")));
}

@JsonInclude

该注解在序列化时会排除属性值是空值(empty或null)、没有默认值的属性。
可作用在类和属性上
实体类:

1
2
3
4
5
6

@JsonInclude(Include.NON_NULL)
public class MyBean {
public int id;
public String name;
}

序列化过程:

1
2
3
4
5
6
7

public void whenSerializingUsingJsonInclude_thenCorrect() throws JsonProcessingException {
MyBean bean = new MyBean(1, null);
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("1"));
assertThat(result, not(containsString("name")));
}

@JsonAutoDetect

该注解可以覆盖属性是否可见的默认语义, 比如对于不可见的private序列化时变成可见的
实体类:

1
2
3
4
5
6

@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class PrivateBean {
private int id;
private String name;
}

序列化过程:

1
2
3
4
5
6
7

public void whenSerializingUsingJsonAutoDetect_thenCorrect() throws JsonProcessingException {
PrivateBean bean = new PrivateBean(1, "My bean");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("1"));
assertThat(result, containsString("My bean"));
}

常用注解

@JsonProperty

该注解可以指定属性在Json字符串中的名字
下例中在非标准的setter和getter方法上使用该注解, 可以成功序列化和反序列化
实体类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public class MyBean {
public int id;
private String name;

@JsonProperty("name")
public void setTheName(String name) {
this.name = name;
}

@JsonProperty("name")
public String getTheName() {
return name;
}
}

序列化和反序列化过程:

1
2
3
4
5
6
7
8
9
10

public void whenUsingJsonProperty_thenCorrect() throws IOException {
MyBean bean = new MyBean(1, "My bean");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("1"));

MyBean resultBean = new ObjectMapper().readerFor(MyBean.class).readValue(result);
assertEquals("My bean", resultBean.getTheName());
}

@JsonFormat

该注解指定序列化日期和时间时的格式
修改前面1.7的实体类:

1
2
3
4
5
6
7

public class Event {
public String name;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
public Date eventDate;
}

序列化过程:

1
2
3
4
5
6
7
8
9
10
11

public void whenSerializingUsingJsonFormat_thenCorrect() throws JsonProcessingException, ParseException {
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("UTC"));

String toParse = "20-12-2014 02:30:00";
Date date = df.parse(toParse);
Event event = new Event("party", date);
String result = new ObjectMapper().writeValueAsString(event);
assertThat(result, containsString(toParse));
}

@JsonUnwrapped

该注解指定值在序列化和反序列化时, 去除对应属性的外包装(根节点)
实体类:

1
2
3
4
5
6
7
8
9
10
11
12

public class UnwrappedUser {
public int id;

@JsonUnwrapped
public Name name;

public static class Name {
public String firstName;
public String lastName;
}
}

序列化过程:

1
2
3
4
5
6
7
8

public void whenSerializingUsingJsonUnwrapped_thenCorrect() throws JsonProcessingException, ParseException {
UnwrappedUser.Name name = new UnwrappedUser.Name("John", "Doe");
UnwrappedUser user = new UnwrappedUser(1, name);
String result = new ObjectMapper().writeValueAsString(user);
assertThat(result, containsString("John"));
assertThat(result, not(containsString("name")));
}

序列化结果:

1
2
3
4
5
6

{
"id":1,
"firstName":"John",
"lastName":"Doe"
}

@JsonView

该注解指明属性序列化和反序列时的视图级别(View)
视图类: 主要用于表明哪一级的实体类的属性会被序列化或反序列化

1
2
3
4
5

public class Views {
public static class Public {}
public static class Internal extends Public {}
}

实体类:

1
2
3
4
5
6
7
8
9

class UserWithRoot {
@JsonView(Views.Public.class)
public int id;
@JsonView(Views.Public.class)
public String name;
@JsonView(Views.Internal.class)
public String school;
}

实例化过程:

1
2
3
4
5
6
7
8
9
10
11
12
13

public void whenSerializingUsingJsonView_thenCorrect()
throws JsonProcessingException {
UserWithRoot user = new UserWithRoot();
user.id = 1;
user.name = "bl";
user.school = "suide";
try {
System.out.println(new ObjectMapper().writerWithView(Views.Internal.class).writeValueAsString(user));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}

本例中, school的视图级别是View.Internal类, 而序列化的映射器设定的视图显示级别是Views.Public类, 比school的类型高了一级, 所以序列化结果中没有school,
{“id”:1,”name”:”bl”}
而如果修改映射器的视图级别是Views.Internal类, 则序列化结果中包含school
{“id”:1,”name”:”bl”,”school”:”suide”}

@JsonManagedReference, @JsonBackReference

这两个注解配合使用, 可以解决两个不同类的属性的父子关系(parent/child relationships)和循环引用(work around loops)
使用@JsonBackReference可以在序列化时阻断循环引用, 原理是忽略被注解的属性, 否则会导致异常
本例中, 我们用这组注解来序列化ItemWithRef实体类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public class ItemWithRef {
public int id;
public String itemName;

@JsonManagedReference
public UserWithRef owner;
}
public class UserWithRef {
public int id;
public String name;

@JsonBackReference
public List<ItemWithRef> userItems;
}

序列化过程:

1
2
3
4
5
6
7

public void whenSerializingUsingJacksonReferenceAnnotation_thenCorrect() throws JsonProcessingException {
UserWithRef user = new UserWithRef(1, "John");
ItemWithRef item = new ItemWithRef(2, "book", user);
user.addItem(item);
String result = new ObjectMapper().writeValueAsString(item);
}

序列化结果: {“id”:2,”itemName”:”book”,”owner”:{“id”:1,”name”:”John”}}
如果把注解对调并序列化user结果是: {“id”:1,”name”:”John”,”userItems”:[{“id”:2,”itemName”:”book”}]}

@JsonIdentityInfo

该注解标明在序列化和反序列化一个值时, 该属性是否作为对象的唯一标识
该特性可以有效的解除循环引用, 和@JsonBackReference的区别是循环引用的对象的一个属性, 可以作为该对象的唯一标识被序列化, 而@JsonBackReference的循环引用对象不会二次序列化
两个实体类:

1
2
3
4
5
6
7
8
9
10
11
12
13

@JsonIdentityInfo( generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class ItemWithIdentity {
public int id;
public String itemName;
public UserWithIdentity owner;
}

public class UserWithIdentity {
public int id;
public String name;
public List<ItemWithIdentity> userItems;
}

实例化过程:

1
2
3
4
5
6
7

public void whenSerializingUsingJsonIdentityInfo_thenCorrect() throws JsonProcessingException {
UserWithIdentity user = new UserWithIdentity(1, "John");
ItemWithIdentity item = new ItemWithIdentity(2, "book", user);
user.addItem(item);
String result = new ObjectMapper().writeValueAsString(item);
}

序列化结果:{“id”:2,”itemName”:”book”,”owner”:{“id”:1,”name”:”John”,”userItems”:[2]}}
这里循环引用对象是ItemWithIdentity, 当它作为UserWithIdentity的属性时, 指定它的id属性为其唯一标识序列化到UserWithIdentity当中

@JsonFilter

该注解可以在序列化时指定一个过滤器
下面为一个实体类指定一个过滤器:

1
2
3
4
5
6

@JsonFilter("myFilter")
public class BeanWithFilter {
public int id;
public String name;
}

定义过滤器并进行序列化

1
2
3
4
5
6
7

public void whenSerializingUsingJsonFilter_thenCorrect() throws JsonProcessingException {
BeanWithFilter bean = new BeanWithFilter(1, "My bean");
FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter",
SimpleBeanPropertyFilter.filterOutAllExcept("name"));
String result = new ObjectMapper().writer(filters).writeValueAsString(bean);
}

序列化结果:{“name”:”My bean”}
这里添加了一个SimpleBeanPropertyFilter.filterOutAllExcept过滤器, 该过滤器的含义是除name属性外, 其他属性都被过滤掉(不序列化)

其他注解

@JsonAppend

该注解用来给一个被序列化的对象添加一个虚拟属性. 这个功能非常有用, 尤其是当我们想直接在Json字符串中添加额外的信息时, 不再需要修改类的定义. 举例来说, 它可以很方便的在Json文档中插入bean的版本信息, 而不需要bean提供对应的属性.
使用@JsonAppend注解的实体类:

1
2
3
4
5
6
7

@JsonAppend(attrs = {@JsonAppend.Attr(value = "version")})
public class BeanWithAppend {
private int id;
private String name;
// constructor, getters and setters
}

序列化过程:

1
2
3
4

BeanWithAppend bean = new BeanWithAppend(2, "Bean With Append Annotation");
ObjectWriter writer = mapper.writerFor(BeanWithAppend.class).withAttribute("version", "1.0");
String jsonString = writer.writeValueAsString(bean);

序列化结果: { “id”: 2, “name”: “Bean With Append Annotation”, “version”: “1.0” }

@JsonNaming

该注解用来在序列化时选择一个属性命名习惯来代替默认属性名. 注解参数value用来指定已有命名习惯, 或用户定义的命名习惯
除默认值(value=LOWER_CAMEL_CASE, 即驼峰命名法)外, Jackson库同时提供了4种内置的属性命名习惯:

1
2
3
4
KEBAB_CASE: 属性名单词用短线分隔连接, 比如hello-world
LOWER_CASE: 属性名用小写字母而且没有分隔符, 比如helloworld
SNAKE_CASE: 属性名用小写字母而且用下划线做分隔符, 比如hello_world
UPPER_CAMEL_CASE: 属性名所有单词用大写开头而且没有分隔符, 比如HelloWorld

下例中用SNAKE_CASE命名法, 将属性beanName名序列化为bean_name

1
2
3
4
5
6
7

@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class NamingBean {
private int id;
private String beanName;
// constructor, getters and setters
}

序列化过程:

1
2
3

NamingBean bean = new NamingBean(3, "Naming Bean");
String jsonString = mapper.writeValueAsString(bean);

序列化结果: { “id”: 3, “bean_name”: “Naming Bean” }

@JsonPropertyDescription

Jackson的独立模块JSON Schema提供了创建Json信息表(Json schemas)来描述Java的类型信息. 信息表可用于输出我们期望的序列化Java对象, 或者在反序列化前验证Json文档(document)
注解@JsonPropertyDescription允许把人类可读的描述信息, 附加在要创建的Json信息表的description属性
实体类:

1
2
3
4
5
6
7

public class PropertyDescriptionBean {
private int id;
@JsonPropertyDescription("This is a description of the name property")
private String name;
// getters and setters
}

序列化过程: 这里生成Json信息表的同时为它附加了description属性

1
2
3
4
5

SchemaFactoryWrapper wrapper = new SchemaFactoryWrapper();
mapper.acceptJsonFormatVisitor(PropertyDescriptionBean.class, wrapper);
JsonSchema jsonSchema = wrapper.finalSchema();
String jsonString = mapper.writeValueAsString(jsonSchema);

序列化结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

{
"type": "object",
"id": "urn:jsonschema:com:baeldung:jackson:annotation:extra:PropertyDescriptionBean",
"properties":
{
"name":
{
"type": "string",
"description": "This is a description of the name property"
},

"id":
{
"type": "integer"
}
}
}

@JsonPOJOBuilder

该注解用来配置一个builder类用于定制反序列化过程, 尤其是当Json文档中属性命名习惯和POJO类对象的属性不同
准备反序列化的Json字符串: { “id”: 5, “name”: “POJO Builder Bean”}
反序列化的目标类:

1
2
3
4
5
6
7

@JsonDeserialize(builder = BeanBuilder.class)
public class POJOBuilderBean {
private int identity;
private String beanName;
// constructor, getters and setters
}

注意:BeanBuilder是自定义bulider类, 参见下文.
可以看到, bean属性的名称和Json字符串中对应属性的名称不同. 这就是@JsonPOJOBuilder发挥作用的地方.
@JsonPOJOBuilder有两个参数:

buildMethodName: 一个无参方法, 用来在绑定Json属性和bean属性后, 创建bean的实例
withPrefix: 方法名前缀, 有该前缀的方法是用来匹配Json属性和bean的属性. 默认前缀是with
下面是BeanBuilder类定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

@JsonPOJOBuilder(buildMethodName = "createBean", withPrefix = "construct")
public class BeanBuilder {
private int idValue;
private String nameValue;

public BeanBuilder constructId(int id) {
idValue = id;
return this;
}

public BeanBuilder constructName(String name) {
nameValue = name;
return this;
}

public POJOBuilderBean createBean() {
return new POJOBuilderBean(idValue, nameValue);
}
}

上面的代码中, 我们配置了注解@JsonPOJOBuilder的参数, 用createBean方法作为build方法, 用construct前缀来匹配属性名
反序列化过程:

1
2
3

String jsonString = "{\"id\":5,\"name\":\"POJO Builder Bean\"}";
POJOBuilderBean bean = mapper.readValue(jsonString, POJOBuilderBean.class);

@JsonTypeId

该注解作用于属性, 使得该属性不再是普通属性, 其值代表bean类的类型ID(`TypeId), 可以用它来描述多态时实体类对象的实际类型
实体类:

1
2
3
4
5
6
7
8

public class TypeIdBean {
private int id;
@JsonTypeId
private String name;

// constructor, getters and setters
}

序列化过程:

1
2
3
4

mapper.enableDefaultTyping(DefaultTyping.NON_FINAL);
TypeIdBean bean = new TypeIdBean(6, "Type Id Bean");
String jsonString = mapper.writeValueAsString(bean);

序列化结果:[“Type Id Bean”,{“id”:6}]

mapper.enableDefaultTyping(DefaultTyping.NON_FINAL)的作用是在序列化结果中显示实体类类型属性
结果是一个Json对象, 其中”Type Id Bean”是实体类ID的描述, {“id”:6}是类的属性值

禁用Jackson注解

通过设置MapperFeature.USE_ANNOTATIONS可以禁用实体类上的Jackson注解
实体类:

1
2
3
4
5
6
7

@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "name", "id" })
public class MyBean {
public int id;
public String name;
}

序列化过程:

1
2
3
4
5
6
7

public void whenDisablingAllAnnotations_thenAllDisabled() throws IOException {
MyBean bean = new MyBean(1, null);
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.USE_ANNOTATIONS);
String result = mapper.writeValueAsString(bean);
}

序列化结果:{ “id”:1, “name”:null}
如果注释掉mapper.disable(MapperFeature.USE_ANNOTATIONS);, 则序列化结果是: {“id”:1}

注解混合

多个实体类的注解可以混合在一起使用
下例中把类MyMixInForIgnoreType的注解@@JsonIgnoreType作用到了类Item的属性User:

1
2
3
4
5
6
7
8
9

public class Item {
public int id;
public String itemName;
public User owner;
}

@JsonIgnoreType
public class MyMixInForIgnoreType {}

序列化过程:

1
2
3
4
5
6
7
8
9
10
11
12
13

public void whenSerializingUsingMixInAnnotation_thenCorrect() throws JsonProcessingException {
Item item = new Item(1, "book", null);

String result = new ObjectMapper().writeValueAsString(item);
//结果: {"id":1,"itemName":"book","owner":null}

ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(User.class, MyMixInForIgnoreType.class);

result = mapper.writeValueAsString(item);
//结果: {"id":1,"itemName":"book"}
}