Spring MVC面试题

news2025/7/19 6:20:25

什么是Spring MVC?简单介绍下你对Spring MVC的理解?

SpringMVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,把复杂的web应用分成逻辑清晰的表示层、控制层、业务层(服务层)、持久层,简化开发,减少出错,方便组内开发人员之间的配合。

Spring MVC的优点

1、可以支持各种视图技术,例如JSP、template等等;

2、与Spring框架集成

3、清晰的角色分配:

前端控制器(dispatcherServlet) ,

处理器映射器(handlerMapping)

处理器适配器(HandlerAdapter),

视图解析器(ViewResolver)。

5、 解耦合

6、支持Restful风格

Spring MVC的主要组件?

前端控制器【中央控制器】(DispatcherServlet):主要负责捕获来自客户端的请求和调度各个组件。

处理器映射器(HandlerMapping):根据url查找后端控制器Handler。

处理器适配器(HandlerAdapter):执行后端控制器(Handler),拿到后端控制器返回的结果ModelAndView后将结果返回给前端控制器DispatcherServlet。

后端控制器(处理器)(Handler):主要负责处理前端请求,完成业务逻辑,生成ModelAndView对象返回给HandlerAdapter。

视图解析器(ViewResolver):主要负责将从DispatcherServlet中拿到的ModelAndView对象进行解析,生成View对象返回给DispatcherServlet。

什么是DispatcherServlet

DispatcherServlet是前端控制器,它用来捕获所有的HTTP请求并分发到对应的后端控制器,这靠前端控制器一个组件是完成不了的,所以它还要调度处理器映射器来找到请求url对应的后端控制器,然后调度处理器适配器来调用对应的后端控制器来处理这个请求,所以前端控制器还有一个作用是调度其他组件来相互配合。这个控制器是不需要程序员写的

什么是Spring MVC框架的控制器?

SpringMVC的控制器分为前端控制器和后端控制器

DispatcherServlet是前端控制器,它用来捕获所有的HTTP请求并分发到对应的后端控制器,这靠前端控制器一个组件是完成不了的,所以它还要调度处理器映射器来找到请求url对应的后端控制器,然后调度处理器适配器来调用对应的后端控制器来处理这个请求,所以前端控制器还有一个作用是调度其他组件来相互配合。这个控制器是不需要程序员写的

后端控制器就是自己定义的控制层里的类(带有@Controller的类)

Spring MVC的控制器是不是单例模式?如果是,有什么问题?怎么解决?

答:是单例模式,所以在多线程访问的时候有线程安全问题

如果要保证Controller的线程安全,有以下解决办法:

1、尽量不要在 Controller 中定义成员变量;

2、如果必须要定义一个非静态成员变量,那么可以通过注解 @Scope(“prototype”),将Controller设置为多例模式。

Controller
@Scope(value="prototype")
public class TestController {
    private int num = 0;
    
    @RequestMapping("/addNum")
    public void addNum() {
        System.out.println(++num);
    }
}

简而言之就是最好不要在控制器里定义成员变量,因为它是线程不安全的,如果非要定义那就把单例模式改成多例模式

请描述Spring MVC的工作流程?描述一下 DispatcherServlet 的工作流程?

1、用户点击某个请求路径,发起一个request请求,此请求会被前端控制器处理。

2、前端控制器请求处理器映射器去查找Handler。可以依据注解或者XML配置去查找。

3、处理器映射器根据配置找到相应的Handler(可能包含若干个Interceptor拦截器),返回给前端控制器。

4、前端控制器请求处理器适配器去执行相应的Handler处理器(常称为Controller)。

5、处理器适配器执行Handler处理器。

6、Handler处理器执行完毕之后会返回给处理器适配器一个ModelAndView对象(SpringMVC底层对象,包括Model数据模型和View视图信息)。

7、处理器适配器接收到Handler处理器返回的ModelAndView后,将其返回给前端控制器。

8、前端控制器接收到ModelAndView后,会请求视图解析器(ViewResolver)对视图进行解析。

9、视图解析器根据View信息匹配到相应的视图结果,反馈给前端控制器。

10、前端控制器收到View具体视图后,进行视图渲染,将Model中的模型数据填充到View视图中的request域,生成最终的视图(View)。

11、前端控制器向用户返回请求结果。

组件说明:

以下组件通常使用框架提供实现:

DispatcherServlet:作为前端控制器,整个流程控制的中心,控制其它组件执行,统一调度,降低组件之间的耦合性,提高每个组件的扩展性。HandlerMapping:通过扩展处理器映射器实现不同的映射方式,例如:配置文件方式,实现接口 方式,注解方式等。

HandlAdapter:通过扩展处理器适配器,支持更多类型的处理器。

ViewResolver:通过扩展视图解析器,支持更多类型的视图解析,例如:jsp、freemarker、pdf、excel等。

组件:

1、前端控制器DispatcherServlet(不需要工程师开发),由框架提供 作用:接收请求,响

应结果,相当于转发器,中央处理器。有了dispatcherServlet减少了其它组件之间的耦合度。 用户 请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它 调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。

2、处理器映射器HandlerMapping(不需要工程师开发),由框架提供 作用:根据请求的url查找 Handler HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射 器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

3、处理器适配器HandlerAdapter 作用:按照特定规则(HandlerAdapter要求的规则)去执行 Handler 通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

4、处理器Handler(需要工程师开发) 注意:编写Handler时按照HandlerAdapter的要求去做, 这样适配器才可以去正确执行Handler Handler 是继DispatcherServlet前端控制器的后端控制器, 在DispatcherServlet的控制下Handler对具体的用户请求进行处理。 由于Handler涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发Handler。

5、视图解析器View resolver(不需要工程师开发),由框架提供 作用:进行视图解析,根据逻辑视图名解析成真正的视图(view) View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。 springmvc框架提供了很多的View视图类型,包括:jstlView、 freemarkerView、pdfView等。 一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由工程师根据业务需求开发具体的页面。

6、视图View(需要工程师开发jsp...) View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf...)

核心架构的具体流程步骤如下:

1、首先用户发送请求——>DispatcherServlet,前端控制器收到 请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控

制;

2、DispatcherServlet——>HandlerMapping, HandlerMapping 将会把请求映射为

HandlerExecutionChain 对象(包含一个Handler 处理器(页面控制器)对象、多个

HandlerInterceptor 拦截器)对象,通过这种策略模式,很容易添加新的映射策略;

3、 DispatcherServlet——>HandlerAdapter,HandlerAdapter 将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;

4、 HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter 将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView 对象(包含模型数据、逻辑视图名);

5、ModelAndView的逻辑视图名——> ViewResolver, ViewResolver 将把逻辑视图

名解析为具体的View,通过这种策略模式,很容易更换其他视图技术; 6、View——>渲染,View 会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支 持其他视图技术;

7、返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到

此一个流程结束。

