欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java之Spring mvc详解

程序员文章站 2022-06-28 20:18:58
文章大纲 一、Spring mvc介绍二、Spring mvc代码实战三、项目源码下载四、参考文章 一、Spring mvc介绍 1. 什么是springmvc springmvc是spring框架的一个模块,springmvc和spring无需通过中间整合层进行整合。springmvc是一个基于m ......

文章大纲

一、spring mvc介绍
二、spring mvc代码实战
三、项目源码下载
四、参考文章

 
Java之Spring mvc详解

一、spring mvc介绍

1. 什么是springmvc

  springmvc是spring框架的一个模块,springmvc和spring无需通过中间整合层进行整合。springmvc是一个基于mvc的web框架。

 
Java之Spring mvc详解

2. mvc设计模式在b/s系统 下的应用

 
Java之Spring mvc详解

3. spring mvc框架执行流程

 
Java之Spring mvc详解

  第一步:发起请求到前端控制器(dispatcherservlet)
  第二步:前端控制器请求handlermapping查找 handler,可以根据xml配置、注解进行查找,通过@requestmapping(value = "/test")中的test进行查找
  第三步:处理器映射器handlermapping向前端控制器返回handler
  第四步:前端控制器调用处理器适配器去执行handler
  第五步:处理器适配器去执行handler
  第六步:handler执行完成给适配器返回modelandview
  第七步:处理器适配器向前端控制器返回modelandview,modelandview是springmvc框架的一个底层对象,包括 model和view
  第八步:前端控制器请求视图解析器去进行视图解析,根据逻辑视图名解析成真正的视图(jsp)
  第九步:视图解析器向前端控制器返回view
  第十步:前端控制器进行视图渲染,视图渲染将模型数据(在modelandview对象中)填充到request域
  第十一步:前端控制器向用户响应结果

4. spring mvc组件介绍

(1)前端控制器dispatcherservlet(不需要程序员开发)
作用接收请求,响应结果,相当于转发器,*处理器。
有了dispatcherservlet减少了其它组件之间的耦合度。

(2)处理器映射器handlermapping(不需要程序员开发)
作用:根据请求的url查找handler

(3)处理器适配器handleradapter
作用:按照特定规则(handleradapter要求的规则)去执行handler

(4)处理器handler(需要程序员开发)
注意:编写handler时按照handleradapter的要求去做,这样适配器才可以去正确执行handler

(5)视图解析器view resolver(不需要程序员开发)
作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)

(6)视图view(需要程序员开发jsp)
view是一个接口,实现类支持不同的view类型(jsp、freemarker、pdf...)

二、spring mvc代码实战

  spring mvc常见使用功能有数据交互方式(modelandview和json)、静态资源的解析、参数校验、全局异常处理、拦截器、上传图片等。

1. 创建maven的javaweb项目

文章重点在于讲解spring mvc功能,因此创建项目方式不进行深入讲解,创建后的项目目录如下:

 
Java之Spring mvc详解

2. spring mvc基本配置

2.1 在pom.xml添加maven依赖

