Spring Web的零碎总结

一、目录结构

在大型Spring Web项目中,良好的目录结构对于项目的可维护性、可扩展性和团队协作至关重要。虽然没有官方的“一刀切”结构,但通常会遵循一些最佳实践和约定。以下是一个典型的目录结构示例:

/my-project
|-- /src
    |-- /main
        |-- /java
            |-- /com
                |-- /mycompany
                    |-- /myproject
                        |-- /config              # 配置类
                        |-- /controller          # 控制器
                        |-- /service             # 服务层
                        |-- /repository          # 数据访问层(如MyBatis Mapper)
                        |-- /domain              # 领域模型(实体类)
                        |-- /dto                 # 数据传输对象(DTO)
                        |-- /exception           # 自定义异常
                        |-- /util                # 工具类
                        |-- /integration         # 集成服务(如消息队列、外部API)
                        |-- /security            # 安全相关(如认证、授权)
                        |-- /filter              # 过滤器
                        |-- /interceptor         # 拦截器
                        |-- /websocket           # WebSocket相关
                        |-- /task                # 定时任务
                        |-- /initializer         # 应用初始化器
                        |-- /listener            # 监听器
                        |-- /mapper              # MyBatis Mapper XML文件
                        |-- /handler             # 处理器(如异常处理器)
                        |-- /constant            # 常量类
                        |-- /properties          # 属性类
                        |-- /test                # 测试类
        |-- /resources
            |-- /static                     # 静态资源(如CSS, JS, images)
            |-- /templates                  # 模板文件(如Thymeleaf, FreeMarker)
            |-- /mapper                     # MyBatis Mapper XML文件
            |-- /config                     # 配置文件(如application.yml, logback.xml)
            |-- /db                         # 数据库脚本
            |-- /META-INF                   # 元数据文件
    |-- /test
        |-- /java                         # 测试代码
        |-- /resources                    # 测试资源
|-- /build                             # Maven或Gradle构建输出
|-- /docs                              # 项目文档
|-- /scripts                           # 脚本文件
|-- /docker                            # Docker相关文件
|-- /deploy                            # 部署相关文件
|-- /lib                               # 第三方库
|-- /logs                              # 日志文件
|-- /tmp                               # 临时文件
|-- .gitignore                         # Git忽略文件
|-- .gitlab-ci.yml                     # GitLab CI/CD配置文件
|-- .travis.yml                        # Travis CI配置文件
|-- pom.xml                            # Maven项目对象模型
|-- README.md                          # 项目说明文档

这种目录结构只是参考,目的还是使项目结构清晰,易于理解增加后期的可维护。可以根据项目的具体需求和团队的偏好,目录结构都会有所不同。重要的是代码组织合理,保持一致性,并确保团队成员都能理解和遵循这种结构。

二、常用注解总结

在Java开发中,尤其是在使用Spring框架的项目中,注解(Annotations)是一种强大的工具,用于简化配置、提高代码的可读性和可维护性。以下是一些常见的注解,它们在开发过程中经常被使用:

1. Java标准注解

  • @Override: 表示方法覆盖了父类的方法。
  • @Deprecated: 表示方法或类已过时,不推荐使用。
  • @SuppressWarnings: 抑制编译器警告。