看到这些步骤我相信大家很感觉非常的乱,这是正常的,但是这里主要是要大家理解springMVC中的几个组件:

前端控制器(DispatcherServlet):接收请求,响应结果,相当于电脑的CPU。

处理器映射器(HandlerMapping):根据URL去查找处理器。

处理器(Handler):需要程序员去写代码处理逻辑的。

处理器适配器(HandlerAdapter):会把处理器包装成适配器,这样就可以支持多种类型的处理器,类比笔记本的适配器(适配器模式的应用)。

视图解析器(ViewResovler):进行视图解析,多返回的字符串,进行处理,可以解析成对应的页面。

MVC是什么?MVC设计模式的好处有哪些

mvc是一种设计模式,mvc分别是模型(model)-视图(view)-控制器(controller)三层架构的设计模式。它实现前端页面的展现与后端业务数据处理的分离。

mvc设计模式的好处

通过MVC模式可以把这些对象、显示、控制分离来提高软件的的灵活性和复用性,这样使得后期维护更加容易,同时重用性高,多个视图共享一个模型,用一个模型就可以处理,这样最大化重用代码(简单来说就是解耦合,便于后期的维护和扩展)

注解原理是什么

注解本质是一个继承了Annotation接口的特殊接口,当你用注解修饰某个元素,编译器将在编译期扫描每个类或者方法上的注解,会做一个基本的检查,你的这个注解是否允许作用在当前位置,如果允许的话就会将注解信息写入元素的属性表。当通过反射获取注解时,虚拟机将所有生命周期在 RUNTIME 的注解取出来放到memberValues这个map 中,并创建一个 AnnotationInvocationHandler 实例,把这个 map 传递给它。最后,虚拟机将采用 JDK 动态代理机制生成一个目标注解的代理类对象,并初始化好处理器。通过代理对象调用注解的方法时,最终调用AnnotationInvocationHandler 的invoke方法。该方法会从memberValues 这个Map 中索引出对应的值并设置进使用这个注解的元素中去(memberValues,它是一个 Map 键值对,键是我们注解属性名称,值就是该属性当初被赋上的值,memberValues 的来源是Java 常量池),一句话概括就是,通过方法名返回注解属性值。

扩展

AnnotationInvocationHandler 是 JAVA 中专门用于处理注解的 Handler, 这个类的设计也非常有意思。

这里有一个 memberValues,它是一个 Map 键值对,键是我们注解属性名称,值就是该属性当初被赋上的值。

而这个 invoke 方法就很有意思了,大家注意看,我们的代理类代理了 Hello 接口中所有的方法,所以对于代理类中任何方法的调用都会被转到这里来。

var2 指向被调用的方法实例,而这里首先用变量 var4 获取该方法的简明名称,接着 switch 结构判断当前的调用方法是谁,如果是 Annotation 中的四大方法,将 var7 赋上特定的值。如果当前调用的方法是 toString,equals,hashCode,annotationType 的话,AnnotationInvocationHandler 实例中已经预定义好了这些方法的实现,直接调用即可。

那么假如 var7 没有匹配上这四种方法,说明当前的方法调用的是自定义注解字节声明的方法,这种情况下,将从我们的注解 map 中获取这个注解属性对应的值。一句话概括就是,通过方法名返回注解属性值。

好文参考:JAVA 注解的基本原理 - Single_Yam - 博客园

好文参考:注解机制及其原理什么是注解注解的使用注解的原理 - 腾讯云开发者社区-腾讯云

Spring MVC常用的注解有哪些?

@RequestMapping

RequestMapping是一个用来处理请求地址映射的注解,即指明处理器可以处理哪些URL请求,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。

@RequestBody

注解实现接收http请求的json数据,将json转换为java对象。

@ResponseBody

将方法的返回值,以特定的格式写入到response的body区域,进而将数据返回给客户端。

  • 当方法上面没有写ResponseBody,底层会将方法的返回值封装为ModelAndView对象。
  • 如果返回值是字符串,那么直接将字符串写到客户端;
  • 如果是一个对象,会将对象转化为json串,然后写到客户端。

SpingMVC中的控制器的注解一般用哪个?有没有别的注解可以替代?

答:一般用@Controller注解,也可以使用@RestController,@RestController注解相当于@ResponseBody + @Controller,表示是表现层,除此之外,一般不用别的注解代替。

@Controller注解的作用

在一个类上添加@Controller注解,表明了这个类是一个控制器类,可以支持同时处理多个请求。Spring使用扫描机制查找应用程序中所有用了这个注解的控制器类。分发处理器会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping注解,而使用@RequestMapping注解的方法才是真正处理请求的处理器

注意:这里注意@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器。单单使用@Controller 标记在一个类上还不能真正意义上的说它就是Spring MVC 的一个控制器类,因为这个时候Spring 还不认识它。那么要如何做Spring 才能认识它呢?这个时候就需要我们把这个控制器类交给Spring 来管理。

有两种方式:

1、在spring的配置文件中创建该类的实例

<bean class="test.controller.MyController" />

上述这种方式是在spring容器中注入单个bean,当项目比较大,控制器类比较多时,用这种方式向Spring容器中注入bean非常的让人苦恼,索性有第二种方式。

2、在Spring MVC的配置文件中,使用<context:component-scan base-scan=""/>元素,该元素的功能为:启动包扫描功能,以便注册带有@Controller,@Service,@repository,@Component等注解的类成为Spring的Bean。

<context:component-scan base-scan="test.controller" />

这种方式会扫描指定包中的所有类,并生成相应的bean注入到spring容器中。使用这种方式当然能够极大提高我们的开发效率,但是有时候我们不想某一类型的类注入到spring容器中。可以通过下面的方法解决

<context:component-scan base-package="test" >
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>

上述代码表示扫描test包中除有@Service注解之外的类。

@RequestMapping注解的作用

RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

RequestMapping注解有六个属性如下所示

value: 指定请求的实际地址,指定的地址可以是URI Template 模式(后面将会说明);

method: 指定请求的method类型,GET、POST、PUT、DELETE等 ;

@RequestMapping(value="/toLogin",method = RequestMethod.GET)

consumes: 指定处理request请求中Content-Type为某种类型的请求,例如application/json, text/html;

produces: 指定处理request请求中Accept头中包含了了某种类型的请求,并且表明返回的内容类型是这种类型,例如application/json, text/html;

/**
* consumes 标识处理request请求中Content-Type为“application/json”类型的请求.
* produces标识处理request请求中Accept头中包含了"application/json"的请求.
* 同时暗示了返回的内容类型为application/json;
*/
  @ApiOperation(value = "保存用户")
  @PostMapping(value = "/execute",produces = MediaType.APPLICATION_JSON_VALUE,consumes = MediaType.APPLICATION_JSON_VALUE)
  public String saveUser(@RequestBody User userl){
        //TO DO
        return "保存成功";
  }