<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>springmvc_demo</groupid>
  <artifactid>springmvc_demo</artifactid>
  <version>0.0.1-snapshot</version>
  <packaging>war</packaging>
  <name/>
  <description/>

  <properties>
    <project.build.sourceencoding>utf-8</project.build.sourceencoding>
    <!-- spring版本号 -->
    <spring.version>4.2.5.release</spring.version>

  </properties>

  <dependencies>

    <dependency>
      <groupid>com.google.code.gson</groupid>
      <artifactid>gson</artifactid>
      <version>2.8.2</version>
    </dependency>


    <dependency>
      <groupid>org.apache.openejb</groupid>
      <artifactid>javaee-api</artifactid>
      <version>5.0-1</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupid>javax.servlet</groupid>
      <artifactid>jstl</artifactid>
      <version>1.2</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupid>javax.servlet.jsp</groupid>
      <artifactid>jsp-api</artifactid>
      <version>2.1</version>
      <scope>provided</scope>
    </dependency>
    <!-- 分页 -->
    <dependency>
      <groupid>com.github.pagehelper</groupid>
      <artifactid>pagehelper</artifactid>
      <version>4.1.4</version>
    </dependency>

    <!--测试包-->
    <dependency>
      <groupid>junit</groupid>
      <artifactid>junit</artifactid>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

    <!-- c3p0数据库连接池 -->
    <dependency>
      <groupid>c3p0</groupid>
      <artifactid>c3p0</artifactid>
      <version>0.9.1.2</version>
    </dependency>

    <!-- commons工具包 -->

    <!--图片上传相关的-->
    <dependency>
      <groupid>commons-fileupload</groupid>
      <artifactid>commons-fileupload</artifactid>
      <version>1.3.1</version>
    </dependency>
    <dependency>
      <groupid>commons-io</groupid>
      <artifactid>commons-io</artifactid>
      <version>2.6</version>
    </dependency>


    <dependency>
      <groupid>commons-beanutils</groupid>
      <artifactid>commons-beanutils</artifactid>
      <version>1.7.0</version>
    </dependency>
    <dependency>
      <groupid>commons-codec</groupid>
      <artifactid>commons-codec</artifactid>
      <version>1.7</version>
    </dependency>
    <dependency>
      <groupid>org.apache.commons</groupid>
      <artifactid>commons-io</artifactid>
      <version>1.3.2</version>
    </dependency>
    <dependency>
      <groupid>commons-collections</groupid>
      <artifactid>commons-collections</artifactid>
      <version>3.2</version>
    </dependency>
    <dependency>
      <groupid>commons-net</groupid>
      <artifactid>commons-net</artifactid>
      <version>3.0</version>
    </dependency>
    <dependency>
      <groupid>org.apache.commons</groupid>
      <artifactid>commons-math3</artifactid>
      <version>3.2</version>
    </dependency>
    <dependency>
      <groupid>commons-validator</groupid>
      <artifactid>commons-validator</artifactid>
      <version>1.4.0</version>
    </dependency>
    <dependency>
      <groupid>commons-httpclient</groupid>
      <artifactid>commons-httpclient</artifactid>
      <version>3.1</version>
    </dependency>
    <dependency>
      <groupid>commons-dbcp</groupid>
      <artifactid>commons-dbcp</artifactid>
      <version>1.4</version>
    </dependency>
    <dependency>
      <groupid>commons-logging</groupid>
      <artifactid>commons-logging-api</artifactid>
      <version>1.1</version>
    </dependency>
    <dependency>
      <groupid>commons-pool</groupid>
      <artifactid>commons-pool</artifactid>
      <version>1.6</version>
    </dependency>


    <!-- 添加spring核心依赖 -->
    <dependency>
      <groupid>org.springframework</groupid>
      <artifactid>spring-core</artifactid>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupid>org.springframework</groupid>
      <artifactid>spring-web</artifactid>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupid>org.springframework</groupid>
      <artifactid>spring-oxm</artifactid>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupid>org.springframework</groupid>
      <artifactid>spring-tx</artifactid>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupid>org.springframework</groupid>
      <artifactid>spring-jdbc</artifactid>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupid>org.springframework</groupid>
      <artifactid>spring-webmvc</artifactid>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupid>org.springframework</groupid>
      <artifactid>spring-context</artifactid>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupid>org.springframework</groupid>
      <artifactid>spring-context-support</artifactid>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupid>org.springframework</groupid>
      <artifactid>spring-aop</artifactid>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupid>org.springframework</groupid>
      <artifactid>spring-test</artifactid>
      <version>${spring.version}</version>
    </dependency>



    <dependency>
      <groupid>org.apache.httpcomponents</groupid>
      <artifactid>httpclient</artifactid>
      <version>4.5.2</version>
    </dependency>
    <dependency>
      <groupid>dom4j</groupid>
      <artifactid>dom4j</artifactid>
      <version>1.6.1</version>
    </dependency>
    <dependency>
      <groupid>org.apache.shiro</groupid>
      <artifactid>shiro-core</artifactid>
      <version>1.3.2</version>
    </dependency>
    <dependency>
      <groupid>org.apache.httpcomponents</groupid>
      <artifactid>httpmime</artifactid>
      <version>4.5.2</version>
    </dependency>

    <dependency>
      <groupid>com.thoughtworks.xstream</groupid>
      <artifactid>xstream</artifactid>
      <version>1.4.9</version>
    </dependency>

    <!-- 日志相关工具类导入 -->
    <dependency>
      <groupid>ch.qos.logback</groupid>
      <artifactid>logback-classic</artifactid>
      <version>1.1.7</version>
    </dependency>

    <dependency>
      <groupid>ch.qos.logback</groupid>
      <artifactid>logback-core</artifactid>
      <version>1.1.7</version>
    </dependency>



    <!-- validation校验-->
    <dependency>
      <groupid>javax.validation</groupid>
      <artifactid>validation-api</artifactid>
      <version>1.1.0.final</version>
    </dependency>
    <!-- validation校验-->
    <dependency>
      <groupid>org.hibernate</groupid>
      <artifactid>hibernate-validator</artifactid>
      <version>5.4.0.final</version>
    </dependency>

  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupid>org.apache.maven.plugins</groupid>
        <artifactid>maven-compiler-plugin</artifactid>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

2.2 web.xml中配置