2. Spring框架注解

  • @SpringBootApplication: 标记一个Spring Boot应用的主类。
  • @Controller: 标记一个类为Spring MVC控制器。
  • @RestController: 结合@Controller@ResponseBody,用于RESTful服务。
  • @RequestMapping: 映射HTTP请求到控制器方法。
  • @GetMapping, @PostMapping, @PutMapping, @DeleteMapping: 特定HTTP方法的请求映射。
  • @RequestParam: 绑定查询参数到方法参数。
  • @PathVariable: 绑定路径变量到方法参数。
  • @RequestBody: 绑定请求体到方法参数。
  • @ResponseBody: 表示方法返回值直接写入HTTP响应体。
  • @Autowired: 自动装配Bean。
  • @Component, @Service, @Repository, @Configuration: 标记组件,用于自动扫描和注册Bean。
  • @Bean: 在@Configuration类中声明一个Bean。
  • @Value: 注入属性值。
  • @Profile: 指定在特定环境下激活的Bean。
  • @Scope: 定义Bean的作用域。
  • @Transactional: 声明事务管理。
  • @Cacheable, @CachePut, @CacheEvict: 缓存管理。
  • @Scheduled: 定时任务。
  • @EnableScheduling: 启用定时任务。
  • @EnableAsync: 启用异步方法执行。
  • @Async: 标记异步方法。
  • @Valid: 校验数据绑定。
  • @Path: 在JAX-RS中定义资源路径。

3. JPA/Hibernate注解

  • @Entity: 标记一个类为JPA实体。
  • @Table: 指定实体对应的表。
  • @Id: 标记主键。
  • @GeneratedValue: 指定主键生成策略。
  • @Column: 指定列属性。
  • @OneToMany, @ManyToOne, @OneToOne, @ManyToMany: 定义实体间的关系。
  • @JoinColumn: 指定外键列。
  • @Query: 自定义JPQL或SQL查询。
  • @Transactional: 声明事务管理。

4. MyBatis注解

  • @Mapper: 标记MyBatis Mapper接口。
  • @Select, @Insert, @Update, @Delete: 标记SQL语句。
  • @Param: 指定方法参数的名称。

5. 测试相关注解

  • @Test: 标记JUnit测试方法。
  • @Before, @After, @BeforeClass, @AfterClass: 标记JUnit生命周期方法。
  • @RunWith: 指定测试运行器。
  • @SpringBootTest: 为Spring Boot应用提供集成测试环境。
  • @MockBean, @SpyBean: 在Spring上下文中添加Mock或Spy Bean。

6. 其他常用注解

  • @JsonIgnore: 在序列化和反序列化时忽略字段。
  • @JsonProperty: 指定JSON属性名。
  • @JsonFormat: 格式化日期或时间字段。
  • @JsonInclude: 指定序列化时的包含策略。
  • @Data: Lombok注解,自动生成Getter、Setter、ToString等方法。
  • @Builder: Lombok注解,生成构建器模式。
  • @NoArgsConstructor, @AllArgsConstructor, @RequiredArgsConstructor: Lombok注解,生成无参、全参、必需参数的构造函数。

这些注解在Java开发中非常常见,它们极大地简化了代码的编写和配置。在实际开发中,根据项目的需求和技术栈,可能会使用到更多的注解。

三、常用工具库

1. SLF4J (Simple Logging Facade for Java)

SLF4J是一个日志抽象层,它为多种日志框架(如Logback、Log4j、java.util.logging等)提供了一个简单的门面或抽象。SLF4J允许开发者在编译时使用统一的API进行日志记录,而在部署时可以插入不同的日志实现。这样做的好处是,开发者可以在不修改代码的情况下,根据需要更换日志框架。

主要特性:

  • 抽象层:提供统一的日志记录API,隐藏底层日志框架的细节。
  • 桥接器:提供桥接器,可以将其他日志框架的API调用重定向到SLF4J。
  • 灵活性:允许在部署时选择和配置具体的日志实现。
  • 无依赖:SLF4J本身不依赖于任何具体的日志框架。

2. Jackson

Jackson是一个高性能的Java库,用于处理JSON数据格式。它提供了灵活的API,可以轻松地将Java对象序列化为JSON字符串,以及将JSON字符串反序列化为Java对象。Jackson广泛用于RESTful Web服务、配置文件处理、数据交换等场景。

