前言
接上次的 MyBatis 入门书籍,这次实现的是 SpringMVC 的入门测试项目。环境与书中不同的地方是项目管理方式是 Maven、IDE 用了 IDEA。内容包括代码、相关知识解读、遇到的BUG记录及解决方式。
测试项目结构
不止有 controller 层和 model 层,cn.com.mvc 中的正确文件层级简单查了下还应该有:dao、cons、domain、exception、service等。继续前进时会遇到,暂时不查那么详细。
代码
项目新建的方法,网上有太多太多了,我就不重复了。直接上代码。
pom.xml
Maven 配置
参考博客:https://www.cnblogs.com/Sinte-Beuve/p/5730553.html
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>groupId</groupId>
<artifactId>SpringMVCTest</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>6</source>
<target>6</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- 日志包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<!-- spring 配置 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<!-- mysql 配置 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.43</version>
</dependency>
<!-- j2ee 配置 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 参考 -->
<!-- https://www.cnblogs.com/Sinte-Beuve/p/5730553.html -->
</dependencies>
</project>
web.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">
<!-- SpringMvc 前端控制器 -->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 初始化参数 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:SpringMVC.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
SpringMVC.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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc/
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/scheama/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd ">
<!-- 1.处理器映射器 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<!-- 2.处理映射适配器 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter " />
<!-- 3.资源集解析视图 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" />
<!-- 4.bean具体配置 -->
<bean name="/queryFruits_test.action" class="cn.com.mvc.controller.FruitsControllerTest" />
</beans>
Handler处理器
package cn.com.mvc.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import cn.com.mvc.model.Fruits;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
// 实现 Controlller 接口
public class FruitsControllerTest implements Controller{
private FruitsService fruitsService = new FruitsService();
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws IOException {
// 模拟 Service 获取水果商品列表
List<Fruits> fruitsList = fruitsService.queryFruitsList();
// 返回 ModelAndView
ModelAndView modelAndView = new ModelAndView();
// 相当于request 的 setAttribute,在 JSP 页面中通过 fruitsList 获取数据
modelAndView.addObject("fruitsList", fruitsList);
// 指定视图
modelAndView.setViewName("WEB-INF/jsp/fruits/fruitsList.jsp");
return modelAndView;
}
}
// 模拟 Service 的内部类
class FruitsService {
public List<Fruits> queryFruitsList() {
List<Fruits> fruitsList = new ArrayList<Fruits>();
Fruits apple = new Fruits();
apple.setName("红富士苹果");
apple.setPrice(2.3);
apple.setProducing_area("山东");
Fruits banana = new Fruits();
banana.setName("香蕉");
banana.setPrice(1.5);
banana.setProducing_area("上海");
fruitsList.add(apple);
fruitsList.add(banana);
return fruitsList;
}
}
Fruits.java
Handler处理器用到的实体类,位于 cn.com.mvc.model 包中
macOS中set、get方法的快速构建方式:
- 写好变量类型、变量名;
- 使用cmd + n 快捷键(盲猜 windows 系统就是 ctrl + n,一般 win 中 ctrl 对应 macOS 中的 cmd,如有错误欢迎指正),选择 Getter and Setter 选项,按住 shift + 方向键下键 将遇到的变量都选中,enter。你的 get 和 set 方法就整齐的呈现出来了。
package cn.com.mvc.model;
public class Fruits {
private String name;
private double price;
private String producing_area;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getProducing_area() {
return producing_area;
}
public void setProducing_area(String producing_area) {
this.producing_area = producing_area;
}
}
fruitsList.jsp
<%@ page language="java" contentType="text/html; ISO-8859-1; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>水果列表</title>
</head>
<body>
<h3>新鲜水果</h3>
<table width="500px;" border=1>
<tr>
<td>名称</td>
<td>价格</td>
<td>产地</td>
</tr>
<c:forEach items="${fruitsList }" var="fruit">
<tr>
<td>${fruit.name }</td>
<td>${fruit.price }</td>
<td>${fruit.producing_area }</td>
</tr>
</c:forEach>
</table>
</body>
</html>
最后在本地 http://localhost:8080/TestMayWork/queryFruits_test.action 即可查询到相关的信息
相关知识点
处理器映射器 HandlerMapping
-
特点:不需开发;
-
作用:回应前端控制器 DispatcherServlet 的处理器映射器请求,为其寻找处理该请求的 Handler(带拦截器的 Handler);
-
映射规则:将 bean 的 name 作为 url 进行查找,需要在配置 Handler 时指定 beanname(就是 url);
-
类型:BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping、ControllerClassNameHandlerMapping;
-
配置方式:注解方式、非注解方式;
一个 SimpleUrlHandlerMapping 的配置样例:
<!-- simpleUrlHandlerMapping 通过内部参数配置请求的 url 和 handler 的映射关系-->
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="someCheckInceptor1" />
<ref bean="someCheckInceptor2" />
</list>
</property>
<property name="mappings">
<props>
<prop key="user.action">userController</prop>
<prop key="product.action">productionController</prop>
<prop key="other.action">otherController</prop>
</props>
</property>
</bean>
处理器适配器 HandlerAdapter
- 特点:需要实现;
- 作用:调用自己的 Handler 方法,利用 Java 的反射机制去执行具体的 Controller方法并获得 ModelAndView 视图对象;
- 类型: SimpleControllerHandlerAdapter(支持所有实现了 Controller 接口的 Handler 控制器)、HttpRquestHandlerAdapter (需实现 HttpRequestHandler 接口);
- 配置方式:注解方式、非注解方式;
一个实现 HttpRequestHandler 接口的 Handler 处理器 (非注解方式)
package cn.com.mvc.controller;
import cn.com.mvc.model.Fruits;
import org.springframework.web.HttpRequestHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
public class FruitsControllerTest2 implements HttpRequestHandler{
private FruitsService fruitsService = new FruitsService();
@Override
public void handleRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// 模拟 Service 获取水果商品列表
List<Fruits> fruitsList = fruitsService.queryFruitsList();
// 设置模型数据
request.setAttribute("fruitsList", fruitsList);
// 设置转发视图
request.getRequestDispatcher("WEB-INF/jsp/fruits/fruitsList.jsp").forward(request, response);
}
}
实现之前需要将 springmvc.xml 中的 处理器映射器适配器 修改为如下形式
<!-- 2.处理映射适配器 -->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter" />
<!-- <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter " />-->
此处可见我注释了之前使用的 SimpleControllerHandlerAdapter
同时, springmvc.xml 也需要做以下内容添加
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/queryFruits_test3.action">fruitsController2</prop>
</props>
</property>
</bean>
<bean id="fruitsController2" class="cn.com.mvc.controller.FruitsControllerTest2" />
(写 prop 标签的时候 IDE 会报错因为还没有写 名为 fruitsController2 的 bean,我没慌你呢?)
这样在本地 http://localhost:8080/TestMayWork/queryFruits_test3.action 即可看到相关的信息。
- 因为 response 的返回数据的格式可以改,这里不返回具体的 JSP 视图,将 List 拼接成 JSON 串,然后以 JSON 格式返回给用户,并使用 response 和 writer 对象直接写出返回数据:
修改后的 FruitsControllerTest2.java
package cn.com.mvc.controller;
import cn.com.mvc.model.Fruits;
import org.springframework.web.HttpRequestHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
public class FruitsControllerTest2 implements HttpRequestHandler{
private FruitsService fruitsService = new FruitsService();
@Override
public void handleRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// 模拟 Service 获取水果商品列表
List<Fruits> fruitsList = fruitsService.queryFruitsList();
// 将 fruitsList 转换为 JSON 串
String jsonInfo = convertListToJson(fruitsList);
// 设置返回格式
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
// 写出 JSON 串
response.getWriter().write(jsonInfo);
}
private String convertListToJson(List<Fruits> fruitsList) {
StringBuilder builder = new StringBuilder();
builder.append('[');
for (Fruits fruits:fruitsList) {
builder.append('{');
builder.append("\"name\":\"").append(fruits.getName()).append("\",");
builder.append("\"price\":\"").append(fruits.getPrice()).append("\",");
builder.append("\"producing_area\":\"").append(fruits.getProducing_area()).append("\"");
}
builder.deleteCharAt(builder.length()-1);
builder.append(']');
return builder.toString();
}
}
[http://localhost:8080/TestMayWork/queryFruits_test3.action]效果
[{"name":"红富士苹果","price":"2.3","producing_area":"山东"{"name":"香蕉","price":"1.5","producing_area":"上海]
(ps:写这个方法里面的格式效率很低,不知道有没有什么好的工具代写一下)
一个 Handler 处理器 (注解方式)
FruitsControllerTest3.java
package cn.com.mvc.controller;
import cn.com.mvc.model.Fruits;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
@Controller
public class FruitsControllerTest3 {
private FruitsService fruitsService = new FruitsService();
// 商品查询列表
// @RequestMapping 实现对 queryFruitsList 方法和 url 进行映射,一个方法对应一个 url
// 一般建议将 url 和 方法写成一样
@RequestMapping("/queryFruitsList")
public ModelAndView queryFruitsList() throws Exception {
// 模拟 Service 获取水果商品列表
List<Fruits> fruitsList = fruitsService.queryFruitsList();
// 返回 ModelAndView
ModelAndView modelAndView = new ModelAndView();
// 相当于request 的 setAttribute,在 JSP 页面中通过 fruitsList 获取数据
modelAndView.addObject("fruitsList", fruitsList);
// 指定视图
modelAndView.setViewName("/WEB-INF/jsp/fruits/fruitsList.jsp");
return modelAndView;
}
}
相关配置方式
springmvc.xml 中添加如下的配置:(开发常用,方便简单)
<mvc:annotation-driven></mvc:annotation-driven>
<context:component-scan base-package="cn.com.mvc.controller"></context:component-scan>
前端控制器 DispatcherServlet
-
特点:不需开发;
-
作用:根据用户在 web 端的请求,请求处理器映射器 Handler Mapping 寻找处理该请求的 Handler;
-
解读:本身就是一个 Servlet,它的顶级父类是 HttpServlet;
含有 doGet() 和 doPost() 方法;
最核心的功能由 doService 和 doDispatch 实现;
-
类型:初始化相关处理类的方法、相应 http 请求的方法、执行处理请求的方法;
-
DispatcherServlet 处理 url 请求的过程:
-
收到请求之后通过几级 Servlet 类型的父类的处理,最终调用顶级父类的 doDispatch 方法;
-
doDispatch 方法中,首先检测 request 是否包含多媒体类型,之后检测 processedRequest 对象是否为原始 request,然后将 boolean 结果赋给 multipartRequestParsed 变量;(这段不知所云)
-
通过 处理器映射器HandlerMapping 查找 Handler;
-
通过 HandlerExecutionChain 对象获取具体的 Handler 处理器对象;
-
调用 HandlerAdapter 对象的 handle 方法,将参数传入,handle 方法会根据开发者写好的代码处理,并返回含有反馈结果和结果视图信息的 ModelAndView 对象;
-
获得 ModelAndView 对象后,会进行视图渲染,将 model 数据填充到 request 域;
-
视图解析器 ViewResolver
- 作用:把一个逻辑上的视图名称解析为一个真正的视图,即将逻辑视图的名称解析为具体的 View 对象,让 View 对象去处理视图,并将带有返回数据的视图反馈给客户端;
- 类型:AbstractCachingViewResolver、UrlBasedViewResolver、InternalResourceViewResolver、XmlViewResolver等;
- ViewResovler链:order 属性可指定优先级,order 值越小,对应解析视图的优先级越高。
BUG及处理方式
java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet
-
问题原因:Maven 包导入正确;多方查找资料,这种情况下找不到 Jar 包的原因是:tomcat 会从 WEB-INF 的 lib 文件中加载 Jar 包,这是这只小猫加载的第一步。我一开始连 lib 的文件夹都没有建的,所以当然错了;
-
处理方式:https://blog.youkuaiyun.com/du_23tiyanwang/article/details/80654313?utm_source=copy
按照这个博主的方式设置即可,如果不成功将就在 WEB-INF 中先建一个 lib 文件夹,然后把 Available Elements 栏中含 Jar 包的文件夹:右键-Put into Output Root,这样 Jar 包就会稳稳进入 lib 中。
org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 18 in XML document from class path resource [SpringMVC.xml] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 18; columnNumber: 70; cvc-elt.1: 找不到元素 ‘beans’ 的声明
- 问题原因:SpringMVC.xml 文件中 schemaLocation 属性中的版本号写的有问题;
- 处理方式:http://www.springframework.org/schema/mvc/ 查找正确版本号填写,或者像我一样不写版本号。
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc/
http://www.springframework.org/schema/mvc/spring-mvc.xsd 没写版本号哦
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/scheama/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd ">
IDEA提示:no view resolvers found
- 问题原因:找不到对应的视图处理器。定位:配置文件 SpringMVC.xml;
<!-- 3.资源集解析视图 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" />
这里错了,一开始没仔细看,直接根据提示回车写成了:
<bean class="org.springframework.web.servlet.view.InternalResourceView" />
The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler
解决方式:
- xml头部的配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/scheama/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd ">
至少要写上 mvc 和 context 的配置,并生效(直观一点,IDEA 中变绿)
- xml中加入:
<mvc:annotation-driven></mvc:annotation-driven>
<context:component-scan base-package="cn.com.mvc.controller"></context:component-scan>