<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
    <display-name></display-name>


    <!-- 解决post请求中文乱码 -->
    <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>



    <!--
    (1)dispatcherservlet是前端控制器设计模式的实现,提供spring web mvc的集中访问点,
    而且负责职责的分派,而且与spring ioc容器无缝集成
    (2)load-on-startup:表示启动容器时初始化该servlet;
    (3)url-pattern:表示哪些请求交给spring web mvc处理, “/” 是用来定义默认servlet映射的。
    也可以如“*.html”表示拦截所有以html为扩展名的请求。
    -->
    <servlet>
        <servlet-name>spring</servlet-name>
        <servlet-class>org.springframework.web.servlet.dispatcherservlet</servlet-class>
        <init-param>
            <!-- 如果不配置contextconfiglocation,则默认加载web-info下面的applicationcontext.xml -->
            <param-name>contextconfiglocation</param-name>
            <param-value>classpath:spring/applicationcontext.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!--
    配置方式有几种
    1. *.action,可以访问以.action结尾,由dispatcherservlet进行解析
    2. /,所有访问的地址由dispatcherservlet进行解析,对于静态文件的解析需要配置不让dispatcherservlet进行解析
    3. /*,这样配置不对,使用这种配置,最终要转发到一个jsp页面,仍然由dispatcherservlet解析jsp,不能根据jsp找到handler,结果错误
 -->
    <servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

温馨提示:上面代码中解决了post中文乱码问题,且添加了前端控制器等相应配置。

2.3 中文乱码解决
post方式乱码解决办法是在web.xml中添加以下代码

    <!-- 解决post请求中文乱码 -->
    <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>

get方式乱码解决方法如下:
(1)修改tomcat配置文件添加编码与工程编码一致,如下:

<connector uriencoding="utf-8" connectiontimeout="20000" port="8080" protocol="http/1.1" redirectport="8443"/>

(2)iso8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码

string username = new string(request.getparamter("username").getbytes("iso8859-1"),"utf-8")

2.4 resources文件夹下配置
新建配置文件logback.xml进行日志配置

<!-- ch.qos.logback.classic.filter.thresholdfilter  临界值过滤器, 过滤掉低于指定临界值的日志
ch.qos.logback.classic.filter.levelfilter   将过滤器的日志级别配置为info,所有info级别的日志交给appender处理,非info级别的日志,被过滤掉。 -->
<configuration>
    <property name="app_name" value="ssm_demo" /> //这里为此项目的日志文件夹名
    <property name="log.dir" value="f:/home"></property> //这里为日志的存储地址
    <timestamp key="bysecond" datepattern="yyyymmdd hhmmss"/>
    <contextname>${app_name}</contextname>
 
    <appender name="stdout" class="ch.qos.logback.core.consoleappender">
        <layout class="ch.qos.logback.classic.patternlayout">
            <pattern>%d{yyyy-mm-dd hh:mm:ss.sss} [%thread] %-5level %logger{85} [%file:%line] - %msg%n</pattern>
        </layout>
    </appender>
 
    <!-- 按日期和大小区分的滚动日志 -->
    <appender name="file_info" class="ch.qos.logback.core.rolling.rollingfileappender">
        <encoder>
            <pattern>%d{yyyy-mm-dd hh:mm:ss.sss} [%thread] %-5level %logger{85} - %msg%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.levelfilter">
            <level>info</level>
            <onmatch>accept</onmatch>
            <onmismatch>deny</onmismatch>
        </filter>
        <rollingpolicy class="ch.qos.logback.core.rolling.timebasedrollingpolicy">
            <filenamepattern>${log.dir}/${app_name}/info/info.%d{yyyy-mm-dd}-%i.log</filenamepattern>
            <maxhistory>30</maxhistory>
 
            <timebasedfilenamingandtriggeringpolicy
                    class="ch.qos.logback.core.rolling.sizeandtimebasedfnatp">
                <maxfilesize>10mb</maxfilesize>
            </timebasedfilenamingandtriggeringpolicy>
        </rollingpolicy>
    </appender>
 
 
    <!-- 按日期和大小区分的滚动日志 -->
    <appender name="file_debug" class="ch.qos.logback.core.rolling.rollingfileappender">
        <!-- 必须指定,否则不会往文件输出内容 -->
        <encoder>
            <pattern>%d{yyyy-mm-dd hh:mm:ss.sss} [%thread] %-5level %logger{85} - %msg%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.levelfilter">
            <level>debug</level>
            <onmatch>accept</onmatch>
            <onmismatch>deny</onmismatch>
        </filter>
 
        <!-- 必需要指定rollingpolicy 与 triggeringpolicy 属性   否则不会生成文件-->
        <rollingpolicy class="ch.qos.logback.core.rolling.timebasedrollingpolicy">
            <filenamepattern>${log.dir}/${app_name}/debug/debug.%d{yyyy-mm-dd}-%i.log</filenamepattern>
            <maxhistory>30</maxhistory>
 
            <timebasedfilenamingandtriggeringpolicy
                    class="ch.qos.logback.core.rolling.sizeandtimebasedfnatp">
                <maxfilesize>10mb</maxfilesize>
            </timebasedfilenamingandtriggeringpolicy>
 
        </rollingpolicy>
    </appender>
 
 
    <!-- error级别只按日期滚动生成日志 -->
    <appender name="file_error" class="ch.qos.logback.core.rolling.rollingfileappender">
        <!-- 必须指定,否则不会往文件输出内容 -->
        <encoder>
            <pattern>%d{yyyy-mm-dd hh:mm:ss.sss} [%thread] %-5level %logger{85} - %msg%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.thresholdfilter">
            <level>error</level>
            <!--    <onmatch>accept</onmatch>
                <onmismatch>deny</onmismatch>-->
        </filter>
 
        <!-- 必需要指定rollingpolicy 与 triggeringpolicy 属性   否则不会生成文件-->
        <rollingpolicy class="ch.qos.logback.core.rolling.timebasedrollingpolicy">
            <filenamepattern>${log.dir}/${app_name}/error/error.%d{yyyy-mm-dd}-%i.log</filenamepattern>
            <maxhistory>30</maxhistory>
            <timebasedfilenamingandtriggeringpolicy class="ch.qos.logback.core.rolling.sizeandtimebasedfnatp">
                <maxfilesize>10mb</maxfilesize>
            </timebasedfilenamingandtriggeringpolicy>
        </rollingpolicy>
        <!-- 默认值是10mb。 -->
        <!--     <triggeringpolicy class="ch.qos.logback.core.rolling.sizebasedtriggeringpolicy">
                  <maxfilesize>5mb</maxfilesize>
            </triggeringpolicy>  -->
    </appender>
 
    <!-- 滚动记录文件 -->
    <appender name="monitor" class="ch.qos.logback.core.rolling.rollingfileappender">
        <encoder>
            <pattern>%d{yyyy-mm-dd hh:mm:ss.sss} [%thread] %-5level %logger{85} - %msg%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.thresholdfilter">
            <level>debug</level>
        </filter>
        <rollingpolicy class="ch.qos.logback.core.rolling.timebasedrollingpolicy">
            <filenamepattern>${log.dir}/${app_name}/monitor/monitor.%d{yyyy-mm-dd}-%i.log</filenamepattern>
            <maxhistory>30</maxhistory>
            <timebasedfilenamingandtriggeringpolicy
                    class="ch.qos.logback.core.rolling.sizeandtimebasedfnatp">
                <maxfilesize>10mb</maxfilesize>
            </timebasedfilenamingandtriggeringpolicy>
        </rollingpolicy>
    </appender>
 
    <logger name="org" level="info" />  <!--将org包下面的所有日志级别设为了error -->
    <logger name="monitor" additivity="false" level="debug" />
 
    <logger name="monitor" additivity="false" level="debug">
        <appender-ref ref="monitor" />
    </logger>
 
    <root level="debug">
        <appender-ref ref="stdout" />
        <appender-ref ref="file_info" />
        <appender-ref ref="file_debug" /> //上线时 这个需注释掉,debug级别的日志
        <appender-ref ref="file_error" />
    </root>
</configuration>

添加spring mvc的xml配置
  在resources文件夹下新建spring文件夹,新建applicationcontext.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"
    xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- 这样classpath:properties目录下的.properties文件就会被spring加载 -->
    <context:property-placeholder location="classpath:properties/*.properties"/>
    
    <!--
        对于spring配置文件的编写,我想,对于经历过庞大项目的人,都有那种恐惧的心理,太多的配置文件。
        不过,分模块都是大多数人能想到的方法,但是,怎么分模块,那就是仁者见仁,智者见智了。我的策略是使用import。 
        下面的配置, 再resources/spring目录下的以applicationcontext开头的xml文件将全部被加载
     -->
    <import resource="applicationcontext-*.xml"/>
</beans>

新建applicationcontext-web.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 http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

</beans>

温馨提示:
我们可能会在其他的项目中看到ssm(spring、spring mvc、mybatis)的配置文件都在同一个xml文件中,但是这样不好管理,在该文章中,我们的spring文件夹中的配置文件采用解耦方式进行配置,在web.xml中进行总的配置加载,再分别加载持久层、应用层、逻辑层,正常配置后的文件结构如下:

 
Java之Spring mvc详解

3. spring mvc创建两种方式

3.1 通过实现controller方式
在applicationcontext-web.xml中添加以下代码,testcontroller2为该controller访问的地址

    <!--配置handler -->
    <bean
            name="/testcontroller2" class="com.wxc.controller.testcontroller2"
    />

在web-inf文件夹下新建jsp文件夹,之后新建myjsp.jsp

<%@ page language="java" import="java.util.*" pageencoding="iso-8859-1"%>
<%
string path = request.getcontextpath();
string basepath = request.getscheme()+"://"+request.getservername()+":"+request.getserverport()+path+"/";
%>

<!doctype html public "-//w3c//dtd html 4.01 transitional//en">
<html>
  <head>
    <base href="<%=basepath%>">
    
    <title>this is a new page</title>
    
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->

  </head>
  
  <body>
  this is a new page<br>
  </body>
</html>

在com.wxc.controller文件夹下新建testcontroller2类并实现controller

package com.wxc.controller;

import org.springframework.web.servlet.mvc.controller;
import org.springframework.web.servlet.modelandview;

import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;


/**
 * 测试spring mvc创建第二种方式  通过实现controller,且在xml文件中进行配置
 */
