Spring MVC
Spring MVC 是 Spring 框架中用于构建
Web
应用程序的模块,基于经典的
Model-View-Controller (MVC)
设计模式,它提供了灵活、强大的 Web 开发解决方案
MVC 设计模式
Model-View-Controller (MVC):C控制层,M模块层,V显示层
用于实现前端页面的展现与后端业务数据处理的分离
M:model(模型) :封装应用程序数据
V:view(视图) :渲染模型数据(JSP, Thymeleaf, FreeMarker 等)
C:controller(控制器):桥梁 处理请求并返回模型和视图
Spring MVC核心组件
1.DispatcherServlet
:前端控制器,接收所有请求并协调处理流程(不需要
程序员开发)
2.HandlerMapping
:映射请求到对应的处理器(控制器)(不需
要程序员开发)
3.Controller
:处理请求并返回模型和视图(需
要程序员开发)
4.ViewResolver
:将逻辑视图名解析为实际视图实现 (不需要
程序员开发)
5.Model
:封装应用程序数据(不需要
程序员开发)
6.View
:渲染模型数据(JSP, Thymeleaf, FreeMarker 等)(需要
程序员开发 如jsp)
我从别处找了一张比较清晰的流程图
对应后端开发者而言,需要后端控制层的Hander的开发,古老的基于配置文件的MVC模式 现在不使用了,但是那种更直观的学习
Spring MVC 后端伪代码示例
控制器层 Controller
// ================== 控制器层 ==================
@RestController // 声明为 REST 控制器,自动将返回值转为 JSON
@RequestMapping("/api/users") // 基础路径映射
public class UserController {
//...请求...//
}
GET 请求
1.数据传输方式
:GET请求将参数包含在URL中
2.安全性
:GET请求的参数暴露在URL中,容易被截获和篡改,因此安全性较低
3.数据大小限制
:GET请求的参数长度通常受限于URL长度限制(通常为2048个字符)
4.缓存和历史记录处理
:GET请求会被浏览器缓存,并且请求的参数会被保存在浏览器的历史记录中
5.TCP数据包数量
:GET请求通常产生一个TCP数据包
/** 获取所有用户 - GET 请求
* @RequestParam 获取查询参数,name defaultValue 键值对的参数值
* required=false 表示非必需
*/
@GetMapping //get请求的注解
public List<UserDTO> getAllUsers(
@RequestParam(name = "page", defaultValue = "1") int page,
@RequestParam(name = "size", defaultValue = "10") int size) {
// 实现逻辑: 调用服务层获取分页用户列表
return List.of(new UserDTO());
}
@RequestParam
:将请求参数绑定到controller的方法参数上,name defaultValue 键值对的参数值,可以设置required=false 表示非必需,可以同时有多个
POST 请求
1.数据传输方式
:而POST请求将参数放在请求体(request body)
2.安全性
:POST请求的参数在请求体中,更安全
3.数据大小限制
:请求的数据大小可以通过设置Content-Length头部来控制,可以处理更大量的数据
4.缓存和历史记录处理
:OST请求则不会被缓存,也不会在浏览器历史记录中保存请求参数
5.TCP数据包数量
:POST请求在某些情况下可能会产生两个TCP数据包,因为POST请求需要先发送请求头,服务器响应后再发送请求体
/**创建用户 - POST 请求
* @RequestBody 从请求体中获取 JSON 数据并转换为 UserDTO 对象
*/
@PostMapping
@ResponseStatus(HttpStatus.CREATED) // 设置响应状态码为 201 Created
public UserDTO createUser(@RequestBody UserDTO userDTO) {
// 实现逻辑: 调用服务层创建用户
return new UserDTO(); // 实际返回创建的用户对象
}
@RequestBody
: 限定了前端传递的参数必须为json格式
,并且前端不能使用GET方式
提交数据,而是用POST
方式进行提交,而且@RequestBody 只能有一个。
PUT 请求
更新需要
:PUT请求用于向服务器存储一个资源,如果这个资源已经存在,则替换旧文档
/**
* 更新用户 - PUT 请求
* 组合使用 @PathVariable 和 @RequestBody
*/
@PutMapping("/{id}")
public UserDTO updateUser(
@PathVariable Long id,
@RequestBody UserDTO userDTO) {
// 实现逻辑: 调用服务层更新用户信息
return new UserDTO();
}
@PathVariable
:用于将URL中的模板变量绑定到方法的参数上
DELETE 请求
删除需要
:DELETE请求用于请求服务器删除URL指定资源
/**删除用户 - DELETE 请求
* 返回 ResponseEntity 可自定义响应状态
*/
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
// 实现逻辑: 调用服务层删除用户
return ResponseEntity.noContent().build(); // 204 No Content
}
数据传输对象层 (DTO)
// ================== 数据传输对象 (DTO) ==================
class UserDTO {
// 使用 Lombok 的 @Data 可简化 getter/setter
@Data
private Long id;
@Data
private String username;
@Data
private String email;
}
@Data
注解相当于 @Getter
、@Setter
、@RequiredArgsConstructo
r、@ToString
和 @EqualsAndHashCode
服务层( Service )
由Conrtoller分发到服务层 调用具体的也从场景实现业务逻辑
// ================== 服务层 ==================
@Service // 标记为 Spring 服务组件
class UserService {
/**
* 业务逻辑实现
* 在这个实现过程可能需要访问数据库
*/
}
数据访问层 (Repository )
// ================== 数据访问层 ==================
@Repository // 标记为数据访问组件
interface UserRepository extends JpaRepository<User, Long> {
// Spring Data JPA 会自动实现基本方法
// 比如 自定义查询方法
List<User> findByUsernameContaining(String keyword);
}
全局处理的控制层
上面都是正常的业务分发处理流程,但是还有日志啊 异常处理 监听啊这些和业务逻辑无关的
异常处理
@ControllerAdvice // 全局控制器增强
class GlobalExceptionHandler {
/**处理特定异常
* @ExceptionHandler 声明处理的异常类型
*/
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleNotFound(ResourceNotFoundException ex) {
return new ErrorResponse(ex.getMessage());
}
/**
* 处理验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleValidationException(MethodArgumentNotValidException ex) {
// 处理参数验证错误
return new ErrorResponse("Validation failed");
}
}
@ExceptionHandler注解
:使用他定义方法上,该方法用于处理特定类型的异常,这个可以处理多个
@ResponseStatus
注解用于标记控制器方法或异常类,以指示HTTP响应的状态码和理由短语
自定义注解
// ================== 自定义注解 ==================
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUser {
// 自定义注解示例:用于获取当前登录用户
}
元注解:@Target @Retention @Documented @Inherited
元注解:注解的注解,即java为注解开发特准备的注解
@Targe
表示该注解用于什么地方
@Retention
:表示该注解可以保存的范围
@Documented
:拥有这个注解的元素可以被javadoc此类的工具文档化
@Inherited
:允许子类继承父类中的注解
但是自定义注解需要自行提供该注解的作用功能逻辑,实现机制是反射应用
自定义注解的使用样例比如
class UserDTO {
@Data
@CurrentUser
private User currentUser;
}
其余场景注解
@RequestHeader
:获取请求头信息
public void process(@RequestHeader("User-Agent") String userAgent)
@Valid / @Validated
:参数验证(需配合 JSR-303 验证注解)
public UserDTO create(@Valid @RequestBody UserDTO userDTO)
@CookieValue
:获取 Cookie 值
public void auth(@CookieValue("sessionId") String sessionId)
顺便简单说下cookie和session 区别
存储位置
:Cookie
的数据信息存放在客户端浏览器
上。Session
的数据信息存放在服务器
上
存储容量
:单个Cookie保存的数据<=4KB,一个站点最多保存20个Cookie。对于Session来说,并没有上限,但出于对服务器端的性能考虑,Session内不要存放过多内容,并且要设置Session删除机制
安全性
:Cookie对客户端是可见的,别有用心的人可以分析存放在本地的Cookie并进行Cookie欺骗,所以Cookie不安全
。Session存储在服务器上,不存在敏感信息泄漏的风险,所以Session安全