Spring-MVC笔记(下)

响应数据

handler 方法分析

/**

* TODO: 一个controller的方法是控制层的一个处理器(每个处理的业务方法),我们称为handler

* TODO: handler需要使用@RequestMapping/@GetMapping系列,声明路径,在HandlerMapping中注册,供DS查找!

* TODO: handler作用总结:

* 1.接收请求参数(param,json,pathVariable,共享域等)

* 2.调用业务逻辑

* 3.响应前端数据(页面(不讲解模版页面跳转),json,转发和重定向等)

* TODO: handler如何处理呢

* 1.接收参数: handler(形参列表: 主要的作用就是用来接收参数)

* 2.调用业务: { 方法体 可以向后调用业务方法 service.xx() }

* 3.响应数据: return 返回结果,可以快速响应前端数据

*/

@GetMapping

public Object handler(简化请求参数接收){

调用业务方法

返回的结果 (页面跳转,返回数据(json格式))

return 简化响应前端数据;

}

总结: 请求数据接收,我们都是通过handler的形参列表

前端数据响应,我们都是通过handler的return关键字快速处理!

springmvc简化了参数接收和响应!

快速返回逻辑视图

* 快速返回一个jsp模板页面

* 实现步骤:

* ①先在WEB-INF下创建一个jsp模板页面,(WEB-INF下的文件会受保护)

* ②创建配置类,添加handlerMapping,handlerAdapter组件 视图解析器

* 添加handlerMapping,handlerAdapter组件

* 直接在配置类上添加@EnableWebMvc注解即可

* 添加 视图解析器

* 让配置类实现WebMvcConfigurer接口,重写configureViewResolvers(ViewResolverRegistry registry) 方法

* 通过registry调用jsp方法传入前缀与后缀

* ③创建springMVC的初始化类,

* * springmvc的初始化类

* * Spring-MVC环境搭建

* * 要做的事情

* * 指定mvc的配置类,创建ioc容器

* * 配置拦截地址

* 继承AbstractAnnotationConfigDispatcherServletInitializer

* 重写三个方法 向getServletConfigClasses方法传入配置类

* 在getServletMappings方法中配置拦截地址

Index.jsp 文件

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%@ page isELIgnored="false" %>

<html>

<head>

<title>Title</title>

</head>

<body>

<!-- request.setAttribute("data","hello jsp!!")-->

<font color="red">${data}</font>

</body>

</html>

mvc主键配置类(视图解析器配置)

@Configuration

@ComponentScan("com.atguigu.jsp")

@EnableWebMvc

public class MvcConfig implements WebMvcConfigurer {

//加入组件;handlerMapping handlerAdapter json转换器------------使用@EnableWebMvc

/**

* 视图解析器,指定前后缀

* 步骤;

* 让配置类实现WebMvcConfigurer接口,重写接口中的configureViewResolvers()方法

* 通过registry对象调用jsp()方法,传入前缀与后缀

*/

@Override

public void configureViewResolvers(ViewResolverRegistry registry) {

//快速添加前后缀

registry.jsp("/WEB-INF/views/",".jsp");

}

}

springmvc的初始化类

* Spring-MVC环境搭建

* 要做的事情

* 指定mvc的配置类,创建ioc容器

* 配置拦截地址

*/

public class SpringMVCInit extends AbstractAnnotationConfigDispatcherServletInitializer {

@Override

protected Class<?>[] getRootConfigClasses() {

return new Class[0];

}

@Override

protected Class<?>[] getServletConfigClasses() {

return new Class[]{MvcConfig.class};

}

@Override

protected String[] getServletMappings() {

return new String[]{"/"};

}

}

测试方法 --- Controller

@Controller

@RequestMapping("jsp")