params: 限定要处理请求的参数,只有匹配该参数的请求,才会被该方法处理;

 @GetMapping(value = "list",params="version=1")  
    public Object list() {
        return "ok";  
    }  

说白了就是请求url中必须带指定参数才会被这个方法处理

例如 localhost:8080/api/v1/books2/12?version=1这样的请求才会被这个方法处理

headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。

其实就是用来匹配特定的请求头

  @RequestMapping(
            value = "/testParamsAndHeaders",
            params = {"username","password!=123456"},
            headers = {"Host=localhost:8080"}
    )
    public String testParamsAndHeaders(){
        return "success";
    }

@ResponseBody注解的作用和原理

将方法的返回值,以特定的格式写入到response的body区域,进而将数据返回给客户端。

  • 当方法上面没有写ResponseBody,底层会将方法的返回值封装为ModelAndView对象。
  • 当方法上面写ResponseBody,如果返回值是字符串,那么直接将字符串写到客户端;
  • 当方法上面写ResponseBody,如果是一个对象,会将对象转化为json串,然后写到客户端。

这里需要注意的是,如果返回对象,按utf-8编码。

如果返回String,默认按iso8859-1编码,页面可能出现乱码。因此在注解中我们可以手动修改编码格式,例如@RequestMapping(value="/cat/query",produces="text/html;charset=utf-8"),前面是请求的路径,后面是编码格式。

原理

其实是通过HttpMessageConverter中的方法实现的,它本是一个接口,在其实现类完成转换。【Converter是转换器】

如果是bean对象,会调用对象的getXXX()方法获取属性值并且以键值对的形式进行封装,进而转化为json串。

@PathVariable和@RequestParam(pei4 rui3 嗯)的区别

1、用法上的不同: 从名字上可以看出来,PathVariable只能用于接收url路径上的参数,而RequestParam只能用于接收请求参数(params )看下面一个例子:

package com.lrm.springbootdemo.web;

import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/v1")
public class HelloController {
    
    @GetMapping("/books/{username}")
    public Object testPathVariable(@PathVariable String username){
        Map<String,Object> map = new HashMap<>();
        map.put("username",username);
        return map;
    }
    
    @PostMapping("/books2")
    public Object testRequestParam(@RequestParam("name") String name,
                                   @RequestParam("author") String author,
                                   @RequestParam("isbn") String isbn) {
        Map<String, Object> book = new HashMap<String, Object>();
        book.put("name", name);
        book.put("author", author);
        book.put("isbn", isbn);
        return book;
    }
    
    
    @PostMapping("/books2/{id}")
    public Object test(@PathVariable("id") long id,@RequestParam("name") String name,
                       @RequestParam("author") String author,
                       @RequestParam("isbn") String isbn) {
        Map<String, Object> book = new HashMap<String, Object>();
        book.put("id",id);
        book.put("name", name);
        book.put("author", author);
        book.put("isbn", isbn);
        return book;
    }
}

其中testPathVariable这个方法中的username参数只能使用@PathVariable来接收,因为username参数是url的path上携带的参数。username是无法使用RequestParam来接受的。

testRequestParam这个方法只能用于 localhost:8080/api/v1/books2/12?name=java in action&author=ric&isbn=dsdas2334 这种模式的请求,因为RequestParam只能用于接收请求上带的params,testPathVariable是无法接收上面的name、author、isbn参数的。

骚戴理解

localhost:8080/api/v1/books2/12?name=java in action&author=ric&isbn=dsdas2334

这个请求路径中?后面带的参数就用@RequestParam注解接收, localhost:8080/api/v1/books2/12的这个12其实就是id,也就是后端的/books2/{id}这个中的id,所以这个请求路径的参数就是用@PathVariable来接收

2、内部参数不同 PathVariable有value,name,required这三个参数,而RequestParam也有这三个参数,并且比PathVariable多一个参数defaultValue(该参数用于当请求体中不包含对应的参数变量时,参数变量使用defaultValue指定的默认值)

3、PathVariable一般用于get和delete请求,RequestParam一般用于post请求。

Spring MVC中函数的返回值是什么?

SpringBoot并不能处理以及响应客户端的请求,最终还是要依赖SpringMVC框架,所以接下来介绍SpringMVC Controller方法的返回值类型,涵盖所有返回值类型。总共有四种返回值

1. ModelAndView

我们在使用SpringMVC的时候,经常返回ModelAndView类型,现在前后端分离后,后端都是返回JSON格式数据为主。

返回 ModelAndView类型,我们可以在ModelAndView对象中指定视图名称,然后也可以绑定数据,如下面代码:

@RequestMapping("/userList")
public ModelAndView getAllUser(ModelAndView mv) {
    List<User> users= userService.getAllUser();
    //添加数据模型到request域中
    mv.addObject("users", users);
    mv.setViewName("userList");//指定视图名
    return mv;
}

2.void

如果返回值为void的话,并不是真正没有返回值,而是会出现以下几种情况:

(1)如果方法内真的没有返回值,那么SpringMVC默认把deleteUser(映射的URL)当成视图名称来解析,如果存在该视图,就返回给客户端;如果不存在该视图,就会报视图找不到异常。

@RequestMapping("/deleteUser")
public void deleteUser() {
	//删除操作
}

通过加@ResponseBody来修改默认行为,加上该注解表示返回JSON数据,这里返回空JSON数据,而不是把URL当成视图名称来解析

@RequestMapping("/deleteUser")
@ResponseBody
public void deleteUser() {
	//删除操作
}

(2)请求转发

@GetMapping("/")
public void root(HttpServletRequest req,HttpServletResponse resp) {
    req.getRequestDispatcher("/WEB-INF/jsp/index.jsp").forward(req,resp);
}

req.getRequestDispatcher("/WEB-INF/jsp/index.jsp").forward(req,resp);

(3)重定向

@RequestMapping("/")
@ResponseBody
public void root(HttpServletResponse resp){
    resp.setStatus(302);
    resp.addHeader("Location","/WEB-INF/jsp/index.jsp");
}

@RequestMapping("/")
@ResponseBody
public void root(HttpServletResponse resp){
    resp.sendRedirect("/WEB-INF/jsp/index.jsp");
}

resp.sendRedirect("/WEB-INF/jsp/index.jsp");

3. String

当方法的返回值为String的时候,也会出现下面几种情况:

(1)逻辑视图名
返回String最常见的是逻辑视图名,这种时候一般利用默认的参数Model来传递数据

@RequestMapping("/deleteUser")
//方法返回JSON数据
@ResponseBody	
public String deleteUser(Model model) {
	model.addAttribute("msg","删除成功");
	return "userList";
}

(2)重定向
登录失败的时候重定向到登录页面。

@RequestParam("/login")
public String redirect(User user){
    if{
        //登录成功...
    }else{
        //登录失败,重定向到登录页面
        return "redirect:tologin";
    }
}

return "redirect:tologin";

(3)请求转发
登录失败的时候请求转发到登录页面。

