「SpringMVC入门Demo」SpringMVC + IDEA + Maven

前言

接上次的 MyBatis 入门书籍,这次实现的是 SpringMVC 的入门测试项目。环境与书中不同的地方是项目管理方式是 Maven、IDE 用了 IDEA。内容包括代码、相关知识解读、遇到的BUG记录及解决方式。

测试项目结构

「SpringMVC入门Demo」SpringMVC + IDEA + Maven_第1张图片

不止有 controller 层和 model 层,cn.com.mvc 中的正确文件层级简单查了下还应该有:dao、cons、domain、exception、service等。继续前进时会遇到,暂时不查那么详细。

代码

项目新建的方法,网上有太多太多了,我就不重复了。直接上代码。

pom.xml

Maven 配置

参考博客:https://www.cnblogs.com/Sinte-Beuve/p/5730553.html


<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.0modelVersion>

    <groupId>groupIdgroupId>
    <artifactId>SpringMVCTestartifactId>
    <version>1.0-SNAPSHOTversion>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <configuration>
                    <source>6source>
                    <target>6target>
                configuration>
            plugin>
        plugins>
    build>

    <dependencies>

    
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.12version>
    dependency>

    <dependency>
        <groupId>log4jgroupId>
        <artifactId>log4jartifactId>
        <version>1.2.17version>
    dependency>

    <dependency>
        <groupId>org.slf4jgroupId>
        <artifactId>slf4j-log4j12artifactId>
        <version>1.7.21version>
    dependency>

    
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-webartifactId>
        <version>4.3.10.RELEASEversion>
    dependency>

    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-webmvcartifactId>
        <version>4.3.10.RELEASEversion>
    dependency>

    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-txartifactId>
        <version>4.3.10.RELEASEversion>
    dependency>

    
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>5.1.43version>
    dependency>

    
    <dependency>
        <groupId>javax.servletgroupId>
        <artifactId>javax.servlet-apiartifactId>
        <version>3.1.0version>
    dependency>

    <dependency>
        <groupId>javax.servlet.jspgroupId>
        <artifactId>jsp-apiartifactId>
        <version>2.2version>
    dependency>

    <dependency>
        <groupId>javax.servletgroupId>
        <artifactId>jstlartifactId>
        <version>1.2version>
    dependency>

    
    
dependencies>
    
project>
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.xmlparam-value>
        init-param>
    servlet>

    <servlet-mapping>
        <servlet-name>SpringMVCservlet-name>
        <url-pattern>*.actionurl-pattern>
    servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.jspwelcome-file>
    welcome-file-list>

web-app>
SpringMVC.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 ">
    
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
    
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter " />
    
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" />
    
    <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 的配置样例:

    
    <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">userControllerprop>
                <prop key="product.action">productionControllerprop>
                <prop key="other.action">otherControllerprop>
            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 中的 处理器映射器适配器 修改为如下形式


    <bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter" />
    

此处可见我注释了之前使用的 SimpleControllerHandlerAdapter

同时, springmvc.xml 也需要做以下内容添加

 <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/queryFruits_test3.action">fruitsController2prop>
            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.csdn.net/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/ 查找正确版本号填写,或者像我一样不写版本号。

<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;

    <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>

你可能感兴趣的:(SpringMVC)