public class JspController {

@GetMapping("index")

public String index(HttpServletRequest request){

/**

* 快速查找视图

* 1 方法的返回值是字符串类型(在handler方法中返回视图的名字即可)

* 2 不能添加@ResponseBody 其意思是:直接返回字符串给浏览器,不找视图,不走视图解析器

*/

System.out.println("JspController.index");

//添加数据到request共享域

request.setAttribute("data","hello jsp!!");

return "index";

}

请求转发与响应重定向

/**

* 请求转发转发(从访问此方法跳转到访问另一个方法)

* 补充:

* 请求转发特点(背诵)

* + 请求转发通过HttpServletRequest对象获取请求转发器实现

* + 请求转发是服务器内部的行为,对客户端是屏蔽的

* + 客户端只发送了一次请求,服务端只产生了一对request和response对象,客户端地址栏不变

* + 服务端只产生了一对请求和响应对象,这一对请求和响应对象会继续传递给下一个资源

* + 因为全程只有一个HttpServletRequset对象,所以请求参数可以传递,请求域中的数据也可以传递

* + 请求转发可以转发给其他Servlet动态资源,也可以转发给一些静态资源以实现页面跳转

* + 请求转发可以转发给WEB-INF下受保护的资源(此时可以访问到这些受保护的资源,直接访问则不行)

* + 请求转发不能转发到本项目以外的外部资源

*

* 直接在此方法中返回目标资源的地址

* 注意;

* ①方法的返回值写成字符串

* ②不能添加responseBody注解(前提:配置类中要有视图解析器)

* ③返回的字符串前要加forward:/转发地址---------不然会被当成是那个视图的名称

*/

@GetMapping("forward")

public String forward(){

System.out.println("JspController.forward");

return "forward:/jsp/index";

}

/**

* 重定定向

* 补充:

* 响应重定向特点(背诵)

* 响应重定向通过HttpServletResponse对象的sendRedirect方法实现

* 响应重定向是服务端通过302响应码和路径,告诉客户端自己去找其他资源,是在服务端提示下的,客户端的行为 客户端至少发送了两次请求,客户端地址栏是要变化的

* 请求产生了多次后端就会有多个request对象

* + 服务端产生了多对请求和响应对象,且请求和响应对象不会传递给下一个资源

* + 因为全程产生了多个HttpServletRequset对象,所以请求参数不可以传递,请求域中的数据也不可以传递

* + 重定向可以是其他Servlet动态资源,也可以是一些静态资源以实现页面跳转

* + 重定向不可以到给WEB-INF下受保护的资源

* + 重定向可以到本项目以外的外部资源

*

* 注意;

* ①方法返回值写成字符串类型

* ②不能添加responseBody

* ③返回字符串的前面 redirect:/重定向的地址

*

*/

@GetMapping("redirect")

public String redirect(){

System.out.println("JspController.redirect");

return "redirect:/jsp/index";

}

* 重定向到第三发资源

*/

@GetMapping("redirect/baidu")

public String redirectBaidu(){

System.out.println("JspController.redirectBaidu");

return "redirect:http://www.baidu.com";

}

返回json数据(重点)

@Controller

@RequestMapping("json")

@ResponseBody//返回json的注解,添加到类和方法上(有直接返回字符串给前端,不要找视图解析器的意思)

//@RestController==@ResponseBody+@Controller

/**

* @ResponseBody 代表数据直接放入响应体返回,也不会走视图解析器

* 这样 快速查找视图,转发和重定向将都不生效

*/

public class JsonController {

@GetMapping("data")

public User data(){

User user = new User();

user.setName("dog");

user.setAge(2);

return user;//(对象会在handlerAdapter中被转化为json字符串)-----------前后端分离

//对象-》json {} 集合-》json [{},{}]

}

@RequestMapping("data2")

public List<User> data2(){

User user = new User();

user.setName("dog");

user.setAge(2);

List<User> users=new ArrayList<>();

users.add(user);

return users;

}

}

访问静态资源

在配置类中开启找静态资源的开关就可以访问静态资源了

/**

* 开启找静态资源的开关

* dispatcherServlet->handlerAdapter(找有没有对应的handler)-》没有->找有没有静态资源

* 如何开启:

* 在配置类中重写configureDefaultServletHandling方法,通过configurer调用enable()方法,开启找静态资源的开关

*/

@Override

public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {

configurer.enable();

}

restful风格设计

简介

补充;

http三个要点:

①Url【地址】

②请求方式:get,post,delete,put

③传递参数形式:param,json,path

几种请求方式的适用场景:

①get:从服务器读取数据(如查询,搜索,过滤)

②post:向服务器提交数据(如创建新资源,登录,文件上传)

③put:更新数据

④delete:删除服务器上指定资源

RestFul是Http协议的标准使用方案和风格

作用:

①教如何设计路径

②教如何设计参数传递

③教如何选择请求方式

特点