@RequestParam("/login")
public String redirect(User user){
    if{
        //登录成功...
    }else{
        //登录失败,转发到登录页面
        return "forward:tologin";
    }
}

return "forward:tologin";

(4)真的返回String,相当于JSON格式的数据

@RequestMapping("/deleteUser")
@ResponseBody
public String deleteUser() {
	try{
		//删除成功
		return "删除成功";
	}catch(Exception e){
		return "删除失败";
	}
}

4.JSON

现在前后端分离的情况下,大部分后端只需要返回JSON数据即可,List 集合、Map集合,实体类等都可以返回,这些数据由 HttpMessageConverter自动转为JSON ,如果使用了Jackson或者Gson,不需要额外配置就可以自动返回JSON了,因为框架帮我们提供了对应的HttpMessageConverter ,如果使用了Alibaba的Fastjson的话,则需要自己手动提供一个相应的 HttpMessageConverter的实例,方法的返回值如下面代码:

@GetMapping("/getUser")
@ResponseBody
public User getUser() {
    User user = userService.getUser();
    return user;
}

@RequestMapping("/userList")
@ResponseBody
public List<User> getAllUser() {
    List<User> users = userService.getAllUser();
    return users;
}

Spring MVC怎么样设定重定向和转发的?

一般情况下,控制器方法返回字符串类型的值会被当成逻辑视图名处理。如果返回的字符串中带 forward: 或 redirect: 前缀时,SpringMVC 会对他们进行特殊处理:将 forward: 和redirect: 当成指示符,其后的字符串作为 URL 来处理。

(1)转发:在返回值前面加"forward:",譬如"forward:user.do?name=method4"

@RequestMapping("/springMvc")
@Controller
public class handler {
    //测试转发
    @RequestMapping("/testRedirect")
    public String testRedirect(){
        System.out.println("testRedirect");
        return "forward:/index.jsp";
    }
 
}

(2)重定向:在返回值前面加"redirect:",譬如"redirect:http://www.baidu.com"

@RequestMapping("/springMvc")
@Controller
public class handler {
    //测试重定向
    @RequestMapping("/testRedirect")
    public String testRedirect(){
        System.out.println("testRedirect");
        return "redirect:/index.jsp";
    }
 
}

Spring MVC怎么和AJAX相互调用的?

在方法上加@ResponseBody注解

把原来Controller类上的@Controller注解替换为@RestController注解。@RestController = @Controller + @ResponseBody,表明该Controller类所有的方法都返回JSON格式的数据(没有加@RequestMapping注解的方法除外)。

加入@ResponseBody注解就能返回JSON格式数据的原因是:SpringMVC提供的HttpMessageConverter自动转为JSON ,如果使用了Jackson或者Gson,不需要额外配置就可以自动返回JSON了,因为框架帮我们提供了对应的HttpMessageConverter ,如果使用了Alibaba的Fastjson的话,则需要自己手动提供一个相应的 HttpMessageConverter的实例。

使用@ResponseBody 注解把Java里面的对象直接转化成Js可以识别的Json对象。

【简化返回的数据转化成json串并且通过response响应的回客户端过程】

具体步骤如下

1、导入jackson的依赖

  <!--jackson相关依赖 Springmvc@responsBody注解使用jackSon-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.11.3</version>
    </dependency>

2、在方法前面要加上@ResponseBody 注解。

如何解决POST请求中文乱码问题,GET的又如何处理呢?

1、解决post请求乱码问题

针对Post方式提交的请求如果出现乱码,可以每次在request解析数据时设置编码格式:

request.setCharacterEncoding("utf-8");

也可以使用CharacterEncodingFilter编码过滤器来解决,最常用的方法是使用spring提供的编码过滤器:

在Web.xml中增加如下配置(要注意的是它的位置一定要是第一个执行的过滤器):

<filter>
  <filter-name>CharacterEncodingFilter</filter-name>
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  
  <init-param>
    <param-name>encoding</param-name>
    <param-value>utf-8</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>CharacterEncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

该过滤器要做的其实就是强制为所有请求和响应设置编码格式:

request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");

2、get请求中文参数出现乱码解决方法有两个

①修改tomcat配置文件添加编码与工程编码一致

<Connector URIEncoding="UTF-8" 
           port="8080"   maxHttpHeaderSize="8192"
           maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
           connectionTimeout="20000" disableUploadTimeout="true" />

只需增加 URIEncoding="UTF-8" 这一句,然后重启tomcat即可。

②在Servlet中使用如下的代码来得到中文请求参数

String name = new String(request.getParameter("name").getBytes("ISO-8859-1"), "utf-8");

ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码。

3、POST和GET乱码的原因

前端使用POST或者GET方法传递的参数一般使用浏览器预先设置的编码方式进行编码,中文浏览器一般是使用UTF8或者GBK,英文的一般是ISO编码;而浏览器编码完成后发送给服务器,服务器进行解码的解码方式默认是使用ISO8859-1。这就造成了编码和解码方式不统一的,进而出现了中文乱码的问题。

Spring MVC的异常处理?

1. @ExceptionHandler

使用该注解有一个不好的地方就是:进行异常处理的方法必须与出错的方法在同一个Controller里面。使用如下:

@Controller
public class GlobalController {

   /**
     * 局部异常
     * @return
     */
    @ExceptionHandler({MyException.class})
    public String exception(MyException e) {
        System.out.println(e.getMessage());
        e.printStackTrace();
        return "exception";
    }

    @RequestMapping("test")
    public void test() {
        throw new MyException("出错了!");
    }
}

可以看到,这种方式最大的缺陷就是不能全局控制异常。每个类都要写一遍。简单来说就是局部异常处理器就是写在某个控制器里的一个方法,也就是这个控制器出现的任何异常都会执行这个局部异常处理器,但是其他控制器出现异常不会执行这个局部异常处理器

2. HandlerExceptionResolver

Spring MVC 通过 HandlerExceptionResolver 处理程序异常,包括处理器异常、数据绑定异常以及控制器执行时发生的异常。HandlerExceptionResolver 仅有一个接口方法,源码如下。

public interface HandlerExceptionResolver {
    ModelAndView resolveException(HttpServletRequest var1, 
                                  HttpServletResponse var2, 
                                  Object var3, 
                                  Exception var4);
}

发生异常时,Spring MVC 会调用 resolveException() 方法,并转到 ModelAndView 对应的视图中,返回一个异常报告页面反馈给用户。

创建一个 HandlerExceptionResolver 接口的实现类 MyExceptionHandler,代码如下。

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
 

@Component
public class MyExceptionHandler implements HandlerExceptionResolver {
    //定义一个全局异常处理器
    public ModelAndView resolveException(HttpServletRequest httpServletRequest,
                                         HttpServletResponse httpServletResponse,
                                         Object o,
                                         Exception e) {
        Map<String, Object> model = new HashMap<String, Object>();
        model.put("errorMessage", "程序运行出错");
        //根据不同错误转向不同页面(统一处理),即异常与View的对应关系
        if (e instanceof ArithmeticException) {
            return new ModelAndView("error", model);
        }
        return new ModelAndView("other_error", model);
    }
}

