狂神说
Controller接口在
org.springframework.web.servlet.mvc包下,该接口只有一个方法:处理请求并返回一个模型与视图对象ModelAndView
@FunctionalInterface
public interface Controller {
@Nullable
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
测试:
新建一个Moudle,和之前一样,添加web配置;
确定导入相关依赖,并刷新;
配置web.xml;
<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">
<servlet>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springmvc-servlet.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
添加Spring MVC配置文件resources/springmvc-servlet.xml
<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.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
bean>
beans>
编写一个Controller类,ControllerTest1.java;
public class ControllerTest1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("msg","Hello ControllerTestOne");
mv.setViewName("test");
return mv;
}
}
在配置文件resources/springmvc-servlet.xml中注册请求的bean;
<bean name="/t1" class="com.ano.controller.ControllerTest1">bean>
编写前端页面WEB-INF/jsp/test.jsp;
<%--
Created by IntelliJ IDEA.
User: wangjiao
Date: 2022/9/13
Time: 20:22
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
${msg}
配置Tomcat运行测试OK!

实现Controller接口定义控制器是较老的方法;
缺点:一个控制器中只能有一个方法,如果多个方法则需要定义多个控制器,比较麻烦。
@Controller注解类型用于声明Spring类的实例是一个控制器;
Spring可以使用扫描机制来找到应用程序中基于注解的控制类;
为了保证Spring能找到相应控制器,需要在配置文件中声明组件扫描,即在springmvc-servlet.xml中声明组件扫描:
<context:component-scan base-package="com.ano.controller">context:component-scan>
增加一个控制器类,使用注解实现,ControllerTest2.java
@Controller
public class ControllerTest2 {
@RequestMapping("/t2")
public String index(Model model) {
model.addAttribute("msg","ControllerTest2");
return "test";
}
}
运行tomcat测试OK!