  1. 每一个URI代表1种资源(URI 是名词);

  2. 客户端使用GET、POST、PUT、DELETE 4个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源;

  3. 资源的表现形式是XML或者JSON;

  4. 客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息。

风格规范

1. HTTP协议请求方式要求

REST 风格主张在项目设计、开发过程中,具体的操作符合HTTP协议定义的请求方式的语义。

|--------|----------|
| |操作 | 请求方式| |
| |查询操作 | GET| |
| |保存操作 | POST| |
| |删除操作 | DELETE| |
| |更新操作 | PUT| |

2. URL路径风格要求

REST风格下每个资源都应该有一个唯一的标识符,例如一个 URI(统一资源标识符)或者一个 URL(统一资源定位符)。资源的标识符应该能明确地说明该资源的信息,同时也应该是可被理解和解释的!

使用URL+请求方式确定具体的动作,他也是一种标准的HTTP协议请求!

|------|-------------------------|----------------------------------|
| |操作 | 传统风格 | REST 风格|(路径传参) |
| |保存 | /CRUD/saveEmp | URL 地址:/CRUD/emp 请求方式:POST| |
| |删除 | /CRUD/removeEmp?empId=2 | URL 地址:/CRUD/emp/2 请求方式:DELETE| |
| |更新 | /CRUD/updateEmp | URL 地址:/CRUD/emp 请求方式:PUT| |
| |查询 | /CRUD/editEmp?empId=2 | URL 地址:/CRUD/emp/2 请求方式:GET| |

  • 路径参数应该用于指定资源的唯一标识或者 ID,而请求参数应该用于指定查询条件或者操作参数。

  • 请求参数应该限制在 10 个以内,过多的请求参数可能导致接口难以维护和使用。

  • 对于敏感信息,最好使用 POST 和请求体来传递参数。

  • 总结

根据接口的具体动作,选择具体的HTTP协议请求方式

路径设计从原来携带动标识,改成名词,对应资源的唯一标识即可!

restful风格好处

1 含蓄,安全

2 风格统一

3 无状态

4 严谨,规范

5 简洁,优雅

6 丰富的语义

其他扩展

全局异常处理机制

异常处理的两种方式:编程式异常处理;声明式异常处理

* 全局异常处理器(全局异常发生,会走此类写的handler)

* 发生异常,找全局异常处理器,--》@ExceptionHandler(指定异常)-->handler

* 指定的异常可以精准查找也可以找其父类

*

* 创建全局异常处理器的步骤:

* 要保证该类的注解会被扫描到

* ①创建一个全局异常处理类------------加上@ControllerAdvice/@RestControllerAdvice注解代表此类是全局异常处理类

* @ControllerAdvice-----------可以返回逻辑视图,转发和重定向的

* @RestControllerAdvice==@ControllerAdvice+@ResponseBody------直接返回json字符串,不走视图解析器

* ②写出现异常处理的方法

* @ExceptionHandler(异常类)--------代表此方法处理的是那种异常

* 在方法中定义该异常类的形参,在方法中定义异常处理的方法即可

代码举例:

//@ControllerAdvice//可以返回逻辑视图,转发和重定向的

//@RestControllerAdvice==@ControllerAdvice+@ResponseBody//直接返回json字符串,不走视图解析器

@RestControllerAdvice

public class GlobalExceptionHandler {

@ExceptionHandler(ArithmeticException.class)

public Object ArithmeticExceptionHandler(ArithmeticException e){

//自定义异常处理

String message = e.getMessage();

System.out.println("message = " + message);

return message;

}

@ExceptionHandler(Exception.class)

public Object ExceptionHandler(Exception e){

String message = e.getMessage();

System.out.println("message= "+ message);

return message;

}

}

拦截器的基本概念和作用:

Filter过滤器(应用场景:登录保护;设置编码格式;权限处理)-------------被过滤器保护的方法中一些重复的工作交由过滤器

拦截器的作用与过滤器的作用相似,不过它可以作用内部;拦截更细

作用位置:

拦截器 Springmvc VS 过滤器 javaWeb