public class testcontroller2  implements controller {

    public modelandview handlerequest(httpservletrequest arg0,
                                      httpservletresponse arg1) throws exception {

        //返回modelview
        modelandview modelandview = new modelandview();

        //指定视图
        modelandview.setviewname("/web-inf/jsp/items/myjsp.jsp");

        return modelandview;
    }

}

创建后文件目录如下:

 
Java之Spring mvc详解

运行项目,访问结果如下:

 
Java之Spring mvc详解

3.2 通过注解方式
在applicationcontext-web.xml添加以下代码:

    <!-- 
        开启注解映射的支持
        开启mvc注解   相当于
        <bean class="org.springframework.web.servlet.mvc.method.annotation.requestmappinghandlermapping"/>
        <bean class="org.springframework.web.servlet.mvc.method.annotation.requestmappinghandleradapter"/>
     -->
    <mvc:annotation-driven/>

    <!-- 自动扫描的包名 -->
    <context:component-scan base-package="com.wxc.controller"/>

在com.wxc.controller包下创建testcontroller.java

package com.wxc.controller;

import com.wxc.vo.testvo;
import org.springframework.stereotype.controller;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.requestmethod;
import org.springframework.web.bind.annotation.responsebody;
import org.springframework.web.servlet.modelandview;

import java.util.hashmap;
import java.util.map;

/**
 * 测试springmvc第一种方式  注解方式
 */
@controller
@requestmapping("/testcontroller")
public class testcontroller {

    @requestmapping(value = "/test")
    @responsebody
    public testvo test()
    {

        system.out.println("接口请求,已处理");

        testvo testvo = new testvo("1", "成功");

        return testvo;
    }

    @requestmapping(value = "/test2")
    @responsebody
    public modelandview test2(){

        //返回modelview
        modelandview modelandview = new modelandview();

        //指定视图
        modelandview.setviewname("/web-inf/jsp/items/myjsp.jsp");

        return modelandview;
    }

    /**
     * 测试返回集合
     * @return
     */
    @requestmapping(method = requestmethod.post, value="/test3")
    @responsebody
    public map<string, object> test3()
    {

        map<string, object> outmap = new hashmap<string, object>();

        outmap.put("测试1", "ceshi1");

        outmap.put("测试2","ceshi2");

        return outmap;
    }
}

运行结果如下:

 
Java之Spring mvc详解

4. spring mvc应用层数据交互方式

  应用层数据交互方式包括jsp(modelandview)与json,其中返回值为modelandview表示返回jsp,如果添加@responsebody注解表示返回json数据,具体查看3.2中代码。

5. spring mvc实现文件上传与访问