主要特性:

  • 高性能:在处理大量数据时表现出色。
  • 灵活的配置:可以通过注解、配置文件或编程方式自定义序列化和反序列化行为。
  • 支持注解:可以使用Java注解来控制序列化和反序列化的细节。
  • 支持多种格式:除了JSON,还支持YAML、Smile等格式。
  • 模块化:由多个模块组成,如databind、dataformat等。

3. Gson

Gson是Google提供的一个简单易用的Java库,用于将Java对象转换为JSON,以及将JSON转换为Java对象。Gson设计简洁,易于集成,适合处理简单的JSON数据。

主要特性:

  • 简单易用:API直观,易于上手。
  • 支持复杂类型:可以处理泛型、嵌套对象等复杂类型。
  • 自定义序列化:可以通过实现JsonSerializerJsonDeserializer接口来自定义序列化和反序列化逻辑。
  • 支持注解:可以使用注解来控制序列化和反序列化的行为。

4. Protobuf (Protocol Buffers)

Protobuf是Google开发的一种轻量级、高效的数据交换格式。它是一种二进制格式,比JSON和XML更紧凑、更快速。Protobuf通过定义消息格式(.proto文件)来生成序列化和反序列化的代码,支持多种编程语言。

主要特性:

  • 二进制格式:紧凑的数据表示,传输效率高。
  • 语言无关:支持多种编程语言。
  • 向后兼容:新增字段不会影响旧版本的解析。
  • 自动代码生成:通过.proto文件生成序列化和反序列化的代码。
  • 支持多种数据类型:包括基本类型、枚举、嵌套消息等。

5. Kryo

Kryo是一个快速高效的Java序列化库,它专注于简单性和性能。Kryo可以序列化和反序列化Java对象,但不支持跨语言。Kryo在处理复杂对象图和大量数据时表现出色。

主要特性:

  • 高性能:序列化和反序列化速度快。
  • 简单API:易于使用的API。
  • 支持注册:可以通过注册来优化序列化和反序列化的性能。
  • 不支持跨语言:Kryo仅支持Java。
  • 支持复杂对象:可以处理循环引用、继承等复杂对象关系。

6. Hutool

Hutool是一个Java工具包,它封装了许多实用的工具类,旨在简化Java开发中的日常编码。Hutool提供了大量的工具方法,涵盖了字符串处理、日期时间处理、加密解密、文件IO、网络通信、集合操作等多个方面。使用Hutool可以减少代码量,提高开发效率。

主要特性:

  • 字符串工具:提供丰富的字符串处理方法。
  • 日期时间工具:简化日期和时间的操作。
  • 加密解密工具:支持多种加密算法。
  • 文件IO工具:简化文件读写和操作。
  • 网络工具:提供HTTP客户端等网络相关工具。
  • 集合工具:简化集合的操作。
  • 等等。

7. MyBatis-Plus

MyBatis-Plus(简称MP)是在MyBatis的基础上开发的增强工具,旨在简化MyBatis的开发。它提供了许多实用的功能,如代码生成器、通用CRUD操作、分页插件、性能分析插件等,可以帮助开发者减少重复的CRUD代码编写,提高开发效率。

主要特性:

  • 代码生成器:自动生成Mapper、Entity、Service等代码。
  • 通用CRUD:提供通用的增删改查方法。
  • 分页插件:简化分页查询的实现。
  • 性能分析插件:帮助分析SQL执行性能。
  • 逻辑删除:支持逻辑删除功能。
  • 乐观锁插件:支持乐观锁机制。
  • 等等。

8. Lombok

Lombok是一个Java库,它通过注解来简化Java代码,减少样板代码的编写。Lombok提供了一系列的注解,用于自动生成常见的Java代码,如getter和setter方法、构造函数、equals和hashCode方法、toString方法等。使用Lombok可以显著减少代码量,提高代码的可读性和可维护性。

