SpringMVC初步学习
MVC
- 模型(Model(Dao,Service)),视图(View(Jsp)),控制器(Controller(Servlet))
- 是一种软件设计规范
- 将业务逻辑,数据,显示分离的方法来组织代码
- 不是设计模式,而是架构模式
- 是基于 Java 实现 MVC 的轻量级 Web 框架
导入依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.14</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>compile</scope>
</dependency>
一个简单的 SpringMVC 程序
- 接口方法实现
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
//导入Controller接口
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//ModelAndView模型和视图
ModelAndView mv=new ModelAndView();
//封装对象,放在ModelAndView中
mv.addObject("msg","HelloSpringMVC");
//封装要跳转的视图,放在ModelAndView中
mv.setViewName("hello");
return mv;
}
}
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--视图解析器:DispatcherServlet给他的ModelAndView-->
<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.yqly.controller.HelloController"/>
</beans>
<?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">
<!--注册DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--关联一个springmvc的配置文件:[servlet-name]-servlet.xml-->
<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>
<!--/匹配所有请求:(不包括.jsp页面)-->
<!--/*匹配所有请求:(包括.jsp页面,会出现不限嵌套的情况)-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
有可能出现的问题,Tomcat 项目中没有自动创建 lib 目录
- 打开项目管理
- 创建 lib 目录
- 将依赖全部导入该目录
- 重启项目
SpringMVC 执行原理
-
用户发出请求
-
DispatcherServlet 表示前端控制器,是整个 SpringMVC 的控制中心,DispatcherServlet 接收请求并拦截请求
假设请求的 url 为:http://localhost:8080/SpringMVC/hello
http://localhost:8080 为服务器域名
SpringMVC 为部署哎服务器上的 web 站点
hello 表示控制器
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
-
HandlerMapping 为处理器映射,由 DispatcherServlet 自行调用
作用:根据 url 查找控制器
-
HandlerExecution 表示具体的 Handler,将解析后的信息传递给 DispatcherServlet
<bean id="/hello" class="com.yqly.controller.HelloController"/>
- HandlerAdapter 表示处理器适配器,其按照特定的规则去执行 Handler
- Handler 让具体的 Controller 执行
- Controller 将具体的执行信息返回给 HandlerAdpater,如 ModelAndView
- HandlerAdapter 将视图逻辑名或模型传递给 DispatcherServlet
- DispacherServlet 调用视图解析器(ViewResolver)来解析 HandlerAdapter 传递的逻辑视图名
- 获取 ModelAndView 的数据
- 解析 ModelAndView 中的视图名字
- 拼接视图名字,找到对应的视图
- 将数据渲染到视图上
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!--后缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--前缀-->
<property name="suffix" value=".jsp"/>
</bean>
- 视图解析器将解析的逻辑视图名传给 DispatcherServlet
- DispatcherServlet 根据视图解析器解析的视图结果,调用具体视图
- 最终将视图呈现给用户
以上十二步可以大概分为三个部分
1~4:适配请求,获得请求的目的
5~8:完成请求,返回结果
9~12:将结果以视图形式展示
注解开发 SpringMVC
<?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">
<!--注册DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--关联一个springmvc的配置文件:[servlet-name]-servlet.xml-->
<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>
<!--/匹配所有请求:(不包括.jsp页面)-->
<!--/*匹配所有请求:(包括.jsp页面,会出现不限嵌套的情况)-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
<?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
https://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.yqly.controller"/>
<!--让SpringMVC不处理静态资源(.css,.js,.html,mp3...)-->
<mvc:default-servlet-handler/>
<!--自动完成DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter两个实例的注入-->
<mvc:annotation-driven/>
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
-
@Controller
代表这个类会被 Spring 接管
被注解的类中的所有方法,如果返回值为 String,并且有具体页面可以跳转,那么就会被视图解析器解析
-
@RequestMapping
可以加在类上(表示该类下的所有响应请求的方法都要以该路径为父路径),也可以加在方法上(表示直接路径)
如果同时注解了类和方法,需要先指定类路径,再指定方法路径
@Controller
public class HelloController{
//真是访问地址的选择器名
@RequestMapping("/test")
public String Hello(Model model){
//向模型中添加属性msg与值,可以在jsp页面中取出并渲染
model.addAttribute("msg","大雾四起");
return "test";
}
}
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
@Controller
public class RestFulController {
@RequestMapping("/add/{a}/{b}")
public String test(Model model, @PathVariable int a, @PathVariable int b){
int sum=a+b;
model.addAttribute("msg",sum);
return "test";
}
}
- 可以通过 @PathVariable 注解,让方法参数的值对应绑定到一个 URL 模板变量上
- 可以通过在 @RequestMapping 中加 method 来设置请求方式,也可以通过 @RequestMapping 注解的变体(@GetMapping 等)来设置请求方式
转发与重定向
没有视图解析器
转发的方式
- 返回路径必须为全路径
@Controller
public class HelloController {
@RequestMapping("/add/c1")
public String test(){
return "/test.jsp";
}
}
@Controller
public class HelloController {
@RequestMapping("/add/c1")
public String test(Model model){
return "forword:/test.jsp";
}
}
重定向的方式
@Controller
public class HelloController {
@RequestMapping("/add/c1")
public String test(Model model){
return "redirect:/test.jsp";
}
}
有视图解析器
转发的方式
- 默认为转发
@Controller
public class HelloController {
@RequestMapping("/add/c1")
public String test(Model model){
return "test";
}
}
重定向的方式
- 与没有视图解析器时一样
@Controller
public class HelloController {
@RequestMapping("/add/c1")
public String test(Model model){
return "redirect:/test.jsp";
}
}
数据接收和传递
数据接收
- 从前端获取数据
提交的数据为单个或多个参数
参数名与提交时的数据名一致
提交数据
http://localhost:8080/c1?name=小王
处理方法
@Controller
public class HelloController {
@RequestMapping("/c1")
public String test(String name){
return "test";
}
}
参数名与提交时的数据名不一致
提交数据
http://localhost:8080/c1?username=小王
处理方法
@Controller
public class HelloController {
@RequestMapping("/c1")
public String test(@RequestParam("username") String name){
return "test";
}
}
提交的数据为对象
- 接收前端传递的数据,与对象中的属性名一一匹配,如果一致则填入数据,否则为空
提交数据
http://localhost:8080/user/c2?id=1&name=wxh&age=17
处理方法
@RequestMapping("/c2")
public String test2(User user){
System.out.println(user);
return "test";
}
数据传递
-
将数据传到前端
-
ModelAndView
可以在存储数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转
-
ModelMap
继承了 LinkedMap,除了实现了自身的一些方法,同样的继承了 LinkedMap 的方法和特性
-
Model
只有寥寥几个方法适用于存储数据,简化了新手对于 Model 的操作和理解
乱码问题
- 从前端接收到的中文会变成乱码
方法一:加过滤器
编写过滤器
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>encoding</filter-name>
<filter-class>com.yqly.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
方法二:配置SpringMVC的乱码过滤
- 在 web.xml 中配置
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
JSON
- JSON(JavaScript Object Notation),JS 对象标记
- 是一种轻量级的数据交换格式,使用广泛
- 采用完全独立于编程语言的文本格式来存储和表示数据
- 要求和语法格式
- 对象表示为键值对,数据由逗号隔开
- { " 键 " : " 值 " , " 键 " : " 值 " … }
- 花括号保存对象
- 方括号保存数组
- 对象表示为键值对,数据由逗号隔开
JSON 和 JavaScript 对象互转
- JSON 字符串转 JavaScript 对象
var obj=JSON.parse('{"a":"hello","b","world"}');
//结果为:{a:'hello',b:'world'}
- JavaScript 对象转 JSON 字符串
var json=JSON.stringify({a:'hello',b:'world'});
//结果为:'{"a":"hello","b","world"}'
<script>
let user={
id:1,
name:"小王",
age:17
};
let json=JSON.stringify(user);
console.log(json);
let parse = JSON.parse(json);
console.log(parse);
</script>
Controller 返回 JSON 数据
Jackson
- 前提:导入相关包
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.1</version>
<scope>compile</scope>
</dependency>
-
将对象转换为字符串返回到前端
@ResponseBody
代表该方法不会走视图解析器(即不会被拼接),而是直接返回该字符串
@RestController
代表该类下的所有方法都不会走视图解析器,不能与 @ResponseBody 同时使用
@Controller
public class UserController {
@RequestMapping("/j1")
@ResponseBody
public String test() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
User user = new User(1,"小钱",22);
String s = mapper.writeValueAsString(user);
return s;
}
}
关于输出 JSON 中的乱码问题
方法一:直接在 @RequestMapping 中设置 produces 的值为 application/json;charset=utf-8
@RequestMapping(value = "/j1",produces = "application/json;charset=utf-8")
方法二:在 springmvc 的配置文件中通过配置来解决乱码问题
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="utf-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
Fastjson
- 前提:导入相关包
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
<scope>compile</scope>
</dependency>
-
Fastjson 主要的类
JSONObject 代表 json 对象
JSONArray 代表 json 对象数组
JSON 代表 JSONObject 和 JSONArray 的转化
@RequestMapping("/j2")
public String test2() throws JsonProcessingException {
ArrayList<User> userArrayList = new ArrayList<>();
User user1 = new User(1, "小钱", 11);
User user2 = new User(2, "小我", 1);
User user3 = new User(3, "小熊", 41);
User user4 = new User(4, "小在", 18);
userArrayList.add(user1);
userArrayList.add(user2);
userArrayList.add(user3);
userArrayList.add(user4);
return JSON.toJSONString(userArrayList);
}