  • 相似点

  • 拦截:必须先把请求拦住,才能执行后续操作

  • 过滤:拦截器或过滤器存在的意义就是对请求进行统一处理

  • 放行:对请求执行了必要操作后,放请求过去,让它访问原本想要访问的资源

  • 不同点

  • 工作平台不同

  • 过滤器工作在 Servlet 容器中

  • 拦截器工作在 SpringMVC 的基础上

  • 拦截的范围

  • 过滤器:能够拦截到的最大范围是整个 Web 应用

  • 拦截器:能够拦截到的最大范围是整个 SpringMVC 负责的请求内部

  • IOC 容器支持

  • 过滤器:想得到 IOC 容器需要调用专门的工具方法,是间接的

  • 拦截器:它自己就在 IOC 容器中,所以可以直接从 IOC 容器中装配组件,也就是可以直接得到 IOC 容器的支持

拦截器的基本使用

* 拦截器的使用步骤:

* ①创建一个拦截器类,实现HandlerInterceptor接口,

* ②实现三个方法,preHandle;postHandle;afterCompletion

* ③在配置类中将拦截器注入:

* 重写addInterceptors(InterceptorRegistry registry)方法

* 通过registry调用addInterceptor方法传入,拦截器类的对象

在配置类中

* @Override

* public void addInterceptors(InterceptorRegistry registry) {

* //配置方案1:拦截全部请求

* registry.addInterceptor(new MyInterceptor());

* }

拦截器类

java 复制代码
public class MyInterceptor implements HandlerInterceptor {

    /**

     * 执行handler之前调用的方法

     * 可以做的事情:编码格式设置,登陆保护,权限处理

     * @param request  请求对象

     * @param response  响应对象

     * @param handler   要调用的方法对象

     * @return  返回 true 放行  false 拦截

     * @throws Exception

     */

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        System.out.println("request = " + request + ", response = " + response + ", handler = " + handler);

        return true;

    }

    /**

     * handler执行完毕后触发的方法,没有拦截机制了,此方法只有 perHandler 返回true才会执行

     * 可以做的事情:对结果处理,敏感词汇检查

     * @param request

     * @param response

     * @param handler handler方法

     * @param modelAndView  返回的视图和共享域数据对象

     * @throws Exception

     */

    @Override

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

        System.out.println("MyInterceptor.postHandle");

    }

    /**

     * 整体处理完毕后执行的方法

     * @param request

     * @param response

     * @param handler

     * @param ex  handler报错的异常对象

     * @throws Exception

     */

    @Override

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        System.out.println("MyInterceptor.afterCompletion");

    }

}

拦截器配置细节

在配置类中;

/**

* 拦截器的注入

* @param registry

*/

@Override

public void addInterceptors(InterceptorRegistry registry) {

//配置方案1:拦截全部请求

registry.addInterceptor(new MyInterceptor());

//配置方案2:指定地址拦截 .addPathPatterns("user/data1")

//可以使用模糊符号 * 代表任意一层字符串 ** 代表任意多层字符串

registry.addInterceptor(new MyInterceptor()).addPathPatterns("user/data1");

//配置方案3:排除拦截 排除的地址应该在拦截的地址内部

registry.addInterceptor(new MyInterceptor()).excludePathPatterns("user/data2");

}

参数校验注解

什么是参数校验:

在 Web 应用三层架构体系中,表述层负责接收浏览器提交的数据,业务逻辑层负责数据的处理。为了能够让业务逻辑层基于正确的数据进行处理,我们需要在表述层对数据进行检查,将错误的数据隔绝在业务逻辑层之外。

参数校验是指在程序中对传入的参数进行检查,确保它们符合预期的格式,范围和约束条件的过程。

参数校验的主要目的:

①防止非法输入

②提高系统健壮性

③增强安全性

④明确错误原因

注解:

* 使用参数校验注解步骤:

* ①实体类属性添加校验注解

* ②handler接收参数时

* handler(@Validated 实体类 对象)

* 细节:对于param|json类型的参数都有效

* 不过对于json类型的要多添加一个注解

* handler(@Validated @RequestBody 实体类 对象)

