SpringMvc 常见面试题

1、SpringMvc概述

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

Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。

1.2、Springmvc的优点:

(1)可以支持各种视图技术,而不仅仅局限于JSP;

(2)与Spring框架集成(如IoC容器、AOP等);

(3)清晰的角色分配:前端控制器(dispatcherServlet) ,请求到处理器映射(handlerMapping),处理器适配器(HandlerAdapter),视图解析器(ViewResolver)。

(4) 支持各种请求资源的映射策略。

1.3、有了spring 为啥还要使用 springmvc

Spring MVC 是 Spring 框架中的一个模块,专注于支持 Web 应用程序的开发。尽管 Spring 框架本身提供了很多功能,包括依赖注入、事务管理、AOP 等,但它并没有提供专门的支持 Web 开发的解决方案。这就是 Spring MVC 出现的原因。

以下是为什么在使用 Spring 框架的同时还需要使用 Spring MVC 的一些原因:

  1. Web 开发专注: Spring MVC 提供了一种基于 MVC 模式的 Web 框架,使得开发者可以更容易地组织和处理 Web 请求。它使得处理用户输入、渲染视图等 Web 相关任务更加直观。
  2. RESTful 支持: Spring MVC 提供了对 RESTful 风格的支持,使得构建 RESTful Web 服务变得简单。通过注解和配置,你可以轻松地定义 RESTful 风格的控制器。
  3. View 技术集成: Spring MVC 集成了多种视图技术,包括 JSP、FreeMarker、Thymeleaf 等。这使得你可以选择最适合你项目的视图技术,而不受限于框架的选择。
  4. 灵活的 URL 映射: Spring MVC 允许你通过注解或配置文件自定义 URL 映射,使得 URL 结构更符合项目需求。
  5. 中间件支持: Spring MVC 可以集成各种中间件,例如拦截器、过滤器等,使得在请求处理的不同阶段执行特定的逻辑变得更加方便。
  6. 数据绑定和验证: Spring MVC 提供了强大的数据绑定和验证机制,帮助你更容易地将用户提交的数据绑定到后端的模型中,并进行有效性验证。

虽然 Spring 框架本身包含了一些基本的 Web 支持,但 Spring MVC 提供了更为专业、全面的解决方案,适用于需要构建 Web 应用程序或 RESTful 服务的场景。因此,当你需要开发 Web 应用时,使用 Spring MVC 是一个自然的选择。

1.4、SpringMVC的流程?

(1)用户发送请求至前端控制器DispatcherServlet;

(2)DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handler;

(3)处理器映射器根据请求url找到具体的处理器Handler,生成处理器对象及处理器拦截器(如果有则生成),一并返回给DispatcherServlet;

(4)DispatcherServlet 调用 HandlerAdapter处理器适配器,请求执行Handler;

(5)HandlerAdapter 经过适配调用 具体处理器进行处理业务逻辑;

(6)Handler执行完成返回ModelAndView;