3. 使用 @ControllerAdvice+ @ ExceptionHandler 注解

上文说到 @ ExceptionHandler 需要进行异常处理的方法必须与出错的方法在同一个Controller里面。那么当代码加入了 @ControllerAdvice,则不需要必须在同一个 controller 中了。这也是 Spring 3.2 带来的新特性。从名字上可以看出大体意思是控制器增强。 也就是说,@controlleradvice + @ ExceptionHandler 也可以实现全局的异常捕捉。请确保此ExceptionAspect类能被扫描到并装载进 Spring 容器中。

// 全局的异常处理器类
@ControllerAdvice
public class ExceptionAspect {
    //定义异常类型
    @ExceptionHandler(Exception.class)
    public String exception(Exception e) {
        System.out.println("全局异常处理方法..." + e.getMessage());
        return "error";
    }
}

也就是定义一个全局处理器,所有控制器的异常都交给这处理器来处理

如果在拦截请求中,我想拦截get方式提交的方法,怎么配置

1、可以在@RequestMapping注解里面加上method=RequestMethod.GET。

@RequestMapping(value="/toLogin",method = RequestMethod.GET)
public ModelAndView toLogin(){}

2、可以使用@GetMapping注解。

@GetMapping(value="/toLogin")
public ModelAndView toLogin(){}

22、怎样在方法里面得到Request或者Session?

将HttpServletRequest作为Spring MVC 的方法参数传入。然后通过request间接的获取到session。

public void getSessionAction(HttpServletRequest request){   
    HttpSession session = request.getSession(); 
}

如果想在拦截的方法里面得到从前台传入的参数,怎么得到?

直接在控制器方法的形参里面声明这个参数就可以,但名字必须和传过来的参数名称一样,否则参数映射失败。
下面方法形参中的userId,就会接收从前端传来参数名称为userId的值。

@RequestMapping("/deleteUser")
public void deleteUser(Long userId){
	//删除用户操作...
}

如果前台有很多个参数传入,并且这些参数都是一个对象的,那么怎么样快速得到这个对象?自动注入实现原理是什么?

直接在控制器方法的形参里面声明这个参数就可以,Springmvc就会自动会请求参数赋值到这个对象的属性中。
下面方法形参中的user用来接收从前端传来的多个参数,参数名称需要和User实体类属性名称一致。

@RequestMapping("/saveUser")
public void saveUser(User user){
	//保存用户操作...
}

@Data
public class User {
    private Long userId;
    private String username;
    private String password;
    //...
}

骚戴讲解:一般常用于前端表单的提交,后端把前端的表单提交的参数封装成实体类对象作为形参来接受前端传来的数据,这里会自动把表单里的数据一一对应的注入到这个实体类中去,实现原理就是调用了这个实体类的构造方法来进行初始化,也就是自动注入

Spring MVC用什么对象从后台向前台传递数据的?

1、使用Model对象(常用)

(1)使用model对象往前台传递数据 attribute

(2)在jsp中接收从后台传递过来的参数

2、使用HttpServletRequest对象

(1)使用HttpServletRequest对象往前台传递数据

(2)jsp中接收,同上!!!

3、使用Map对象

把Model改成Map即可。

怎么样把ModelMap里面的数据放入Session里面?

在控制器头部使用@SessionAttributes("user")注解,里面包含的字符串就是要放入session里面的key。

1、实体类

public class User {

	private int id;            //递增主键
	private String username;   //用户账号
	private String password;   //用户密码
	//省略set和get方法
}

2、控制器头部

@SessionAttributes("user")
@Controller
@RequestMapping("/login")
public class LoginController {
	//省略代码
}

在控制器头部使用@SessionAttributes("user")注解,里面包含的字符串就是要放入session里面的key。

3、控制器方法

@RequestMapping(method=RequestMethod.POST)
	public String login(ModelMap modelMap,String username)
	{
		User user=userService.getUser(username);
		modelMap.addAttribute("user", user);
      	       return "index";
	}

4、在jsp页面获取session中的值

${ sessionScope.user.id }

5、注意事项:@SessionAttributes(“user”)必须和modelMap.addAttribute(“user”, user)以及${ sessionScope.user.id }中"user"相同。id是User实体类的属性。

Spring MVC里面拦截器是怎么写的

有两种写法

  • 一种是实现HandlerInterceptor接口
  • 另外一种是继承适配器类,接着在接口方法当中,实现处理逻辑;

然后在Spring MVC的配置文件中配置拦截器即可:

1、定义自定义的拦截器来实现HandlerInterceptor(in4 t sai3 pu3 te3)接口

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。

常见应用场景

日志记录

权限检查

性能监控

通用行为:例如读取用户cookie

(1)实现自定义的拦截器

package net.biancheng.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class TestInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle方法在控制器的处理请求方法调用之前执行");
        return false;
    }
        @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法在控制器的处理请求方法调用之后,解析视图之前执行");
    }
        @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("afterCompletion方法在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行");

    }
}

拦截器一个有3个回调方法

  • preHandle:预处理回调方法,实现处理器的预处理(如登录检查), Object handler为响应的处理器返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
  • postHandle:后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
  • afterCompletion:整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中preHandle返回true的拦截器才会执行afterCompletion。

(2)在Spring的配置文件中添加如下配置

<!-- 配置拦截器 -->
<mvc:interceptors>
    <!-- 配置一个全局拦截器,拦截所有请求 -->
    <bean class="net.biancheng.interceptor.TestInterceptor" /> 
    <mvc:interceptor>
        <!-- 配置拦截器作用的路径 -->
        <mvc:mapping path="/**" />
        <!-- 配置不需要拦截作用的路径 -->
        <mvc:exclude-mapping path="" />
        <!-- 定义<mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截 -->
        <bean class="net.biancheng.interceptor.Interceptor1" />
    </mvc:interceptor>
    <mvc:interceptor>
        <!-- 配置拦截器作用的路径 -->
        <mvc:mapping path="/gotoTest" />
        <!-- 定义在<mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截 -->
        <bean class="net.biancheng.interceptor.Interceptor2" />
    </mvc:interceptor>
</mvc:interceptors>

在上述示例代码中,元素说明如下。

<mvc:interceptors>:该元素用于配置一组拦截器。

<bean>:该元素是 <mvc:interceptors> 的子元素,用于定义全局拦截器,即拦截所有的请求。

<mvc:interceptor>:该元素用于定义指定路径的拦截器。