可以发现,两个请求都可以指向同一个视图,但是页面结果的结果是不一样的,可以看出视图是被复用的,而控制器与视图之间是弱偶合关系;
注解方式是平时使用的最多的方式。
只注解在方法上,
@Controller
public class ControllerTest3 {
@RequestMapping("/t3")
public String test3(Model model) {
model.addAttribute("msg","ControllerTest3");
return "test";
}
}
访问路径为:ip:port/项目名/t3 该测试示例的访问路径即 http://localhost:8080/t3
同时注解在类和方法上
@Controller
@RequestMapping("/admin")
public class ControllerTest3 {
@RequestMapping("/t3")
public String test3(Model model) {
model.addAttribute("msg","ControllerTest3");
return "test";
}
}
访问路径为:ip:port/项目名/admin/t3 该测试示例的访问路径即http://localhost:8080/admin/t3
Restful是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁、更有层次、更易于实现缓存等机制。
功能:互联网所有的事物都可以被抽象为资源,一般使用POST、DELETE、PUT、GET等不同方法对资源进行添加、删除、修改、查询操作。
传统方式操作资源:通过不同参数来实现不同的操作效果。方法比较单一,如下所示:
http://127.0.0.1/item/queryUser.action?id=1 查询,GET
http://127.0.0.1/item/saveUser.action 新增,POST
http://127.0.0.1/item/updateUser.action 更新,POST
http://127.0.0.1/item/deleteUser.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
测试示例
@Controller
public class RestFulController {
/**
* 原生的URL:http://localhost:8080/add?a=1&b=2
*/
@RequestMapping("/add")
public String addNumTest1(int a, int b, Model model) {
model.addAttribute("msg",a+b);
return "test";
}
/**
* RestFul方式1:请求方式 method = GET
* @GetMapping() = RequestMapping("/add/{a}/{b}",method=requestMethod.GET)
* http://localhost:8080/add/2/3
*/
@GetMapping("/add/{a}/{b}")
public String addNumTest2(@PathVariable int a, @PathVariable int b, Model model) {
model.addAttribute("msg",a+b);
return "test";
}
/**
* RestFul方式2:请求方式 method = POST
* 复用了相同的URL
* http://localhost:8080/add/2/3
*/
@PostMapping("/add/{a}/{b}")
public String addNumTest3(@PathVariable int a, @PathVariable int b, Model model) {
model.addAttribute("msg",a+b);
return "test";
}
}
通过设置ModelAndView对象,并根据view的名称和视图解析器跳转到指定的页面;
页面:{视图解析器前缀} + viewName +{视图解析器后缀}
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
bean>
Controller类通过实现Controller接口的方式实现
public class ControllerTest1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("msg","Hello ControllerTestOne");
mv.setViewName("test");
return mv;
}
}
通过设置ServletAPI的方式,不需要视图解析器。
示例:
@Controller
public class ResultServletApi {
/**
* 输出
*/
@RequestMapping("/result/t1")
public void test1(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.getWriter().print("Hello, SpringMVC result by ServletAPI");
}
/**
* 重定向
*/
@RequestMapping("/result/t2")
public void test2(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.sendRedirect("/index.jsp");
}
/**
* 转发
*/
@RequestMapping("/result/t3")
public void test3(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
request.setAttribute("msg","result/t3");
request.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(request,response);
}
}
通过HttpServletResponse进行输出
通过HttpServletResponse实现重定向
通过HttpServletRequest实现转发
无需视图解析器的情况
@Controller
public class ResultSpringMvc {
/**
* 转发
*/
@RequestMapping("/resM/t1")
public String test1(Model model) {
model.addAttribute("msg","/resM/t1");
return "/WEB-INF/jsp/test.jsp";
}
/**
* 转发
*/
@RequestMapping("/resM/t2")
public String test2(Model model) {
model.addAttribute("msg","/resM/t2");
return "forward:/WEB-INF/jsp/test.jsp";
}
/**
* 重定向
*/
@RequestMapping("/resM/t3")
public String test2() {
return "redirect:/index.jsp";
}
}
有视图解析器的情况
@Controller
public class ResultSpringMvc2 {
/**
* 转发
*/
@RequestMapping("/resM2/t1")
public String test1(Model model) {
model.addAttribute("msg","resM2/t1");
return "test";
}
/**
* 重定向
*/
@RequestMapping("/resM2/t2")
public String test2() {
return "redirect:/index.jsp";
}
}
测试程序
@Controller
public class DataHandle {
/**
* 提交的域名称和处理方法的参数名一致
* http://localhost:8080/test?name=ano
*/
@RequestMapping("/test")
public String test(String name, Model model){
model.addAttribute("msg",name);
return "test";
}
/**
* 提交的域名称和处理方法的参数名不一致
* http://localhost:8080/test2?username=ano2
*/
@RequestMapping("/test2")
public String test2(@RequestParam("username") String name, Model model){
model.addAttribute("msg",name);
return "test";
}
/**
* 提交的参数是一个对象时,前端传的参数必须和对象名称一致,否则会null
* http://localhost:8080/user?name=ano&id=1001&age=18
* @param user
* @param model
* @return
*/
@RequestMapping("/user")
public String userInfo(User user, Model model) {
System.out.println(user);
model.addAttribute("msg",user.toString());
return "test";
}
}
实体类User
package com.ano.pojo;
/**
* @author wangjiao
* @version 1.0
* @date 2022/9/21 17:45
*/
public class User {
private int id;
private String name;
private int age;
public User() {
}
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
第一种方式:ModelAndView
第二种方式:Model
第三种方式:ModelMap
简单对比:
ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转;
Model 只有寥寥几个方法只适合用于储存数据,简化了对于Model对象的操作和理解;
ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性。
一个简单的测试程序
在首页编写一个表单index.jsp
编写响应的Controller类及处理方法
@RequestMapping("/encoding/test")
public String test(String name, Model model) {
model.addAttribute("msg",name);
return "test";
}
localhost:8080进入首页表单输入中文测试,发现会乱码


一般通过配置过滤器解决乱码问题。
EncodingFilter.java;public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setCharacterEncoding("utf-8");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
<filter>
<filter-name>encodingfilter-name>
<filter-class>com.ano.filter.EncodingFilterfilter-class>
filter>
<filter-mapping>
<filter-name>encodingfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>

web.xml即可
<filter>
<filter-name>encodingfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
一般情况下,Spring框架默认的这个过滤器就已经能够很好的解决乱码问题,但是有一些极端情况,Spring框架提供的过滤器对GET请求的支持不友好,处理方法如下:
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
package com.ano.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/**
* 通用乱码过滤器:解决get和post请求全部乱码的过滤器
*/
public class GenericEncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
// 处理response的字符编码
HttpServletResponse myResponse=(HttpServletResponse) response;
myResponse.setContentType("text/html;charset=UTF-8");
// 转型为与协议相关对象
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 对request包装增强
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
filterChain.doFilter(myrequest, response);
}
@Override
public void destroy() {
}
}
/**
* 自定义request对象,HttpServletRequest的包装类
*/
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
/**
* 是否编码的标记
*/
private boolean hasEncode;
/**
* 定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
* @param request
*/
public MyRequest(HttpServletRequest request) {
// super必须写
super(request);
this.request = request;
}
/**
* 对需要增强方法 进行覆盖
* @return
*/
@Override
public Map getParameterMap() {
// 先获得请求方式
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
// post请求
try {
// 处理post乱码
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (method.equalsIgnoreCase("get")) {
// get请求
Map<String, String[]> parameterMap = request.getParameterMap();
// 确保get手动编码逻辑只运行一次
if (!hasEncode) {
for (String parameterName : parameterMap.keySet()) {
String[] values = parameterMap.get(parameterName);
if (values != null) {
for (int i = 0; i < values.length; i++) {
try {
// 处理get乱码
values[i] = new String(values[i]
.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
hasEncode = true;
}
return parameterMap;
}
return super.getParameterMap();
}
/**
* 取一个值
* @param name
* @return
*/
@Override
public String getParameter(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
if (values == null) {
return null;
}
// 取回参数的第一个值
return values[0];
}
/**
* 取所有值
* @param name
* @return
*/
@Override
public String[] getParameterValues(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
}