主要特性:

  • @Getter@Setter: 自动生成字段的getter和setter方法。
  • @ToString: 自动生成toString方法。
  • @EqualsAndHashCode: 自动生成equals和hashCode方法。
  • @NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor: 自动生成无参、必需参数和全参构造函数。
  • @Data: 结合了@Getter@Setter@ToString@EqualsAndHashCode@RequiredArgsConstructor
  • @Builder: 自动生成构建器模式代码。
  • @Slf4j@Log: 自动生成日志记录器。
  • @NonNull: 标记参数或字段为非空,自动添加空值检查。
  • @Cleanup: 自动管理资源,确保资源被正确关闭。

9. Fastjson

Fastjson是一个高性能的Java JSON库,由阿里巴巴开发。它提供了强大的JSON解析和生成功能,支持将Java对象序列化为JSON字符串,以及将JSON字符串反序列化为Java对象。Fastjson以其快速的性能和丰富的功能而受到开发者的欢迎。

主要特性:

  • 高性能:Fastjson在序列化和反序列化JSON数据时具有很高的性能。
  • 支持泛型:可以处理泛型类型的序列化和反序列化。
  • 支持复杂类型:可以处理复杂的Java类型,如集合、映射、数组等。
  • 支持注解:可以使用注解自定义序列化和反序列化的行为。
  • 支持JSONPath:可以使用JSONPath表达式查询JSON数据。
  • 支持多种日期格式:可以自定义日期和时间的格式。
  • 支持自定义序列化器和反序列化器:可以自定义序列化和反序列化的逻辑。

这里需要提一下,项目中引入lombok后,就可以使用@Data和@Slf4j注解了。此处需要注意的是:
SLF4J(Simple Logging Facade for Java)和Lombok是两个独立的Java库,它们各自解决不同的问题,但可以一起使用以提高开发效率。Lombok的@Slf4j 注解是Lombok提供的一个特殊注解,它自动为类生成一个名为log的日志记录器变量,该变量是org.slf4j.Logger类型。这样,开发者就可以直接在类中使用log变量来记录日志,而无需手动创建Logger实例。@Slf4j 注解利用了SLF4J的API,使得开发者可以方便地使用SLF4J进行日志记录,而不需要编写重复的日志记录器初始化代码。

四、接收参数

在Spring MVC或Spring Boot项目中,Controller层负责处理HTTP请求并返回响应。接收参数的方式多种多样,以下是一些常见的方法:

1. 路径变量(Path Variables)

通过URL路径传递参数,通常用于标识资源。

@GetMapping("/users/{userId}")
public ResponseEntity<User> getUser(@PathVariable Long userId) {
    // ...
}

2. 查询参数(Query Parameters)

通过URL的查询字符串传递参数。

@GetMapping("/users")
public ResponseEntity<List<User>> getUsers(@RequestParam String name) {
    // ...
}

3. 请求体(Request Body)

通过POST或PUT请求的请求体传递复杂数据结构,通常用于创建或更新资源。

@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody User user) {
    // ...
}

4. 表单数据(Form Data)

通过表单提交的数据,可以是键值对形式。

@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestParam String name, @RequestParam int age) {
    // ...
}

5. 请求头(Request Headers)

通过请求头传递参数。

@GetMapping("/users")
public ResponseEntity<List<User>> getUsers(@RequestHeader("Authorization") String authToken) {
    // ...
}

6. Cookie

通过Cookie传递参数。

@GetMapping("/users")
public ResponseEntity<List<User>> getUsers(@CookieValue("sessionId") String sessionId) {
    // ...
}

7. 请求参数(Request Parameters)

直接从HttpServletRequest对象中获取参数。

@GetMapping("/users")
public ResponseEntity<List<User>> getUsers(HttpServletRequest request) {
    String name = request.getParameter("name");
    // ...
}

8. 自定义注解

创建自定义注解来封装参数获取逻辑。

@GetMapping("/users")
public ResponseEntity<List<User>> getUsers(@MyCustomAnnotation String customParam) {
    // ...
}

9. 会话属性(Session Attributes)