<mvc:mapping>:该元素是 <mvc:interceptor> 的子元素,用于配置拦截器作用的路径,该路径在其属性 path 中定义。path 的属性值为/**时,表示拦截所有路径,值为/gotoTest时,表示拦截所有以/gotoTest结尾的路径。如果在请求路径中包含不需要拦截的内容,可以通过 <mvc:exclude-mapping> 子元素进行配置。
需要注意的是,<mvc:interceptor> 元素的子元素必须按照 <mvc:mapping.../>、<mvc:exclude-mapping.../>、<bean.../> 的顺序配置。

2、拦截器适配器

有时候我们可能只需要实现三个回调方法中的某一个,如果实现HandlerInterceptor 接口的话,三个方法必须实现

spring 提供了一个HandlerInterceptorAdapter 适配器(适配器模式),允许我们只实现需要的回调方法。

在HandlerInterceptorAdapter中,对HandlerInterceptor 接口中的三个方法都进行了空实现,其中preHandle方法的返回值,默认是true

(1)实现自定义的拦截器

//自定义拦截器
public class MyHandlerInterceptor extends HandlerInterceptorAdapter {

	//Controller控制器执行之前执行该方法
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println("MyHandlerInterceptor preHandle");
		
		//表示是否继续执行请求,是否放行HandlerAdapter的请求
		return true;
	}

	//Controller控制器执行之后执行该方法,多了一个ModelAndView对象
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("MyHandlerInterceptor postHandle");
		
	}

	//视图渲染之后执行
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("MyHandlerInterceptor afterCompletion");
		
	}	
}

(2)在Spring的配置文件中添加如下配置

<!-- 拦截器:针对HandlerMapping配置 -->
	<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
	    <property name="interceptors">
	         <list>
	              <ref bean="myInterceptor1"></ref>
	              <ref bean="myInterceptor2"></ref>
	         </list>
	    </property>
	</bean>
	<bean id="myInterceptor1" class="com.interceptor.MyInterceptor1"></bean>
	<bean id="myInterceptor2" class="com.interceptor.MyInterceptor2"></bean>

Bean容器中的拦截器是单例模式:因此不管多少用户请求多少次都只有一个拦截器对象,即线程不安全。

拦截器中使用ThreadLoacl,ThreadLocal类型是一个线程变量,其并不是用来像lock/sychronized一样解决java多线程中变量共享的安全性问题的,并且使用ThreadLocal类型变量并不一定能保证共享对象上的安全并发(放入Thread类的变量ThreadLocalMap threadLocals中的对象即value值,必须是在当前线程方法中所创建的局部变量,或者是在其他地方正确发布的线程安全对象。否则,若放入的对象为多个线程所共享的非线程安全对象,那么就会造成并发问题)。

ThreadLocal类型变量最大的用途是用来存储线程内部的变量,使得在线程的方法调用栈上的任何方法中,都可以通过ThreadLocal变量的get方法得到该ThreadLocal类型为key对应的value值。

//计算渲染时间的拦截器
public class TimeInterceptor implements HandlerInterceptor {
    
    //每一个thread线程都有自己的TreadLocal,当前thread共享一个threadLocal
    // 每个thread的threadLocal都会copy一份变量初始化并存储进行存储
    private ThreadLocal<Long> threadLocal = new ThreadLocal<>();
    
    //Controller控制器执行之前执行该方法
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        System.out.println("TimeInterceptor preHandle");
        
        long start = System.currentTimeMillis();
        
        threadLocal.set(start);
        
        //表示是否继续执行请求,是否放行HandlerAdapter的请求
        return true;
    }
    
    //Controller控制器执行之后执行该方法,多了一个ModelAndView对象
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        System.out.println("TimeInterceptor postHandle");
        
    }
    
    //视图渲染之后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
        throws Exception {
        System.out.println("TimeInterceptor afterCompletion");
        
        long end = System.currentTimeMillis();
        
        System.out.println("总共耗时:"+(end-threadLocal.get()));
        
    }
    
}

注意:推荐能使用servlet规范中的过滤器Filter实现的功能就用Filter实现,因为HandlerInteceptor只有在SpringWebMVC环境下才能使用,因此Filter是最通用的、最先应该使用的。

介绍一下 WebApplicationContext

WebApplicationContext是实现ApplicationContext接口的子类,这个接口就是专门为Web应用准备的,它允许从相对于Web根目录的路径中加载配置文件完成初始化工作,从WebApplicationContext中可以获取ServletContext引用,整个Web应用上下文对象将作为属性放置在ServletContext中, 以便Web应用环境可以访问Spring上下文。 我们可以通过WebApplicationContextUtils工具类获取到WebApplicationContext接口的实例。

除此之外,在WebApplicationContext接口中还对对象新增了3个新的作用域,它们分别是:request、session、global session, 其中两个参数HttpServletRequest:服务器从客户端拿去数据 ,HttpServletResponse:服务器向前台传送数据。而在非Web应用中对象只有2个作用域,也就是singleton和prototype。

WebApplicationContext的初始化和BeanFactory、ApplicationContext有所区别。因为WebApplicationContext是专门为Web应用设计的,所以WebApplicationContext需要ServletContext实例,也就是必须需要Web容器才可以完成初始化。在spring中我们可以使用ContextLoaderListener监听器启动WebApplicationContext。

好文参考:Spring WebApplicationContext 介绍_俺就不起网名的博客-CSDN博客

Restful风格

Restful是一种设计风格。对于我们Web开发人员来说。就是使用一个url地址表示一个唯一的资源。然后把原来的请求参数加入到请求资源地址中。

REST: Representational State Transfer。(资源)表现层状态转化。是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。
资源: Resources。网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务, 总之就是一个具体的存在。可以用一个URI(统一资源定位符)指向它, 每种资源对应一个特定的 URI。要获取这个资源,访问它的URI就可以,因此 URI即为每一个资源的独一无二的识别符。
表现层: Representation。把资源具体呈现出来的形式,叫做它的表现层(Representation)。比如,文本可以用 txt 格式表现,也可以用HTML 格式、XML 格式、JSON格式表现,甚至可以采用二进制格式。
状态转化: State Transfer。每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是“表现层状态转化”。

具体说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:

POST用来新建资源/更新资源;
DELETE 用来删除资源。
PUT用来更新资源/新建资源;

GET用来获取资源;

尽管通常来讲,HTTP 方法会映射为 CRUD 动作,但这并不是严格的限制,有时候 PUT 也可以用来创建新的资源,POST 也可以用来更新资源。实际上,POST 请求非幂等的特性(即同一个 URL 可以得到不同的结果)使其成一个非常灵活地方法,对于无法适应其他 HTTP 方法语义的操作,它都能够胜任。

事实上,Spring 4.3 之后,为了更好的支持 RESTful 风格,增加了几个注解:@PutMapping@GetMapping@DeleteMapping@PostMapping,从名字也能大概的看出,其实也就是将 method 属性的值与 @RequestMapping 进行了绑定而已

30、SpringMVC中如何发送GET请求、POST请求、PUT请求、DELETE请求

由于浏览器只支持POST和GET方法,因此需要使用_method隐藏字段通知Spring这是一个PUT/DELETE请求。
为此,Spring3.0增加了一个过滤器,可以将这些请求转换为标准的http方法,使得支持GET、POST、PUT与DELETE请求,该过滤器是HiddenHttpMethodFilter
因此,我们需要在web.xml文件中配置此过滤器。

<!-- 过滤器 转化提交方式 -->
<filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <servlet-name>DispatcherServlet</servlet-name>
</filter-mapping>
<!-- 获得get -->
<form action="stuManager/${stu.stuNo }.action" method="get">
    <input type="submit" value="查看">
</form>

<!-- 添加post -->
<form action="${ctxPath}/stuManager.action" method="post"> 
    <input type="submit" value="添加">
</form>

<!-- 修改put -->
<form action="${ctxPath}/stuManager.action" method="post">
    <input type="hidden" name="_method" value="put"/>
    <input type="submit" value="修改">
</form>

<!-- 删除delete -->
<form action="stuManager/${stu.stuNo }.action" method="post">
    <input type="hidden" name="_method" value="DELETE">
    <input type="submit" value="删除">
</form>
/**
 * 提交方式GET
 * 通过学生编号stuNo获得学生信息
 */