5.1 配置tomncat虚拟磁盘目录访问文件
在tomcat的配置文件server.xml下添加以下代码:

<context docbase="g:\ssm\spring mvc\daima\picture" path="/picture" reloadable="false"/>

添加后截图如下:

 
Java之Spring mvc详解

温馨提示:
(1)docbase代表本地磁盘的文件地址
(2)path表示访问的目录地址

5.2 pom.xml文件添加maven依赖

    <!--图片上传相关的-->
    <dependency>
      <groupid>commons-fileupload</groupid>
      <artifactid>commons-fileupload</artifactid>
      <version>1.3.1</version>
    </dependency>
    <dependency>
      <groupid>commons-io</groupid>
      <artifactid>commons-io</artifactid>
      <version>2.6</version>
    </dependency>

5.3 applicationcontext-web.xml添加配置

    <!-- 文件上传 -->
    <bean id="multipartresolver"
          class="org.springframework.web.multipart.commons.commonsmultipartresolver">
        <!-- 设置上传文件的最大尺寸为5mb -->
        <property name="maxuploadsize">
            <value>5242880</value>
        </property>
    </bean>

5.4 新建测试类picturecontroller.java

package com.wxc.controller;

import org.springframework.stereotype.controller;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.responsebody;
import org.springframework.web.multipart.multipartfile;

import java.io.file;
import java.util.uuid;

/**
 * 图片上传相关
 *
 * 第一步:配置tomncat虚拟目录访问图片
 *
 * 第二步:导入maven相关jar包
 *
 * 第三步:xml中配置相关参数
 *
 */
@controller
@requestmapping("/picturecontroller")
public class picturecontroller {

    //商品修改提交
    @requestmapping("/edititemsubmit")
    @responsebody
    public string edititemsubmit(multipartfile picturefile)throws exception {

        //原始文件名称
        string picturefile_name = picturefile.getoriginalfilename();
        //新文件名称
        string newfilename = uuid.randomuuid().tostring() + picturefile_name.substring(picturefile_name.lastindexof("."));

        //上传图片
        file uploadpic = new java.io.file("g:/ssm/spring mvc/daima/picture/" + newfilename);

        if (!uploadpic.exists()) {
            uploadpic.mkdirs();
        }
        //向磁盘写文件
        picturefile.transferto(uploadpic);

        return "true";
    }
}

运行结果如下:

 
Java之Spring mvc详解
 
Java之Spring mvc详解
 
Java之Spring mvc详解

6. 全局异常处理

  通过 @controlleradvice 注解,我们可以在一个地方对所有 @controller 注解的控制器进行管理。注解了 @controlleradvice 的类的方法可以使用 @exceptionhandler、 @initbinder、 @modelattribute 注解到方法上,这对所有注解了 @requestmapping 的控制器内的方法都有效。
  本文通过全局统一的异常处理将自定义错误码以json的形式发送给前端。

6.1 新建统一返回结果类 apiresult.java
定义一个统一结果返回类,最终需要将这个结果类的内容返回给前端。

package com.wxc.vo;

import com.wxc.enums.resultcode;

/**
 * api统一的返回结果类
 */
public class apiresult {
    /**
     * 结果码
     */
    private string code;

    /**
     * 结果码描述
     */
    private string msg;


    public apiresult() {

    }

    public apiresult(resultcode resultcode) {
        this.code = resultcode.getcode();
        this.msg = resultcode.getmsg();
    }

    /**
     * 生成一个apiresult对象, 并返回
     *
     * @param resultcode
     * @return
     */
    public static apiresult of(resultcode resultcode) {
        return new apiresult(resultcode);
    }

    public string getcode() {
        return code;
    }

    public void setcode(string code) {
        this.code = code;
    }

    public string getmsg() {
        return msg;
    }

    public void setmsg(string msg) {
        this.msg = msg;
    }

    @override
    public string tostring() {
        return "apiresult{" +
                "code='" + code + '\'' +
                ", msg='" + msg + '\'' +
                '}';
    }
}

6.2 新建错误码枚举类 resultcode.java
有了 apiresult ,接下来需要定义一个枚举类, 来包含所有自定义的结果码

package com.wxc.enums;

/**
 * 错误码
 */
public enum resultcode {

    /**
     * 成功
     */
    success("0", "success"),

    /**
     * 未知错误
     */
    unknown_error("0x10001", "unkonwn error"),

    /**
     * 用户名错误或不存在
     */
    username_error("0x10002", "username error or does not exist"),

    /**
     * 密码错误
     */
    password_error("0x10003", "password error"),

    /**
     * 用户名不能为空
     */
    username_empty("0x10004", "username can not be empty");

    /**
     * 结果码
     */
    private string code;

    /**
     * 结果码描述
     */
    private string msg;


    resultcode(string code, string msg) {
        this.code = code;
        this.msg = msg;
    }

    public string getcode() {
        return code;
    }

    public string getmsg() {
        return msg;
    }
}

6.3 自定义业务异常类 businessruntimeexception.java
接下来需要定义我们自己的业务异常类,以后和业务相关的异常通通抛出这个异常类,我们将错误码枚举变量的值存于其中。

package com.wxc.exception;

import com.wxc.enums.resultcode;

/**
 * 自定义业务异常
 */
public class businessruntimeexception extends runtimeexception {

    /**
     * 结果码
     */
    private string code;

    /**
     * 结果码描述
     */
    private string msg;

    /**
     * 结果码枚举
     */
    private resultcode resultcode;


    public businessruntimeexception(resultcode resultcode) {
        super(resultcode.getmsg());
        this.code = resultcode.getcode();
        this.msg = resultcode.getmsg();
        this.resultcode = resultcode;
    }