(7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;

(8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;

(9)ViewResolver解析后返回具体View;

(10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)

(11)DispatcherServlet响应用户。

前端控制器 DispatcherServlet:接收请求、响应结果,相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度。

处理器映射器 HandlerMapping:根据请求的URL来查找Handler

处理器适配器 HandlerAdapter:负责执行Handler

处理器 Handler:处理器,需要程序员开发

视图解析器 ViewResolver:进行视图的解析,根据视图逻辑名将ModelAndView解析成真正的视图(view)

视图View:View是一个接口, 它的实现类支持不同的视图类型,如jsp,freemarker,pdf等等

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

答:是单例模式,在多线程访问的时候有线程安全问题,解决方案是在控制器里面不能写可变状态量,如果需要使用这些可变状态,可以使用ThreadLocal机制解决,为每个线程单独生成一份变量副本,独立操作,互不影响。

2、Spring Mvc 的简单使用

2.1、XML文件配置版

1、创建一个web项目

2、导入SpringMvc对应的依赖

3、配置web.xml , 注册DispatcherServlet

这里暂停一下,我们在学习myBatis,Spring时可能就没有使用web框架,使用Junit提供的测试就可以了,但是Mvc是用于专注web开发的,我们需要用到浏览器,服务器。我们在学习JavaWeb的时候,就是在web.xml通过配置servlet映射,来将浏览器的需求传递给服务器,并且做出相应的响应的

xml 复制代码
<!--    注册Servlet-->
    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.x.servlet.HelloServlet</servlet-class>
    </servlet>
<!--    Servlet的请求路径-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
java 复制代码
public class HelloServlet extends HttpServlet {
    // 由于get或者post只是请求实现的不同的方式,可以相互调用,业务逻辑都一样
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //ServletOutputStream outputStream = resp.getOutputStream();
        PrintWriter writer = resp.getWriter();//响应流
        writer.print("Hello,Servlet");
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

好了,这里只是带大家回顾一下JavaWeb的知识。

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--配置DispatchServlet:这个是SpringMVC的核心,请求分发器,前端控制器-->
    <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatcherServlet必须要绑定Spring的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!--启动级别:1 跟服务器一起启动-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--
    在SpringMVC中
    /:只匹配所有的请求,不会去匹配JSP界面
    /*:匹配所有的请求,包括jsp页面
    -->
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

我们在上面提到过,SpringMvc的核心就是DispatcherServlet它接收所有的请求并进行转发与处理。

4、编写SpringMVC 的 配置文件!名称:springmvc-servlet.xml : [servletname]-servlet.xml 说明,这里的名称要求是按照官方来的

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--处理器映射器-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <!--处理起适配器-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    <!--视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!--前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>

    <bean id="/hello" class="com.kolt.controller.HelloController"/>

</beans>

5、编写我们要操作业务Controller ,要么实现Controller接口,要么增加注解(下面讲);需要返回一个 ModelAndView,装数据,封视图;

java 复制代码
public class HelloController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView modelAndView = new ModelAndView();
        //业务代码
        String result = "HelloSpringMVC";
        modelAndView.addObject("msg",result);
        //视图跳转
        modelAndView.setViewName("test");
        return modelAndView;
    }
}

2.2、注解版(注意,不是完全不使用 xml 文件,只是简化)

大部分步骤是不变的,spring的配置文件略有区别

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
    <context:component-scan base-package="com.kolt.controller"/>
    <!-- 让Spring MVC不处理静态资源 css啥的-->
    <mvc:default-servlet-handler/>
    <!--
    支持mvc注解驱动
    在spring中一般采用@RequestMapping注解来完成映射关系
    要想使@RequestMapping注解生效
    必须向上下文中注册DefaultAnnotationHandlerMapping
    和一个AnnotationMethodHandlerAdapter实例
    这两个实例分别在类级别和方法级别处理。
    而annotation-driven配置帮助我们自动完成上述两个实例的注入。
    -->
    <mvc:annotation-driven/>
    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!-- 后缀 -->
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>
java 复制代码
@Controller//代表这个类会被spring接管,所有方法,如果返回值是string。并且有具体的页面可以跳转,它就会被视图解析器解析
public class HelloController {

    @RequestMapping("/h1")
    public String hello(Model model){
        //封装数据
        model.addAttribute("msg","Hello,SpringMVC");
        return "hello";//会被视图解析器处理
    }
}

我们是不是没有在配置文件中声明 Bean,因为什么呀,我们开了自动扫描,而且在类上使用了 @Controller 注解(相信大家对这种注解都不陌生。

2.3、常用注解

1、Controller 这个就不讲了,大家都比较熟悉

2、@RequestMapping

@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。 用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。(这个没啥难的,大家都知道)

java 复制代码
@Controller
@RequestMapping("/admin")
public class ControllerTest3 {

    @RequestMapping("/t1")
    public String test4(Model model){
        model.addAttribute("msg","admin");
        return "admin/admin";
    }
}

Spring MVC 的 @RequestMapping 注解能够处理 HTTP 请求的方法, 比如 GET, PUT, POST, DELETE 以 及 PATCH。 所有的地址栏请求默认都会是 HTTP GET 类型的。 方法级别的注解变体有如下几个: 组合注解

xml 复制代码
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

@GetMapping 是一个组合注解 它所扮演的是 @RequestMapping(method =RequestMethod.GET) 的一个快捷方式。 平时使用的会比较多!

3、@ResponseBody

@ResponseBody加了这个注解,他就不会走视图解析器,会直接返回一个字符串,而不是去找界面

java 复制代码
@RequestMapping(value = "/t1", produces = "application/json;charset=utf-8")
    @ResponseBody//加了这个注解,他就不会走视图解析器,会直接返回一个字符串,而不是去找界面
    public String json1() throws JsonProcessingException {

        //jackson,ObjectMapper
        ObjectMapper objectMapper = new ObjectMapper();
        User user = new User("hh",4,"男");
        String str = objectMapper.writeValueAsString(user);

        return str;
    }
}

或者直接在类上使用 @RestController,效果是一样的。

java 复制代码
RestController
public class UserController {

    @RequestMapping(value = "/t1", produces = "application/json;charset=utf-8")
    public String json1() throws JsonProcessingException {

        //jackson,ObjectMapper
        ObjectMapper objectMapper = new ObjectMapper();
        User user = new User("hh",4,"男");
        String str = objectMapper.writeValueAsString(user);

        return str;
    }
}

2.4、RestFul风格

概念 :Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设 计的软件可以更简洁,更有层次,更易于实现缓存等机制。

功能 资源:互联网所有的事物都可以被抽象为资源 资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。 分别对应 添加、 删除、修改、查询。

传统方式操作资源 :通过不同的参数来实现不同的效果!方法单一,post 和 get

http://127.0.0.1/item/queryItem.action?id=1 查询,GET

http://127.0.0.1/item/saveItem.action 新增,POST

http://127.0.0.1/item/updateItem.action 更新,POST

http://127.0.0.1/item/deleteItem.action?id=1 删除,GET或POST

使用RESTful操作资源 : 可以通过不同的请求方式来实现不同的效果!如下:请求地址一样,但是功能 可以不同!

http://127.0.0.1/item/1 查询,GET

http://127.0.0.1/item 新增,POST

http://127.0.0.1/item 更新,PUT

http://127.0.0.1/item/1 删除,DELETE

java 复制代码
@Controller
public class RestFulController {
    //原本的写法:http://localhost:8080/4/add?a=1&b=2
    //RestFul:

    @PostMapping("/add/{a}/{b}")
    public String test1(@PathVariable int a, @PathVariable int b, Model model) {
        int res = a + b;
        model.addAttribute("msg","结果为"+res);

        return "admin/admin";
    }

    @RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.GET)
    public String test2(@PathVariable int a, @PathVariable int b, Model model) {
        int res = a + b;
        model.addAttribute("msg","结果为"+res);

        return "admin/admin";
    }
}

2.5、转发和重定向

java 复制代码
public String test1(Model model){
    model.addAttribute("msg","ModelTest1");
	//重定向
    return "redirect:/index.jsp";
    //转发
    return "forward:/index.jsp";
}

2.6 怎么返回给前端数据

第一种: 通过 ModelAndView

java 复制代码
public class ControllerTest1 implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView();
mv.addObject("msg","ControllerTest1");
mv.setViewName("test");
return mv;
}
}

第二种:ModelMap

java 复制代码
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name, ModelMap model){
//封装要显示到视图中的数据
//相当于req.setAttribute("name",name);
model.addAttribute("name",name);
System.out.println(name);
return "hello";
}

第三种:通过Model

java 复制代码
@RequestMapping("/ct2/hello")
public String hello(@RequestParam("username") String name, Model model){
//封装要显示到视图中的数据
//相当于req.setAttribute("name",name);
model.addAttribute("msg",name);
System.out.println(name);
return "test";
}

对比:

Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;

ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特 性;

ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。

3、Spring Mvc 拦截器

3.1 拦截器概述

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开 发者可以自己定义一些拦截器来实现特定的功能。

过滤器与拦截器的区别:拦截器是AOP思想的具体应用。

过滤器:

servlet规范中的一部分,任何java web工程都可以使用 在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截

拦截器

拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用 拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的

3.2、自定义拦截器

想要自定义拦截器,必须实现 HandlerInterceptor 接口。

java 复制代码
public class MyInterceptor implements HandlerInterceptor {
    //在请求处理的方法之前执行
//如果返回true执行下一个拦截器
//如果返回false就不执行下一个拦截器
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("------------处理前------------");
        return true;
    }
    //在请求处理方法执行之后执行
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("------------处理后------------");
    }
    //在dispatcherServlet处理后执行,做清理工作.
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("------------清理------------");
    }
}

还要在spring的配置文件中声明

xml 复制代码
 <!--关于拦截器的配置-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--/** 包括路径及其子路径-->
            <!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截-->
            <!--/admin/** 拦截的是/admin/下的所有-->
            <mvc:mapping path="/**"/>
            <!--bean配置的就是拦截器-->
            <bean class="com.kolt.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

4、注解

4.1、什么是注解

Annontation是Java5开始引入的新特征,中文名称叫注解。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据 (metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具或框架使用。

Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。 Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation包中。

4.2、注解的用处

1、生成文档。这是最常见的,也是java 最早提供的注解。常用的有@param @return

2、跟踪代码依赖性,实现替代配置文件功能。比如Dagger 2 依赖注入,java 开发比如我们的 Spring框架,将大量注解配置,具有很大用处;

3、在编译时进行格式检查。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。

4.3 注解的原理

注解本质是一个继承了Annotation 的特殊接口,其具体实现类是Java 运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java 运行时生成的动态代理对象$Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandlerinvoke方法。该方法会从memberValues 这个Map 中索引出对应的值。而memberValues 的来源是Java 常量池。

4.4、注解的使用(自定义注解)

注解在实际开发中非常常见,比如 Java 原生的 @Overried、@Deprecated 等,Spring的 @Controller、@Service等,Lombok 工具类也有大量的注解,不过在原生 Java 中,还提供了元 Annotation(元注解),他主要是用来修饰注解的,比如 @Target、@Retention、@Document、@Inherited 等。

@Target:标识注解可以修饰哪些地方,比如方法、成员变量、包等,具体取值有以下几种:ElementType.TYPE/FIELD/METHOD/PARAMETER/CONSTRUCTOR/LOCAL_VARIABLE/ANNOTATION_TYPE/PACKAGE/TYPE_PARAMETER/TYPE_USE
@Retention:什么时候使用注解:SOURCE(编译阶段就丢弃) / CLASS(类加载时丢弃) / RUNTIME(始终不会丢弃),一般来说,我们自定义的注解都是 RUNTIME 级别的,因为大多数情况我们是根据运行时环境去做一些处理,一般需要配合反射来使用,因为反射是 Java 获取运行是的信息的重要手段
@Document:注解是否会包含在 javadoc 中;
@Inherited:定义该注解与子类的关系,子类是否能使用。

如何自定义注解?

  1. 创建一个自定义注解:与创建接口类似,但自定义注解需要使用 @interface

  2. 添加元注解信息,比如 @Target、@Retention、@Document、@Inherited 等

  3. 创建注解方法,但注解方法不能带有参数

  4. 注解方法返回值为基本类型、String、Enums、Annotation 或其数组

  5. 注解可以有默认值;

    java 复制代码
    @Target(FIELD)
    @Retention(RUNTIME)
    @Documented
    public @interface CarName {
        String value() default "";

5、乱码问题

1、解决post请求乱码问题:在web.xml中配置一个CharacterEncodingFilter过滤器,设置成utf-8;

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>

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

  1. 修改tomcat配置文件添加编码与工程编码一致,如下:

    xml 复制代码
    <ConnectorURIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

​ 2.另外一种方法对参数进行重新编码:

java 复制代码
String userName = new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")

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

值得一提的是乱码的情况可能会有很多种,需要大家根据不同的情况去进行不同的处理

相关推荐
【D'accumulation】21 分钟前
典型的MVC设计模式:使用JSP和JavaBean相结合的方式来动态生成网页内容典型的MVC设计模式
java·设计模式·mvc
小菜yh1 小时前
关于Redis
java·数据库·spring boot·redis·spring·缓存
爱上语文2 小时前
Springboot的三层架构
java·开发语言·spring boot·后端·spring
荆州克莱2 小时前
springcloud整合nacos、sentinal、springcloud-gateway,springboot security、oauth2总结
spring boot·spring·spring cloud·css3·技术
拾光师9 小时前
spring获取当前request
java·后端·spring
xujinwei_gingko10 小时前
Spring IOC容器Bean对象管理-Java Config方式
java·spring
Xua305511 小时前
浅谈Spring Cloud:认识微服务
spring·spring cloud·微服务
JOJO___13 小时前
Spring IoC 配置类 总结
java·后端·spring·java-ee
黄昏_14 小时前
苍穹外卖Day01-2
java·spring
亚林瓜子14 小时前
Jackson注解屏蔽某些字段读取权限
spring·json·jackson