从HttpSession中获取属性。

@GetMapping("/users")
public ResponseEntity<List<User>> getUsers(HttpSession session) {
    String userId = (String) session.getAttribute("userId");
    // ...
}

10. 模型属性(Model Attributes)

通过@ModelAttribute注解将请求参数绑定到模型对象。

@PostMapping("/users")
public ResponseEntity<User> createUser(@ModelAttribute User user) {
    // ...
}

11. 文件上传(File Upload)

通过@RequestParam("file") MultipartFile file接收上传的文件。

@PostMapping("/upload")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
    // ...
}

12. 自定义参数解析器

创建自定义的参数解析器来处理特定的参数类型。

@GetMapping("/users")
public ResponseEntity<List<User>> getUsers(@MyCustomParameter User user) {
    // ...
}

在实际开发中,选择哪种方式取决于参数的类型、用途以及API的设计。通常,路径变量和查询参数用于获取资源,请求体用于创建或更新资源,而请求头、Cookie和会话属性用于传递认证和授权信息。表单数据和文件上传通常用于用户交互,而自定义注解和参数解析器用于处理特殊情况。

五、不同数据的响应方式

在Spring MVC或Spring Boot项目中,Controller层负责处理HTTP请求并返回响应。实现返回不同数据格式的方式通常涉及使用不同的注解和配置。以下是一些常见的方式:

1. 返回JSON数据

使用@ResponseBody注解或@RestController注解,Spring会自动将返回的对象转换为JSON格式。

@RestController
@RequestMapping("/api")
public class MyController {
    @GetMapping("/data")
    public MyData getData() {
        return new MyData("example data");
    }
}

或者使用@ResponseBody

@Controller
@RequestMapping("/api")
public class MyController {
    @GetMapping("/data")
    @ResponseBody
    public MyData getData() {
        return new MyData("example data");
    }
}

2. 返回XML数据

要返回XML数据,你需要添加Jackson XML模块(如jackson-dataformat-xml),并使用@ResponseBody注解。

@RestController
@RequestMapping("/api")
public class MyController {
    @GetMapping(value = "/data", produces = MediaType.APPLICATION_XML_VALUE)
    public MyData getData() {
        return new MyData("example data");
    }
}

确保你的MyData类使用了@XmlRootElement注解,以便Jackson可以将其转换为XML。

3. 返回HTML视图

使用@Controller注解,并返回视图名称。

@Controller
@RequestMapping("/")
public class MyController {
    @GetMapping("/page")
    public String getPage() {
        return "myView";
    }
}

确保你的项目中配置了Thymeleaf、FreeMarker或其他模板引擎,并且myView是模板文件的名称。

4. 返回字符串或原始数据

直接返回字符串或原始数据类型,Spring会将其作为响应体返回。

@RestController
@RequestMapping("/api")
public class MyController {
    @GetMapping("/text")
    public String getText() {
        return "This is a text response";
    }
}

5. 返回文件

使用ResponseEntity来返回文件,可以设置响应头和响应体。

@GetMapping("/download")
public ResponseEntity<Resource> downloadFile() {
    Resource file = // 获取文件资源
    return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"")
            .body(file);
}

6. 返回自定义数据格式

如果你需要返回自定义的数据格式,可以使用HttpMessageConverter来自定义转换逻辑。

@RestController
@RequestMapping("/api")
public class MyController {
    @GetMapping("/custom")
    public MyCustomData getCustomData() {
        return new MyCustomData("custom data");
    }
}

确保你的项目中配置了相应的HttpMessageConverter来处理MyCustomData类的序列化。

7. 使用MediaType指定数据格式

@RequestMapping@GetMapping等注解中使用produces属性来指定返回的数据格式。

@GetMapping(value = "/data", produces = MediaType.APPLICATION_JSON_VALUE)
public MyData getData() {
    return new MyData("example data");
}