    public string getcode() {
        return code;
    }

    public void setcode(string code) {
        this.code = code;
    }

    public string getmsg() {
        return msg;
    }

    public void setmsg(string msg) {
        this.msg = msg;
    }

    public resultcode getresultcode() {
        return resultcode;
    }

    public void setresultcode(resultcode resultcode) {
        this.resultcode = resultcode;
    }
}

6.4 新建全局异常处理类 globalexceptionresolver.java
说明:
(1)通过 @controlleradvice 指定该类为 controller 增强类。
(2)通过 @exceptionhandler 自定捕获的异常类型。
(3)通过 @responsebody 返回 json 到前端
(4)注意一点:被@controlleradvice注解的全局异常处理类也是一个 controller ,我们需要配置扫描路径,确保能够扫描到这个controller。

package com.wxc.exception;

import com.wxc.enums.resultcode;
import com.wxc.vo.apiresult;
import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springframework.stereotype.controller;
import org.springframework.web.bind.annotation.controlleradvice;
import org.springframework.web.bind.annotation.exceptionhandler;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.responsebody;

/**
 * 全局controller层异常处理类
 */
@controlleradvice
public class globalexceptionresolver {

    private static final logger log = loggerfactory.getlogger(globalexceptionresolver.class);

    /**
     * 处理所有不可知异常
     *
     * @param e 异常
     * @return json结果
     */
    @exceptionhandler(exception.class)
    @responsebody
    public apiresult handleexception(exception e) {
        // 打印异常堆栈信息
        log.error(e.getmessage(), e);
        return apiresult.of(resultcode.unknown_error);
    }

    /**
     * 处理所有业务异常
     *
     * @param e 业务异常
     * @return json结果
     */
    @exceptionhandler(businessruntimeexception.class)
    @responsebody
    public apiresult handleopdruntimeexception(businessruntimeexception e) {
        // 不打印异常堆栈信息
        log.error(e.getmsg());
        return apiresult.of(e.getresultcode());
    }
}

配置完成后的项目结构如下:

 
Java之Spring mvc详解

6.5 新建测试类catchcontroller.java

package com.wxc.controller;

import com.wxc.enums.resultcode;
import com.wxc.exception.businessruntimeexception;
import org.springframework.stereotype.controller;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.responsebody;

/**
 * 测试异常抛出
 *
 * 参考博客:https://blog.csdn.net/hbtj_1216/article/details/81102063
 */
@controller
@requestmapping("/catchcontroller")
public class catchcontroller {

    /**
     * 测试返回异常信息
     * @return
     */
    @requestmapping(value = "/test")
    public void test() {

        throw new businessruntimeexception(resultcode.username_error);

    }
}

6.6 运行结果如下:

 
Java之Spring mvc详解

7. spring mvc参数绑定

7.1 支持类型介绍
spring mvc支持的绑定方式包括以下:
默认支持的参数类型
处理器形参中添加如下类型的参数处理适配器会默认识别并进行赋值。
(1)httpservletrequest:通过request对象获取请求信息
(2)httpservletresponse:通过response处理响应信息
(3)httpsession:通过session对象得到session中存放的对象
(4)model/modelmap:modelmap是model接口的实现类,通过model或modelmap向页面传递数据,如下:

简单类型
包括布尔类型、字符串、单双精度、整型等,下面重点讲解@requestparam
通过@requestparam对简单类型的参数进行绑定。
如果不使用@requestparam,要求request传入参数名称和controller方法的形参名称一致,方可绑定成功。
如果使用@requestparam,不用限制request传入参数名称和controller方法的形参名称一致。
通过required属性指定参数是否必须要传入,如果设置为true,没有传入参数,报下边错误:

 
Java之Spring mvc详解
 
Java之Spring mvc详解

