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 Class0;

}

@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 | | 可获取控制器上下文 | | 可获取方法参数、返回值等 | |

相关推荐
Sammyyyyy18 分钟前
结构化决策模型 Jev 实战笔记,3 个原语、4 个限制、5 个落地场景
人工智能·笔记·开发工具·编程语言
东方芷兰19 分钟前
Agent 技术摘要 05 —— BP、SaaS、B2B、C2B、B2C
java·笔记·python·语言模型·自然语言处理
Chen—LSN29 分钟前
数据结构——拿捏复杂度
java·c语言·开发语言·数据结构·经验分享·笔记·算法
步行cgn34 分钟前
Spring AOP 切面的先后顺序详解
java·spring
星恒随风1 小时前
Linux开发工具详解(一):软件包管理、Vim、GCC/G++、Makefile与进度条实战
linux·运维·笔记·学习·vim
小小、码农1 小时前
Ubuntu / Linux 软件安装完整笔记
linux·笔记·ubuntu
xzal121 小时前
Python 列表操作笔记:append / extend / + / pop
笔记·python
维克兜率天1 小时前
【维克】价差交易实战:配对交易的核心参数设置
大数据·笔记·算法·量化
Seraphina361 小时前
SQL注入(一)
数据库·经验分享·笔记·sql·网络安全
小尹哥-程序员1 小时前
第6集:RAG检索不准?3个参数调优路线图
人工智能·spring