@RequestMapping(value="/stuManager/{stuNo}", method=RequestMethod.GET)
public String getStuInfo(@PathVariable("stuNo") String stuNo, Map<String,Object> map){
    map.put("stu", us.getStuInfo(stuNo));
    //实现Service层方法获得学生信息,并添加进map返回前台
    return "queStu";
}

/**
 * 提交方式POST
 * 添加学生信息
 */
@RequestMapping(value="/stuManager", method=RequestMethod.POST)
public String addStu(Student stu, Map<String,Object> map){
    us.addStu(stu);
    //实现Service层方法添加学生信息
    map.put("msg", "学生信息添加成功");
    return "addStu";
}

/**
 * 提交方式PUT
 * 修改学生信息
 */
@RequestMapping(value="/stuManager", method=RequestMethod.PUT)
public String updateStu(Student stu){
    us.updateStu(stu);
    //实现Service层方法更新学生信息
    return "redirect:/stuList";
}

/**
 * 提交方式DELETE
 * 通过学生编号stuNo删除学生信息
 */
@RequestMapping(value="/stuManager/{stuNo}", method=RequestMethod.DELETE)
public String delStu(@PathVariable("stuNo") String stuNo){
    us.delStu(stuNo);
    //实现Service层方法删除学生信息
    return "redirect:/stuList";
}

讲解:前端代码其实就是在表单里设置表单提交的方法为post,然后在里面加一个隐藏标签<input type="hidden" name="_method" value="put/delete"/>就可,后端代码可以在@RequestMapping中指定对应的方法为put或者delete或者用@PutMapping、@GetMapping、@DeleteMapping、@PostMapping注解

例如@RequestMapping(value="/stuManager", method=RequestMethod.PUT/DELETE)

重定向和请求转发的区别

重定向和请求转发的区别

  • 转发和重定向是两种不同的请求处理方式,转发是服务器行为,重定向是客户端行为。
  • 请求转发过程中,数据在同一个web服务器中得到共享,因为浏览器只做了一次访问请求,浏览器地址栏路径不发生变化,为同一个request域;而重定向中,浏览器做了两次请求,浏览器地址栏路径发生变化,请求并不是同一个request域。

请求转发(Forward)

请求转发是一种在服务器内部的资源跳转方式,简单来说,当客户浏览器发送http请求到web服务器中,web服务器接受请求后调用内部servlet方法完成请求处理和转发资源给同一个web容器下的另一资源做处理,最后将目标资源response给客户

工作原理及特点

比如图中web容器(Tomcat)有两个资源(A和B),若浏览器请求资源A的时候,资源A处理了一部分,然后跳转到资源B,让资源B接着处理,资源B处理完成后做出响应到客户端。资源A跳转到资源B的这个过程就叫转发。

实现代码

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
	//获取请求转发器对象,调用forward()方法,请求转发
	RequestDispatcher requestDispatcher = request.getRequestDispatcher("跳转资源的路径").forward(requset,response);
}

特点

1. 请求转发资源间共享数据

图中资源A处理了一部分数据后把其他数据转交给资源B处理,所以在这过程中资源之间数据是共享的。

void setAttribute(String name,Object o)//存储数据到request域中
Object getAttribute(String name)//根据key,获取值
void removeAttribute(String name)//根据key,删除该键值对

2. 浏览器地址栏路径不发生变化,只能转发到当前服务器的内部资源,浏览器只做了一次请求

在这里,转发的路径必须是同一个web容器下的url,其不能转向到其他的web路径上去,中间传递的是自己的容器内的request。在客户浏览器路径栏显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。

Response完成重定向(Redirect)

重定向(Redirect)是一种资源跳转的方式,一般用来解决登录进入主页、跨域访问、系统维护等等都使用重定向技术,比如当系统进行维护时,用户若发起请求,系统将会告诉浏览器重定向访问指定的url。

简单来说,当浏览器发起请求给资源A时,资源A发现浏览器发起的请求自身处理不了,但是知道资源B可以处理,这时候资源A就会告诉浏览器说这次请求处理不了,请找资源B处理并且告诉资源B的访问路径,浏览器会自动去请求资源B。

工作原理及特点

客户浏览器发送http请求后,web服务器中资源A接受请求后无法处理,资源A将会发送302状态码响应及对应新的location(资源B)给客户浏览器,客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址。

实现代码

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
	//旧方法
	//response.setStatus(302);
	//response.setHeader("location","资源B的路径");
	
	//其中资源B的路径需要添加虚拟目录
	response.sendRedirect("资源B的路径");
}

特点

1. 浏览器地址栏路径发生变化

2. 可以重定向到任意位置的资源(服务器内部、外部均可)

3. 浏览器发起两次请求,不能在多个资源使用request共享资源

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/17380.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

谈谈从DAMA、DCMM和DGI三大数据治理框架详细了解数据战略规划的关键要素

当前,数据作为新的生产要素提到了关键位置,众多组织认为数据是重要的战略资产。可是,如何发挥数据要素的生产力,数据资产又如何为企业创造价值,确有些无从下手。那么,这就是数据战略要解决的问题。企业怎么看待数据资产、数据的价值如何定位,对数据价值的期望是什么,数…

javaweb 之 会话技术 Cookie Session 登录注册案例 验证码

会话跟踪技术 会话&#xff1a;用户打开浏览器&#xff0c;访问web服务器的资源&#xff0c;会话建立&#xff0c;直到有一方断开连接&#xff0c;会话结束。在一次会话中可以包含多次请求和响应 会话跟踪&#xff1a;一种维护浏览器状态的方法&#xff0c;服务器需要识别多次…

《面试系列篇》——11种常用的设计模式