pojo
将pojo对象中的属性名于传递进来的属性名对应,如果传进来的参数名称和对象中的属性名称一致则将参数值设置在pojo对象中

             页面定义如下;
            
              <input type="text" name="name"/>
             <input type="text" name="price"/>
             
              contrller方法定义如下:
             
              @requestmapping("/edititemsubmit")
                public string edititemsubmit(items items)throws exception{
                system.out.println(items);

自定义参数绑定

集合绑定

7.2 创建测试类parameterbindingcontroller.java

package com.wxc.controller;

import com.wxc.vo.pojovo;
import com.wxc.vo.testvo;
import org.springframework.stereotype.controller;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.requestmethod;
import org.springframework.web.bind.annotation.requestparam;
import org.springframework.web.bind.annotation.responsebody;

import java.util.hashmap;
import java.util.map;

/**
 * 讲解spring mvc参数绑定
 * spring mvc支持的绑定方式包括:
 * 1. 默认支持的参数类型
 *      处理器形参中添加如下类型的参数处理适配器会默认识别并进行赋值。
         (1)httpservletrequest:通过request对象获取请求信息
         (2)httpservletresponse:通过response处理响应信息
         (3)httpsession:通过session对象得到session中存放的对象
         (4)model/modelmap:modelmap是model接口的实现类,通过model或modelmap向页面传递数据,如下:

         (2)//调用service查询商品信息
         items item = itemservice.finditembyid(id);
         model.addattribute("item", item);
         页面通过${item.xxxx}获取item对象的属性值。
         使用model和modelmap的效果一样,如果直接使用model,springmvc会实例化modelmap。

 * 2. 简单类型
 *      包括布尔类型、字符串、单双精度、整型等,下面重点讲解@requestparam
 *      使用@requestparam常用于处理简单类型的绑定。
 *
         * value:参数名字,即入参的请求参数名字,如value=“item_id”表示请求的参数区中的名字为item_id的参数的值将传入;
         * required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报;
         * ttp status 400 - required integer parameter 'xxxx' is not present
         *
         * defaultvalue:默认值,表示如果请求中没有同名参数时的默认值
         *
         * 定义如下:
         * public string edititem(@requestparam(value="item_id",required=true) string id) {
         *
         * }
         *
         * 形参名称为id,但是这里使用value=" item_id"限定请求的参数名为item_id,所以页面传递参数的名必须为item_id。
         * 注意:如果请求参数中没有item_id将跑出异常:
         * http status 500 - required integer parameter 'item_id' is not present
         *
         * 这里通过required=true限定item_id参数为必需传递,如果不传递则报400错误,可以使用defaultvalue设置默认值,即使required=true也可以不传item_id参数值
 * 3. pojo
 *          将pojo对象中的属性名于传递进来的属性名对应,如果传进来的参数名称和对象中的属性名称一致则将参数值设置在pojo对象中
             *
             * 页面定义如下;
             *
             * <input type="text" name="name"/>
             * <input type="text" name="price"/>
             *
             * contrller方法定义如下:
             *
             * @requestmapping("/edititemsubmit")
             *  public string edititemsubmit(items items)throws exception{
             *  system.out.println(items);
             * 请求的参数名称和pojo的属性名称一致,会自动将请求参数赋值给pojo的属性。
 * 4. 自定义参数绑定
 *
 * 5. 集合类
 *
 */
@controller
@requestmapping("/parameterbindingcontroller")
public class parameterbindingcontroller {

    //method = requestmethod.get表示限定只能通过get方式进行访问,如果通过post访问则报错:
    //http status 405 - request method 'post' not supported
    //返回值是json数据,字符编码为utf-8
    @requestmapping(method = requestmethod.get, value="/test")
    @responsebody
    public testvo test(int id, @requestparam(value="item_name",required=true) string name)
    {
        testvo testvo = new testvo(id+"", name);

        return testvo;
    }

    //method = requestmethod.get表示限定只能通过get方式进行访问,如果通过post访问则报错:
    //http status 405 - request method 'post' not supported
    //返回值是json数据,字符编码为utf-8
    @requestmapping(method = requestmethod.post, value="/test2")
    @responsebody
    public pojovo test2(pojovo vo)
    {
        return vo;
    }

}

8. spring mvc拦截器

8.1 applicationcontext-web.xml添加以下配置

    <!--拦截器 -->
    <mvc:interceptors>
        <!--多个拦截器,顺序执行 -->
        <mvc:interceptor>
            <!-- /**表示所有url包括子url路径 -->
            <mvc:mapping path="/**"/>
            <bean class="com.wxc.interceptor.handlerinterceptor1"></bean>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.wxc.interceptor.handlerinterceptor2"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

8.2 新建拦截器
handlerinterceptor1.java

package com.wxc.interceptor;

import org.springframework.web.servlet.handlerinterceptor;
import org.springframework.web.servlet.modelandview;

import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;

/**
 *description:测试拦截器1
 */
public class handlerinterceptor1 implements handlerinterceptor {

    
    //进入 handler方法之前执行
    //用于身份认证、身份授权
    //比如身份认证,如果认证通过表示当前用户没有登陆,需要此方法拦截不再向下执行
    @override
    public boolean prehandle(httpservletrequest request,
                             httpservletresponse response, object handler) throws exception {
        
        system.out.println("handlerinterceptor1...prehandle");
        
        //return false表示拦截,不向下执行
        //return true表示放行
        return true;
    }

    //进入handler方法之后,返回modelandview之前执行
    //应用场景从modelandview出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图
    @override
    public void posthandle(httpservletrequest request,
                           httpservletresponse response, object handler,
                           modelandview modelandview) throws exception {
        
        system.out.println("handlerinterceptor1...posthandle");
        
    }

    //执行handler完成执行此方法
    //应用场景:统一异常处理,统一日志处理
    @override
    public void aftercompletion(httpservletrequest request,
                                httpservletresponse response, object handler, exception ex)
            throws exception {
        
        system.out.println("handlerinterceptor1...aftercompletion");
    }

}

handlerinterceptor2.java

package com.wxc.interceptor;

import org.springframework.web.servlet.handlerinterceptor;
import org.springframework.web.servlet.modelandview;

import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;

/**
    description:测试拦截器2
 */
public class handlerinterceptor2 implements handlerinterceptor {

    
    //进入 handler方法之前执行
    //用于身份认证、身份授权
    //比如身份认证,如果认证通过表示当前用户没有登陆,需要此方法拦截不再向下执行
    @override
    public boolean prehandle(httpservletrequest request,
                             httpservletresponse response, object handler) throws exception {
        
        system.out.println("handlerinterceptor2...prehandle");
        
        //return false表示拦截,不向下执行
        //return true表示放行
        return true;
    }

    //进入handler方法之后,返回modelandview之前执行
    //应用场景从modelandview出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图
    @override
    public void posthandle(httpservletrequest request,
                           httpservletresponse response, object handler,
                           modelandview modelandview) throws exception {
        
        system.out.println("handlerinterceptor2...posthandle");
        
    }

    //执行handler完成执行此方法
    //应用场景:统一异常处理,统一日志处理
    @override
    public void aftercompletion(httpservletrequest request,
                                httpservletresponse response, object handler, exception ex)
            throws exception {
        
        system.out.println("handlerinterceptor2...aftercompletion");
    }

}

8.3 创建后项目结构如下:

 
Java之Spring mvc详解

8.4 随便访问项目接口

 
Java之Spring mvc详解
 
Java之Spring mvc详解

9. spring mvc之参数校验

9.1 校验理解
项目中,通常使用较多是前端的校验,比如页面中js校验。对于安全要求较高点建议在服务端进行校验。
服务端校验:
控制层conroller:校验页面请求的参数的合法性。在服务端控制层conroller校验,不区分客户端类型(浏览器、手机客户端、远程调用)
业务层service(使用较多):主要校验关键业务参数,仅限于service接口中使用的参数。
持久层dao:一般是不校验的。

9.2 pom.xml添加maven依赖

<!-- validation校验-->
    <dependency>
      <groupid>javax.validation</groupid>
      <artifactid>validation-api</artifactid>
      <version>1.1.0.final</version>
    </dependency>
    <!-- validation校验-->
    <dependency>
      <groupid>org.hibernate</groupid>
      <artifactid>hibernate-validator</artifactid>
      <version>5.4.0.final</version>
    </dependency>

9.3 applicationcontext-web.xml配置校验器

<!-- 校验器 -->
    <bean id="validator"
          class="org.springframework.validation.beanvalidation.localvalidatorfactorybean">
        <!-- hibernate校验器-->
        <property name="providerclass" value="org.hibernate.validator.hibernatevalidator" />
        <!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用classpath下的validationmessages.properties -->
        <property name="validationmessagesource" ref="messagesource" />
    </bean>
    <!-- 校验错误信息配置文件 -->
    <bean id="messagesource"
          class="org.springframework.context.support.reloadableresourcebundlemessagesource">
        <!-- 资源文件名-->
        <property name="basenames">
            <list>
                <value>classpath:customvalidationmessages.properties</value>
            </list>
        </property>
        <!-- 资源文件编码格式 -->
        <property name="fileencodings" value="utf-8" />
        <!-- 对资源文件内容缓存时间,单位秒 -->
        <property name="cacheseconds" value="120" />
    </bean>

9.4 customvalidationmessages.properties添加错误信息

#\u6dfb\u52a0\u6821\u9a8c\u9519\u8bef\u63d0\u4ea4\u4fe1\u606f
items.name.length.error=\u8bf7\u8f93\u51651\u523030\u4e2a\u5b57\u7b26\u7684\u5546\u54c1\u540d\u79f0
items.createtime.isnull=\u8bf7\u8f93\u5165 \u5546\u54c1\u7684\u751f\u4ea7\u65e5\u671f

9.5 创建测试实体类validationvo.java

package com.wxc.vo;

import javax.validation.constraints.size;

/**
 * 测试校验的实体类
 */
public class validationvo {

    private integer id;

    //校验名称在1到30字符中间
    //message是提示校验出错显示的信息
    @size(min=1,max=30,message="{items.name.length.error}")
    private string name;

    public integer getid() {
        return id;
    }

    public string getname() {
        return name;
    }

    public void setid(integer id) {
        this.id = id;
    }

    public void setname(string name) {
        this.name = name;
    }

    public validationvo(integer id, string name) {
        this.id = id;
        this.name = name;
    }

    public validationvo() {
    }
}

9.6 创建校验测试类validationcontroller.java

package com.wxc.controller;

import com.wxc.enums.resultcode;
import com.wxc.exception.businessruntimeexception;
import com.wxc.vo.validationvo;
import org.springframework.stereotype.controller;
import org.springframework.validation.bindingresult;
import org.springframework.validation.objecterror;
import org.springframework.validation.annotation.validated;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.responsebody;

import java.util.list;

/**
 * 校验测试类
 */

@controller
@requestmapping("/validationcontroller")
public class validationcontroller {

    @requestmapping(value="/test")
    @responsebody
    public validationvo test(@validated validationvo vo, bindingresult bindingresult)
    {

        if(bindingresult.haserrors())
        {
            //实际开发采用采用下面方式获取错误信息
            //输出错误信息
            list<objecterror> allerrors = bindingresult.getallerrors();

            for (objecterror objecterror : allerrors) {
                // 输出错误信息
                system.out.println(objecterror.getdefaultmessage());

            }

            throw new businessruntimeexception(resultcode.username_error);
        }

        return vo;
    }
}

运行及访问结果如下
(1)校验通过情况:

 
Java之Spring mvc详解

(2)校验不通过情况:

 
Java之Spring mvc详解

10. spring mvc配置静态资源访问

  我们在配置前端控制器时候,可能是所有资源都进行拦截,所以导致静态资源无法访问,所以需要通过以下方式配置可以直接访问静态资源
10.1 web-inf新建文件夹image,并存放photo.png

 
Java之Spring mvc详解

10.2 applicationcontext-web.xml添加配置

<mvc:resources location="/web-inf/image/" mapping="/image/**" />

10.3 运行后访问结果如下:

 
Java之Spring mvc详解

11. springmvc和struts2的区别

(1)springmvc基于方法开发的,struts2基于类开发的。
springmvc将url和controller方法映射。映射成功后springmvc生成一个handler对象,对象中只包括了一个method。方法执行结束,形参数据销毁。springmvc的controller开发类似service开发。
(2)springmvc可以进行单例开发,并且建议使用单例开发,struts2通过类的成员变量接收参数,无法使用单例,只能使用多例。
(3)经过实际测试,struts2速度慢,在于使用struts标签,如果使用struts建议使用jstl。

三、项目源码下载

链接:https://pan.baidu.com/s/1ahudryyumj1yiybqjhpppq
提取码:s8r1

四、参考文章

    1. http://yun.itheima.com/course/8.html
    2. https://blog.csdn.net/hbtj_1216/article/details/81102063