或者对于XML:

@GetMapping(value = "/data", produces = MediaType.APPLICATION_XML_VALUE)
public MyData getData() {
    return new MyData("example data");
}

通过上述方式,你可以根据需要返回不同的数据格式。Spring框架提供了灵活的机制来处理这些需求,使得开发者可以轻松地构建RESTful服务或传统的Web应用。

六、自定义数据类型的响应

此处以用户和订单为例,假设你希望在返回User对象的JSON表示时,额外包含Order数据,你可以采用以下几种方法:

1. 创建一个新的数据传输对象(DTO)

创建一个新的DTO类,该类包含User对象的所有字段以及额外的Order字段。在Controller中,你可以将User对象和Order对象组合成DTO对象,然后返回这个DTO对象。

public class UserWithOrderDTO {
    private User user;
    private Order order;

    // 构造函数、getter和setter
}

在Controller中:

@GetMapping("/userWithOrder")
public UserWithOrderDTO getUserWithOrder() {
    User user = userService.getUser();
    Order order = orderService.getOrder();
    return new UserWithOrderDTO(user, order);
}

2. 使用@JsonUnwrapped注解

如果你不想创建新的DTO类,可以使用Jackson的@JsonUnwrapped注解来扁平化User对象,使其包含Order字段。这需要在User类中添加一个包含Order信息的字段,并使用@JsonUnwrapped注解。

public class User {
    // 其他字段

    @JsonUnwrapped
    private UserWithOrder userWithOrder;

    // getter和setter
}

public class UserWithOrder {
    private User user;
    private Order order;

    // 构造函数、getter和setter
}

在Controller中:

@GetMapping("/userWithOrder")
public User getUserWithOrder() {
    User user = userService.getUser();
    Order order = orderService.getOrder();
    UserWithOrder userWithOrder = new UserWithOrder(user, order);
    user.setUserWithOrder(userWithOrder);
    return user;
}

3. 使用自定义序列化器

你可以创建一个自定义的Jackson序列化器,在序列化User对象时,手动添加Order字段。

public class UserSerializer extends StdSerializer<User> {
    public UserSerializer() {
        this(null);
    }

    public UserSerializer(Class<User> t) {
        super(t);
    }

    @Override
    public void serialize(User user, JsonGenerator jgen, SerializerProvider provider) throws IOException {
        jgen.writeStartObject();
        // 写入User字段
        jgen.writeObjectField("user", user);
        // 获取Order并写入
        Order order = orderService.getOrder();
        jgen.writeObjectField("order", order);
        jgen.writeEndObject();
    }
}

然后在User类上使用@JsonSerialize注解来应用这个序列化器:

@JsonSerialize(using = UserSerializer.class)
public class User {
    // User字段
}

4. 使用@JsonAnyGetter@JsonAnySetter

你可以使用@JsonAnyGetter@JsonAnySetter注解来动态地添加额外的字段。这需要在User类中添加一个Map来存储额外的字段。

public class User {
    // User字段

    private Map<String, Object> additionalProperties = new HashMap<>();

    @JsonAnyGetter
    public Map<String, Object> getAdditionalProperties() {
        return this.additionalProperties;
    }

    @JsonAnySetter
    public void setAdditionalProperty(String name, Object value) {
        this.additionalProperties.put(name, value);
    }
}

在Controller中:

@GetMapping("/userWithOrder")
public User getUserWithOrder() {
    User user = userService.getUser();
    Order order = orderService.getOrder();
    user.setAdditionalProperty("order", order);
    return user;
}

以上方法中,创建DTO是最常见和推荐的做法,因为它有助于保持领域模型(User实体类)的清晰和简洁,同时允许你灵活地控制API的响应格式。自定义序列化器提供了最大的灵活性,但也会增加代码的复杂性。@JsonUnwrapped@JsonAnyGetter/@JsonAnySetter提供了介于两者之间的解决方案。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