目录 【一】前言 【二】单例模式 2.1概念 2.2 饿汉模式 2.3 懒汉模式 多线程版本&#xff1a; 【三】简单工厂模式 【四】工厂方法模式 【五】抽象工厂模式 【六】策略模式 【七】装饰模式 7.1 定义 7.2 使用示例 【八】代理模式 8.1 定义 8.2 使用的优势 8.3…

【机器学习】岭回归和LASSO回归详解以及相关计算实例-加利福尼亚的房价数据集、红酒数据集

文章目录一,岭回归和LASSO回归1.1 多重共线性1.2 岭回归接手1.3 线性回归的漏洞&#xff08;线性系数的矛盾解释&#xff09;1.4 Ridge Regression1.5 岭回归实例&#xff08;加利福尼亚的房价数据集&#xff09;1.6 MSE1.7 LASSO1.8 Ridge岭回归和Lasso套索回归的比较1.9 Lass…

H5基本开发2——(HTML常见标签)

常见标签说明 <!DOCTYPE html> <html lang"zh-CN"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <meta http-equiv"X-UA-Compatible&q…

Elasticsearch安装

1.Linux下使用Elasticsearch 本教程所有操作均在CentOS 7.x环境下进行。 elasticsearch基于java进行开发&#xff0c;所以在运行elasticsearch之前需要安装jdk yum -y install java-1.8.0-openjdk 1.1.安装和配置 即将开始在centos 7 下安装Elasticsearch 1.1.1.新建一个用…

(四)QCustomPlot柱形图动态显示实例开发

系列文章目录 提示&#xff1a;这里是该系列文章的所有文章的目录 第一章&#xff1a; &#xff08;一&#xff09;QCustomPlot常见属性设置、多曲线绘制、动态曲线绘制、生成游标、矩形放大等功能实现 第二章&#xff1a; &#xff08;二&#xff09;QCustomPlot生成热力图/矩…

【数据结构与算法】递归全流程详细剖析 | 详解图的深度优先遍历

&#x1f680; 作者 &#xff1a;“大数据小禅” &#x1f680;文章简介&#xff1a;本篇文章属于数据结构与算法系列文章&#xff0c;这篇文章会对算法中的递归进行一个详细的介绍&#xff0c;不仅是概念&#xff0c;而是从运行过程中的每一步进行详细分析。并使用递归的方式来…

Vue3 - 使用 mitt.js 进行组件通信(兄弟关系)

前言 在 Vue2 中&#xff0c;两个同级&#xff08;兄弟关系&#xff09;的组件传递数据&#xff0c;都是利用一个叫 EventBus 事件总线&#xff0c;利用这根总线&#xff0c;可以让两个毫无关系的组件进行通信。 其实这个问题的本质核心解决方案&#xff0c;就一句话。通过第三…

开传奇需要什么技术要什么条件

开传奇需要什么技术要什么条件 对于这个问题&#xff0c;近期问的人比较多。这也是热爱传奇这个游戏的朋友会问到的一个问题&#xff0c;因为喜欢玩这个游戏&#xff0c;也想要自己去开一个经营一个 不管是电脑端还是手机端。但是对于一些新手确实不知道该如何开始操作从哪些方…

基于PHP+MySQL的校园二手旧书回收捐赠系统

校园内因为所教授的课程比较多,人数基数也比较大,所以每年都会产生大量的二手废旧图书,很多时候这些图书还可以进行二次利用,如果直接当废品进行回收很暴殄天物的,所以为了能够让这些校园内的二手图书得到更多的利用,我们开发了PHP校园二手旧书回收捐赠系统 本系统分为前台和后…

「Redis数据结构」压缩列表(ZipList)

「Redis数据结构」压缩列表&#xff08;ZipList&#xff09; 文章目录「Redis数据结构」压缩列表&#xff08;ZipList&#xff09;一、概述二、结构三、连锁更新问题四、压缩列表的缺陷五、小结参考ZipList 是一种特殊的“双端链表” &#xff0c;由一系列特殊编码的连续内存块…

基于协同过滤进行金融产品个性化推荐 代码+数据

1.案例简介 (1)方法概述: 本教程包含如下内容: 从原始的数据文件中加载数据,进行训练集和测试集的切分。 对数据分batch, 利用用户侧信息,和商品侧信息,进行双塔模型的训练 结果展示 (2)宏观流程图 (3)案例知识点 pytorch 视频CTR预估指标 博客point wise 以及 pair wise …

从0到1图文教你如何将spring boot项目部署到minikube中去

这里是weihubeats,觉得文章不错可以关注公众号小奏技术&#xff0c;文章首发。拒绝营销号&#xff0c;拒绝标题党 源码 本项目博文的源码已上传github&#xff0c;有需要自取 github: https://github.com/weihubeats/weihubeats_demos/tree/master/spring-boot-demos/spring-…

C#界面里Control.Enabled 属性的使用

C#界面里Control.Enabled 属性的使用 在开发中即使一个细节也可以决定成败。 前段时间刚好是毕业季,公司招聘了很多应界毕业生, 由于项目非常多,许多老手都已经分配到各个项目上去,还有一些小项目不得不让这些新生力军来接手。 这些初生牛犊不怕虎,天天加班,夜夜苦战…

idea打开之前的项目不能正常编译/idea中项目Compile output丢失问题

报错笔记 文章目录错误一&#xff1a;Cannot resolve class or package java错误二&#xff1a;Compile output丢失问题下面的报错发生情况都是打开之前写的JavaWeb文件时&#xff0c;各个文件中的代码爆红错误一&#xff1a;Cannot resolve class or package ‘java’ 在idea…

【安卓应用渗透】第一篇:安卓逆向回顾和梳理-2211

文章目录安卓开发课程回顾第一课&#xff1a;安卓项目文件结构&#xff08;逻辑视图&#xff09;第五课&#xff1a;文件读取和数据库存取文件存取SQLite数据库存取安卓开发&#xff1a;案前资料库安卓逆向课程回顾第一课&#xff1a;基础逆向工具&#xff08;Apktool, Keytool…

【分享】订阅金蝶云进销存集简云连接器同步销货数据至金蝶云进销存系统

方案场景 在企业规模不断壮大的过程中&#xff0c;企业都是在钉钉内提交OA审批&#xff0c;并通过人工的方式统计多种审批数据&#xff0c;然后手动导入到某系统内&#xff0c;如果中间有调整又需要从头再进行核对&#xff0c;繁琐的流程严重影响了业务拓展。基于该方式企业希…

[附源码]SSM计算机毕业设计重庆工程学院教师宿舍管理系统论文JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Eolink征文活动--推荐国产神器Eolink!API优先,Eolink领先!

前言&#xff1a; 在我们后端开发者做项目的时候&#xff0c;避免不了与前端界面的交互&#xff0c;对于我来讲&#xff0c;在做项目的时候用到过postman&#xff0c;swagger做接口测试工作&#xff0c;在公司的时候公司主要用YApi可视化接口平台&#xff0c;最近使用了一个爆款…