*

* 出现的问题:如果,不符合校验规则,直接向前端抛出异常!

* 如何解决:

* 可以接收错误的绑定信息!自定义返回结果

* 捕捉错误绑定信息:

* 1 handler(校验对象,BindingResult result) 注意:BindingResult result一定要紧挨着校验对象

* 2 通过bindingResult对象获取绑定错误---------调用hasErrors()方法查看是否有错误

代码举例:

java 复制代码
@Controller

@ResponseBody

@RequestMapping("user")

public class UserController {

    //接收用户数据,用户有校验注解

    @PostMapping("register")

    public Object register(@Validated @RequestBody User user,BindingResult result){

        if (result.hasErrors()){

            //有绑定错误,就不直接返回,由我们自己决定返回什么

            Map data=new HashMap();

            data.put("code",400);

            data.put("msg","参数校验异常了!");

            return data;

        }

        System.out.println("user = " + user);

        return user;

    }

    //空指针异常

    @GetMapping("data1")

    public String data1(){

        String name=null;

        System.out.println("UserController.data1");

//        name.toString();

        return "ok";

    }

    @GetMapping("data2")

    //算数异常

    public String data2(){

        System.out.println("UserController.data2");

//        int i=1/0;

        return "ok";

    }

}

实体类:

/**

* 要求:

* 1 name 不为null或空字符串

* 字符串 @NotBlank 集合 @NotEmpty 包装 @NotNull

* 2 password 长度大于6

* 3 age 必须 >=1

* 4 email 邮箱格式的字符串

* 5 birthday 过去时间

*/

java 复制代码
public class User {

    @NotBlank

    private String name;

    @Length(min = 6)

    private String password;

    @Min(1)

    private int age;

    @Email

    private String email;

    @Past

    private Date birthday;

}

过滤器(Filter)、拦截器(Interceptor)与AOP的区别

这三者都是实现横切关注点(Cross-Cutting Concerns)的技术,但存在显著差异:

三者的核心区别

|--------------------|---------------------|----------------------|-------------------------------------|
| | 特性 | 过滤器(Filter) | 拦截器(Interceptor) | AOP(Aspect-Oriented Programming) | |
| | **作用层次** | Web容器层(Servlet规范) | MVC框架层(如Spring MVC) | 应用层(任何Java方法) | |
| | **作用对象** | | HTTP请求/响应 | | 控制器(Controller)方法 | | 任何方法(Service/Dao等) | |
| | **实现方式** | | 实现Filter接口 | | 实现框架特定接口 | | 通过切面(Aspect)定义 | |
| | **依赖框架** | | 不依赖(属于Servlet规范) | | 依赖MVC框架 | | 依赖AOP框架(如Spring AOP) | |
| | **执行粒度** | | 请求/响应级别 | | 请求处理流程级别 | | 方法调用级别 | |
| | **获取信息能力** | | 只能获取Servlet API | | 可获取控制器上下文 | | 可获取方法参数、返回值等 | |

相关推荐
上等猿13 分钟前
Elasticsearch笔记
java·笔记·elasticsearch
shuair2 小时前
01 - spring security自定义登录页面
java·后端·spring
东京老树根2 小时前
SAP 学习笔记 - 系统移行业务 - MALSY(由Excel 移行到SAP 的收费工具)
笔记·学习
逆风局?5 小时前
Spring-AOP-面相切面编程
java·后端·spring
严文文-Chris6 小时前
【MVC简介-产生原因、演变历史、核心思想、组成部分、使用场景】
mvc
傍晚冰川6 小时前
【STM32】最后一刷-江科大Flash闪存-学习笔记
笔记·科技·stm32·单片机·嵌入式硬件·学习·实时音视频
吴梓穆6 小时前
UE5学习笔记 FPS游戏制作33 游戏保存
笔记·学习·ue5
IT19956 小时前
uniapp笔记-自定义分类组件
前端·笔记·uni-app
〆、风神7 小时前
策略模式与元数据映射模式融合 JSR 380 验证规范实现枚举范围校验
windows·spring·策略模式
多多*7 小时前
JVM Java类加载 isInstance instanceof 的区别
开发语言·python·spring·ai作画·eclipse·maven