SpringMVC入门总结

SpringMVC入门总结

博客:

image-20210723173712034

地址:https://www.cnblogs.com/ShanYu-Home/

目录表

目录

SpringMVC基础知识

一,MVC设计模式

  • 视图层(View):负责格式化数据并把它们呈现给用户,包括数据展示、用户交互、数据验证、界面设计等功能。
  • 控制层(Controller):负责接收并转发请求,对请求进行处理后,指定视图并将响应结果发送给客户端。
  • 数据模型层(Model):模型对象拥有最多的处理任务,是应用程序的主体部分,它负责数据逻辑(业务规则)的处理和实现数据操作(即在数据库中存取数据)。

MVC应该是耳熟能详的基本知识,从最开始学习JavaEE开始,最早接触的就是JSP+JavaBean的开发模式,JSP 用于处理用户请求,JavaBean 用于封装和处理数据。

JSP+JavaBean

学过JSP+JavaBean之后又接触Servlet+JSP+JavaBean的开发模式

Servlet+JSP+JavaBean 模式的结构清晰,是一个松耦合架构模式

Servlet+JSP+JavaBean

我记得在这这本书,里面说Servlet消灭了大量的JSP中的代码,在实际学习过程中确实深有体会(但是仍然不能否认这是本十八流教材)。

image-20210808201937602

二.SpringMVC的特点

为了使Spring可插入的MVC架构,SpringFrameWork在Spring基础上开发SpringMVC框架,从而在使用Spring进行WEB开发时可以选择使用Spring的SpringMVC框架作为web开发的控制器框架

需要注意的是Spring基础上开发的框架所以在环境依赖就会有Spring的身影,并且在SpringMVC使用过程中会出现很多Spring 的设计思想(只是在学完配置环境时候的想法)

在C语言中文网教程中给出了Spring MVC 是结构最清晰的 Servlet+JSP+JavaBean 的实现,是一个典型的教科书式的 MVC 构架的评价

优点有

  • 清晰地角色划分,Spring MVC 在 Model、View 和 Controller 方面提供了一个非常清晰的角色划分,这 3 个方面真正是各司其职,各负其责。
  • 灵活的配置功能,可以把类当作 Bean 通过 XML 进行配置。
  • 提供了大量的控制器接口和实现类,开发者可以使用 Spring 提供的控制器实现类,也可以自己实现控制器接口。
  • 真正做到与 View 层的实现无关。它不会强制开发者使用 JSP,可以根据项目需求使用 Velocity、FreeMarker 等技术。
  • 国际化支持
  • 面向接口编程
  • 与 Spring 框架无缝集成

说这么多,好用就完事儿了,比之前直接使用Servlet开发又快又优雅

三.SpringMVC的运行流程

数据流模型图

1.请求:从客户端来的信息到达DispatcherServlet,DispatcherServlet拦截所有请求发送给处理器映射

2.解析:数据从DispatcherServlet到处理器映射就是处理url中的信息,比如/后面的名字,比如/Sayhello 它会解析到Sayhello这个Controller,包括多种Controller跳转,如/Hello/Sayhello。

3.返回Controller信息,包括名字和参数。

4.传递Controller信息到处理器适配器,它会检索控制器。选择合适参数的对应控制器

5.根据处理器适配器的信息,发送信息到控制器

6.控制器做好数据处理,通常的步骤有

  • 收集数据

  • 调用业务对象

  • 响应处理

    做好处理之后会带着视图和数据模型(就是需要返回给用户并在浏览器上显示的信息,通常还会格式化)返回给处理器适配器

7.处理器适配器处理好视图名字(也就是return的字符串)给DispatcherServlet

8.DispatcherServlet带着视图名字给视图解析器,它会检索视图(比如JSP,HTML之类的),之后组装成URL.。

9.数据返回给DispatcherServlet

10.DispatcherServlet带着数据模型和URL组装成视图

11.响应:程序作出响应也就时我们看到了网页响应

注:SpringMVC的三大组件:处理器适配器,视图解析器,处理器映射器

四,第一个SpringMVC程序

1.环境配置

1.项目的创建

​ (坑)选择Module Webapp,注意不要选错了,是Maven标准的

image-20210808212516127

(坑)在此处可以最好更换为自己的仓库,以及加上archetypeCatalog=internal 否则构建项目极慢(因为下载不了某个配置文件)

image-20210808212729693

创建好项目之后记得补全Maven标准目录

image-20210808213014742

2.插件的准备

为了提高效率和心情愉悦,必须安排JRebel等热部署插件,大大缩短编码等待时间

image-20210808213429414

由于插件收费,作为初学者实在负担不起

所以如下步骤激活

1.生成一个GUID

​ 可以使用这个地址获取Generate GUIDs online (guidgen.com)

2.根据反向代理服务器地址拼接激活地址

​ 服务器地址: https://jrebel.qekang.com/{GUID}

​ 如果失效刷新GUID替换就可以

来自:博客园-工匠精神

3.Tomcat的配置

​ 在这里直接选择Tomcat即可,进入后选择自己的项目部署,不多说了。

image-20210808213855452

4.Maven依赖

​ 之前提到过需要Spring依赖,所以Spirng必须有再加上Spring-MVC和Spring-web以及Servlet-api,由于maven的特性会自动的下载相互依赖的Jar包所以只需要添加 SpringMVC和Servlet的依赖

2.配置SpringMVC

1.web.xml中配置DispatcherServlet

如下配置

<servlet>
    <!--配置Servlet -->
   <servlet-name>SpringMVC</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
   <init-param>
     <param-name>contextConfigLocation</param-name>
    <!--确定Spring配置文件 此处使用了通配的写法-->
     <param-value>classpath:/SpringMVC/SpringMvc_*.xml</param-value>
   </init-param>
 </servlet>
 <servlet-mapping>
   <servlet-name>SpringMVC</servlet-name>
     <!--指定相对于Servlet的URL的路径,此处特别注意!-->  
   <url-pattern>/</url-pattern>
 </servlet-mapping>

细节:在指定指定相对于Servlet的URL的路径时,SpringMVC使用的是“/”,而不是“/*”

/ 模式下,Servlet不会拦截 .jsp(仅限于此,.html仍会被拦截)格式的请求;

而 /* 模式才是真正意义上的拦截所有形式的请求。

查看Tomcat的配置文件

发现web容器拦截了.jsp(Tomcat默认的servlet)

所以使用/*会发现转发不了页面,出现404

image-20210808225502877

2.配置SpringMVC_config.xml

SpringMVC是Spring的衍生产品,所以使用和Spring 一样的DTD即可,由于idea的自带模板不全所以直接用下面的DTD

<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-4.2.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.2.xsd">
 <beans/>

开发SpringMVC使用注解开发所以需要加入注解扫描、

 <!--注解扫描-->
<context:component-scan base-package="#方式自定"/>
<!--创建处理器映射器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" id="requestMappingHandlerMapping"/>
<!--c'jian-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" id="requestMappingHandlerAdapter"/>
<!--创建视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
         <!--也就是转发到什么样的页面,比如页面的逻辑名字(Cotroller返回的字符串)为index则组装出来的Url为 /index.jsp-->
        <!--组装前缀-->
        <property name="prefix" value="/"/>
        <!--组装后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>

配置方案mvc:annotation-driven

但是SpringMVC加入< mvc:annotation-driven/>配置方案,可以让初学都快速应用默认配置方案,包括了

  • 会自动注册RequestMappingHandlerMapping与RequestMappingHandlerAdapter
  • 提供了数据绑定支持
  • @NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持读写XML的支持(JAXB)和读写JSON的支持(默认Jackson)等功能

所以我们可以写成

 	<context:component-scan base-package="#方式自定"/>
    <mvc:annotation-driven/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

3.编写Controller

1.@Controller和@RequestMapping

@Controller:是@Component的衍生注解,作用是创建对象

@RequestMapping:是请求路径,其中有个值为value,用于指定某个Controller的位置

  • 修饰范围: 用在方法或者类上
  • 注解作用: 用来指定类以及类中方法的请求路径
  • 注解位置:注解在类上则给所有方法给了一个统一的请求路径,在访问方法时需要加上类的路径

2.编写控制器类

@Controller("test")
//加在类上以后先访问类
//@RequestMapping(value = "HelloGroup")
public class TestController {
    @RequestMapping(value = "Sayhello")
    public  String SayHello(){
        System.out.println("TestController.SayHello");
        //页面逻辑名字
        return "index";
    }
}

3.测试

我们输入一下URL

image-20210808232451722

可以看见正常转发

接着取消第三行注释

image-20210808232551861

发现转发失败

再接着加上类的访问路径

image-20210808232633380

又可以看见正常转发

SpringMVC中的跳转和参数接收

一,servlet跳转方式

1.forward:请求转发

​ 服务器内部跳转,跳转url不变,跳转可以使用request作用域传递数据

2.redirect:请求重定向

在客户端内跳转,跳转之后url变化,不能传递数据

二,SpringMVC跳转方式

1.在相同的Controller内跳转

1.forward跳转到页面

默认就是forward跳转 语法: return "页面逻辑名"或者 return "forward:xxx/xxx"

例如:

    @Controller("test")
    //加在类上以后先访问类
    @RequestMapping(value = "HelloGroup")
    public class TestController {
        @RequestMapping(value = "Sayhello")
        public  String SayHello(){
            System.out.println("TestController.SayHello");
            return "index";
        }
        @RequestMapping(value = "SayHello2")
        public String SayHello2(){
            System.out.println("TestController.SayHello2");
            return "forward:/HelloGroup/Sayhello";
        }

访问URL如下

http://localhost:8080/SpringMVC01_war_exploded/HelloGroup/SayHello2

控制台打印如下

    TestController.SayHello2
    TestController.SayHello

可见访问路径

2.redirect跳转到页面

使用springmvc提供redirect:关键字进行重定向页面跳转
语法: return "redirect:目的视图全命名" (包括/和后缀)
注意: 使用redirect跳转页面不会经过试图解析器,因为指定了目的视图,无需组装

例如:

@RequestMapping(value = "RedirectHello")
public String RedirectHello(){
    System.out.println("TestController.RedirectHello");
    return "redirect:/index.jsp";
}

使用灵活可以自由搭配

2.在不同的Controller内跳转

使用和上面的完全一致,只不过是把逻辑地址换为了其他的Controller而已,非常简单

三,SpringMVC中参数接收

(注)这段主要是从JSP-->SpringMVC

1.普通JDK类型,包括util日期

SpringMVC传送单个或者多个参数的方法很简单,只需要把Controller 的方法形参对应上列表即可,还会进行类型转换,像Spring 在某些时候还需要自己些类型转换器,比如Util的Date

例如:

@Controller
@RequestMapping(value = "Param")
public class ParamController {
    @RequestMapping(value = "JdkParam")
    public String JDKParam(String name, Integer age, double price, Date bir){
        System.out.println("name = " + name);
        System.out.println("age = " + age);
        System.out.println("price = " + price);
        System.out.println("bir = " + bir);
        return "index";
    }

url如下:

# GET 方式传递参数http://localhost:8080/SpringMVC01_war_exploded/Param/JdkParam?name=SY&price=1.1&bir=2020/1/1&age=11

控制台打印如下:

    name = SY
    age = 11
    price = 1.1
    bir = Wed Jan 01 00:00:00 CST 2020

2.数组类型

只需要请求的每个key相同(重要)就代表了传送给一个数组

例如:

@RequestMapping("Arry")
public String ArryParam(String[]Param){
    for (String p:Param
         ) {
        System.out.println("p = " + p);
    }
return "index";
}

url如下:

# GET 方式传递参数http://localhost:8080/SpringMVC01_war_exploded/Param/Arry?Param=A&Param=b&Param=C&Param=D

打印:

    p = A
    p = b
    p = C
    p = D

3.自定义类型

对于拥有大量数据需要传送就不太可能使用Url,所以就使用对象传送,对象传送SpringMVC会自动匹配成员变量

例如:

我的自定义对象User

public class User {
    String name;
    String id;
    String age;
    //提供GET/SET方法和toStirng
    xxx
}

控制器方法

@RequestMapping(value = "ObjectParam")
public String ObjectParam(User user){
    System.out.println(user.toString());
    return "index";
}

url为:

# GET 方式传递参数http://localhost:8080/SpringMVC01_war_exploded/Param/ObjectParam?name=SY&id=32&age=99

打印为:

    User{name='SY', id='32', age='99'}

4.集合类型

SpringMVC不支持使用形参列表直接传送参数

SpringMVC推荐使用VO传送集合对象方法和上面自定义类型一样,不过多操作了,只需要把集合类型作为自定义类型的参数即可

5.表单传送参数

需要值得注意的表单传送的数据和url传送数据方式类似,所以如果需要传送自定义对象,数组,集合只需要把表单中的name作为url的KEY即可

例如(以个别数据和数组为例)

个别数据:

<form action="Param/JdkParam" method="POST">
    id:<input type="text" name="id"><br>
    name:<input type="text" name="name"><br>
    age:<input type="text" name="age"><br>
    price:<input type="text" name="price"><br>
    <input type="submit" value="confirm">
</form>

接受参数代码在【三.1】中

结果为,可以看见有乱码下面解决

    name = ?±±è??
    age = 123
    price = 1.1
    bir = null

数组:

<form action="Param/Arry" method="POst">
    id:<input type="text" name="Param"><br>
    name:<input type="text" name="Param"><br>
    age:<input type="text" name="Param"><br>
    price:<input type="text" name="Param"><br>
    <input type="submit" value="confirm">
</form>

接受参数代码在【三.2】中

结果为

    p = 32
    p = ?±±è??
    p = 12
    p = 2.0

四,POST乱码问题解决方式

在学习servlet之前也有乱码情况,通过设置请求域编码,过滤器等方式,在SpringMVC也差不多。

在tomcat8以前版本的GET方式会出现乱码,原因是在web.xml配置文件中URIEncoding是Unicode现在设置为UTF-8即可

<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/>

1.Filter

过滤器

public class EncodingFilter implements Filter {
    private String code;
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
       this.code= filterConfig.getInitParameter("encoding");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding(this.code);
        response.setCharacterEncoding(this.code);
        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {
    }
}

web.xml

<filter>
  <filter-name>character</filter-name>
  <filter-class>Filter.EncodingFilter</filter-class>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>GBK</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>character</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

2.Spring提供的过滤器

web.xml 注意param-name参数不要填错了

<filter>
  <filter-name>charset</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>charset</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

五,SpringMVC中的数据传递

(注)这段从SpringMVC-->Jsp

1.JSTL和EL

之前没怎么关注这个,现在发现还是比较重要,重新学习和总结了下

1.JSTL

< c:set>

< c:set> 等同于JSP中的 session的setAttribute() 方法

使用方法:<c:set var=”名字” value=”值”/>

< c:out>

< c:out> 等同于 JSP中的 <%= %>

使用方法:<c:out value=”需要输出的值”>

< c:remove>

< c:remove> 和c:set标签正好相反,c:set标签是设置setAttrbute 这个标签则是 删除 Attrbute中设置的值 等同于 session中的removeAttrbute()方法

使用方法:<c:remove var=”名字”/>

< c:if>

< c:if> 等同于 java 中的 if 语句

使用方法:<c:if test=”条件”> 满足条件执行的JSP代码 < /c:if>

< c:catch>

< c:catch> 类似于 java 中的 try

使用方法:<c:catch var=”名字”>JSP代码< /c:catch>

< c:choose> < c:when>< c:otherwise>

< c:choose> 和 < c:when> 类似于 java 中的 switch 和 case

使用方法:(c:choose中可以有多个c:when标签)

<c:choose>
<c:when test=”条件”>处理</c:when>
<c:when test=”条件”>处理</c:when>
<c:otherwise>处理</c:otherwise>    
</c:choose>

< c:otherwise> 等同于 java switch 中的 default

< c:import>

检索一个绝对或相对 URL,然后将其内容暴露给页面

< c:forEach> 和 < c:forTokens>

类似于JAVA中的 FOR循环 和 FOREACH循环

forEach 和forTokens 语法格式

<c:forEach
    items="<object>"
    begin="<int>"
    end="<int>"
    step="<int>"
    var="<string>"
    varStatus="<string>">
    ...

属性

< c:forEach>标签有如下属性:

属性 描述 是否必要 默认值
items 要被循环的信息
begin 开始的元素(0=第一个元素,1=第二个元素) 0
end 最后一个元素(0=第一个元素,1=第二个元素) Last element
step 每一次迭代的步长 1
var 代表当前条目的变量名称
varStatus 代表循环状态的变量名称

< c:forTokens>标签与< c:forEach>标签有相似的属性,不过< c:forTokens>还有另一个属性:

属性 描述 是否必要 默认值
delims 分隔符
< c:redirect>

类似于service中的重定向

使用方法:<c:redirect url=”#地址”/>

2.EL

EL表达式定义规则:以 $ 开头 内容写在 {} 中 例: ${test}

操作符 描述
. 访问一个Bean属性或者一个映射条目
[] 访问一个数组或者链表的元素
( ) 组织一个子表达式以改变优先级
+
- 减或负
*
/ or div
% or mod 取模
== or eq 测试是否相等
!= or ne 测试是否不等
< or lt 测试是否小于
> or gt 测试是否大于
<= or le 测试是否小于等于
>= or ge 测试是否大于等于
&& or and 测试逻辑与
|| or or 测试逻辑或
! or not 测试取反
empty 测试是否空值

如果要动态取值 可以使用[]

比如:${session.user[param]}

1.EL表达式查找顺序(都是针对Attrbute()):

如果使用类似于 ${username} 没有指定哪个范围

那么它会以:

  1. Page
  2. Request
  3. Session
  4. Application

为顺序来进行查找,加入中途找到了 username 那么就会返回值 如果一路没找到 返回 null

2.EL表达式的隐性变量:
隐含对象 描述
pageScope page 作用域
requestScope request 作用域
sessionScope session 作用域
applicationScope application 作用域
param Request 对象的参数,字符串
paramValues Request对象的参数,字符串集合
header HTTP 信息头,字符串
headerValues HTTP 信息头,字符串集合
initParam 上下文初始化参数
cookie Cookie值
pageContext 当前页面的pageContext

param和paramValues

param对象用于获取请求参数的某个值,它是Map类型,与request.getParamter()方法相同,如:url传送的数据

例:我们想要获得GET或则POST传递过来的name参数,在以前我们只能使用:

request.getParameter(name);

使用EL表达式可代替为:

${param.name}

如果一个请求参数有多个值,可以使用paramValues对象来获取请求参数的所有值

例:num请求参数有多个值

<form action="${pageContext.request.contextPath}/param.jsp">
		num1:<input type="text" name="num1"><br /> 
		num2:<input type="text" name="num"><br /> 
		num3:<input type="text" name="num"><br /> <br />
		<input type="submit" value="提交" />&nbsp;&nbsp;
		<input type="submit" value="重置" />
		<hr>
		num1:${param.num1}<br />
		num2:${paramValues.num[0]}<br />
		num3:${paramValues.num[1]}<br />
	</form>

参考:

EL表达式中的param和paramValues对象

JSTL和EL的使用

JSP 表达式语言_w3cschool

2.SpringMVC数据传送机制

数据存储位置 数据如何获取 数据如何展示在JSP
Servlet request,session,application EL EL+JSTL
Struts2 request,session,application EL EL+JSTL
SpringMVC request,session,application EL EL+JSTL

1.存储数据的作用域和SpringMVC获取作用域

1.存储数据

跳转方式

forward:使用Request作用域或者SpirngMVC提供的Model对象(对Request的封装)

redire: session,application(一直保存到程序消亡,少用),通过url问号传送数据

2.获取作用域(重要)

直接在Controller里面声明HttpServletRequest,HttpServletResponse变量即可

public class ScopeController {
    public String finfAll(HttpServletRequest request, HttpServletResponse response){
 		xxx   
        return "xxx";
    }
}

3.数据传递实例

1.forward

1.传递零散数据

Controller:

/*
* 实例一:使用request传递零散数据
* 只需要使用request.setAttribute()即可
* */
String name="sy";
Integer id=32;
request.setAttribute("name",name);
request.setAttribute("id",id);

JSP:

<!--使用EL表达式中requestScope接收数据-->
<h3>测试request作用域传送一般数据</h3>
name: ${requestScope.name}<br>
id: ${requestScope.id}<br>
2.传送对象

Controller:

/**
 * 实例二:request作用域传送对象
 */
User user=new User();
user.setName("SY1");
user.setId("132");
request.setAttribute("User",user);

JSP:

<h3>测试request作用域对象</h3>
name:${requestScope.User.name}
id:${requestScope.User.id}
3.传送集合

Controller:

/**
 *实例三:request作用域传送集合
 * Arrays.asList用于快速组成集合
 */
User user1=new User();
user1.setName("SY2");
user1.setId("232");
List<User> Users= Arrays.asList(user,user1);
request.setAttribute("Users",Users);

JSP:

<!--遍历方法要搭配JSTL,要记住JSP用来展示数据,不要做逻辑处理-->
<h3>测试request作用传送集合</h3>
<c:forEach items="${requestScope.Users}" var="S">
    id:${S.id} & name:${S.name}<br>

2.redirect

1.传送零散数据

Controller:

/**
 *redirect方式使用URL传送数据
 *  URLEncoder.encode解决中文编码问题
 *  request.getSession().setAttribute()设定k-v
 */
String name="山芋";
String id="32";
 return "redirect:/TestScope.jsp?name="+ URLEncoder.encode(name,"UTF-8") +"&id="+id;

JSP:

<!--接受url传送的数据-->
<h3>测试redirect传送数据</h3>
name:${param.name}<br>
id:${param.id}
2.使用ssesion传送

Controller:

String name="山芋";
String id="32";
request.getSession().setAttribute("name",name);
request.getSession().setAttribute("id",id);

JSP:

<!--接受session的数据-->
<h3>测试redirect-session传送数据</h3>
name:${sessionScope.name}<br>
id:${sessionScope.id}
3.使用ssesion传送对象

ssession有”传什么送什么“的特性

Controller:

User user=new User();
user.setName("SY");
user.setId("32");
request.getSession().setAttribute("user",user);

JSP:

<h3>测试传送对象</h3>
name:${sessionScope.user.name}===id:${sessionScope.user.id}

集合也是一样的不过多赘述

现在再次回去看SpringMVC的数据传送机制,更加理解

Spring SpringMVC Mybaits的整合

整合思路

大体分为三步开发

更为详细的可以参考之前的Spring笔记的Spring和Mybatis的整合

这里只是加入Service层调用DAO层的一些实现

Spring和Mybaits的整合

以及Mybaits的配置

环境配置-语句查询-对Mybatis如何创建出对象的思考

配置Spring SpringMVC也同样可以参考之前的笔记

SpringMVC基础知识

这里只是打通这个流程方便快速查阅,非常简单只不过是对之前的知识进行联系和衔接而已,建议自己多搭建几次熟悉流程

Spring SpringMVC Mybaits的整合

1.Spring和Mybaits的整合

1.建立数据库表

ID NAME EMAIL AGE
XXX XX XXXXXXX XX

2.建立实体类

  • 实体类成员变量要和数据库中字段保持一致
public class StudentBean {
    Integer id;
    String name;
    String email;
    Integer age;
    //节约篇幅 提供SET/GET方法

3.建立DAO接口

public interface StuentDAO {
    public void save(StudentBean student);
    public List<StudentBean> findAll();
}

4.配置Mapper

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.Dao.StuentDAO">
    <!--save-->
    <insert id="save" parameterType="com.Entiry.StudentBean">
        insert into student (id,name,email,age) values( #{id},#{name},#{email},#{age})
    </insert>
    <!--findall-->
    <select id="findAll" resultType="com.Entiry.StudentBean">
        select * from student
    </select>
</mapper>

5.建立Service接口

public interface StuentService {
    List<StudentBean>Findall();
    void save(StudentBean studentBean);
}

6.编写Service接口的实现类

@Service("studentimpl")
public class StudentImpl implements StuentService{
    @Autowired
    private StuentDAO stuentDAO;
    @Override
    public List<StudentBean> Findall() {
        return stuentDAO.findAll();
    }

    @Override
    public void save(StudentBean studentBean) {
        studentBean.setId(1);
        studentBean.setName("SY");
        stuentDAO.save(studentBean);
    }
}

7.引入SM配置文件

	<!--指定注解扫描方式-->
    <context:component-scan base-package="com"/>
    <bean id="dataSourse" class="com.alibaba.druid.pool.DruidDataSource" >
        <property name="username" value="root"/>
        <property name="url" value="jdbc:mysql://localhost:3306/jdbc_test?useSSL=false&amp;allowPublicKeyRetrieval=true&amp;serverTimezone=UTC"/>
        <property name="password" value="123456"/>
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    </bean>
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--指定数据源-->
        <property name="dataSource" ref="dataSourse"/>
        <!--类型别名,日后直接用类名充当-->
        <property name="typeAliases" value="com.Entiry.StudentBean"/>
        <!--指定Mapper位置-->
        <property name="mapperLocations" >
            <list>
                <value>
                    <!--通配写法,按照这个命名规范书写-->
                    classpath:Mapper/*Mapper.xml
                </value>
            </list>
        </property>
    </bean>
    <bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--对应上面的SqlSessionFactoryBean的名字-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
        <!--MapperScannerConfigurer到DAO下的包自动的找到对应DAO接口来创建对象-->
        <!--注意!在通过Spring工厂获取对象时使用的时接口名首字母小写,我们在接口命名时就需要约定接口名首字母大写-->
        <property name="basePackage" value="com.Dao"/>
    </bean>
</beans>

2.Spring和SpringMVC的整合

1.配置web.xml

<web-app>
  <display-name>Archetype Created Web Application</display-name>
    <!--Spring工厂监听器-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:Spring*.xml</param-value>
  </context-param>
    <!--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>
    <!--SpringMVC字符处理-->
  </servlet>
  <servlet-mapping>
    <servlet-name>
      SpringMVC
    </servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  <filter>
    <filter-name>charset</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>charset</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

</web-app>

2.SpringMVC的核心配置文件

<context:component-scan base-package="com"/>
<mvc:annotation-driven/>
 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>

3.开发Contoller和视图

1.Controller

@Controller
@RequestMapping(value = "stuController")
public class StudentController {
        SqlSession sqlSession = null;
    	//注入Service对象
        @Autowired
        private StuentService service;
    	//查询所有操作
        @RequestMapping(value = "findall")
    public String findall(HttpServletRequest request){
        List<StudentBean>findall=service.Findall();
        request.setAttribute("findall",findall);
        return  "findall";
    }
   		 //插入操作
    @RequestMapping(value = "addinfo")
    public String add(StudentBean studentBean){
            try {
                service.save(studentBean);
                return  "redirect:/stuController/findall";
            }catch (Exception e)
            {
                e.printStackTrace();
                sqlSession.rollback();
                return "redirect:/Add.jsp";
            }

    }
}

2.视图

查询所有的视图

<html>
<head>
    <title>Title</title>
</head>
<body>
    <!--JSTL遍历-->
<c:forEach items="${requestScope.findall}" var="fa">
    ${fa.name}==${fa.id}==${fa.age}==${fa.email}<br>
</c:forEach>
</body>
</html>

添加视图

<head>
    <title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/stuController/addinfo" method="post">
    用户名:<input type="text" name="name"/><br>
    id:<input type="text" name="id"/><br>
    email:<input type="text" name="email"/><br>
    年龄:<input type="text" name="age"/><br>
    <input type="submit" value="confirm">
</form>
</body>
</html>

SpringMVC的文件上传,下载,静态资源处理

一,静态资源处理

SpringMVC在DispatcherServlet使用/会导致拦截静态资源(HTML,CSS,JS...)也就导致找不到静态文件

所以建议处理方式有一下两种:

1.在SpringMVC配置文件中使用默认处理方式

<mvc:default-servlet-handler/>

2.修改Spring的全局拦截设置为*.xxx的拦截(在web.xml)

<servlet>
 2     <servlet-name>SpringMVC</servlet-name>
 3     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
 4     <init-param>
 5         <param-name>contextConfigLocation</param-name>
 6         <param-value>classpath:spring-mvc.xml</param-value>
 7     </init-param>
 8     <load-on-startup>1</load-on-startup>
 9     <async-supported>true</async-supported>
10 </servlet>
11 <servlet-mapping>
12     <servlet-name>SpringMVC</servlet-name>
13     <url-pattern>*.xxx</url-pattern>
14 </servlet-mapping>

SpringMVC只会拦截.xxx结尾的请求

二,文件上传

1.环境配置

需要Jar包

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>

2.配置文件

引入CommonsMultipartResolver用于处理文件上传

MultipartFile 封装了请求数据中的文件,此时这个文件存储在内存中或临时的磁盘文件中,需要将其转存到一个合适的位置,因为请求结束后临时存储将被清空。在 MultipartFile 接口中有如下方法:

  • String getName(); // 获取参数的名称
  • String getOriginalFilename(); // 获取文件的原名称
  • String getContentType(); // 文件内容的类型
  • boolean isEmpty(); // 文件是否为空
  • long getSize(); // 文件大小
  • byte[] getBytes(); // 将文件内容以字节数组的形式返回
  • InputStream getInputStream(); // 将文件内容以输入流的形式返回
  • void transferTo(File dest); // 将文件内容传输到指定文件中

注意:Beanid必须为multipartResolver

还可以设置上传文件的大小限制

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
      <property name="maxUploadSize" value="2097152"/>
</bean>

参考:SpringMVC工作原理

3.编写上传表单

其中注意的是:

enctype="multipart/form-data"指定为上传文件

input 类型为file 名字为传递到conetroller的名字,应该和controller形参对应起来

<body>
    <form action="#对应Controller" method="post" enctype="multipart/form-data">
        上传:<input name="FileLoad" type="file"><br>
            <input type="submit" value="确认">
    </form>
</body>

4.编写Controller

注意点:

  • 传入 request是为了获取路径
  • MultipartFile的transferTo是保存文件操作参数为File类型
@Controller
@RequestMapping(value = "uploadController")
public class UploadController {
    @RequestMapping(value = "FileLoad")
    public  String fileLoad(MultipartFile FileLoad, HttpServletRequest request) throws IOException {
        /*
        **只是为了测试上传的信息
        System.out.println("filename = " + FileLoad.getOriginalFilename());
        System.out.println("fileSize = " + FileLoad.getSize());
        System.out.println("fileType = " + FileLoad.getContentType());
        */
        //1.获取存储目的路径
        String realPath = request.getSession().getServletContext().getRealPath("/UplodaResourse");
        //2.修改文件名的操作 使用UUID避免文件重名
        String FName= FilenameUtils.getExtension(FileLoad.getOriginalFilename());
        String FileName=UUID.randomUUID().toString()+"."+FName;
       	//3.保存文件
        FileLoad.transferTo(new File(realPath,FileName));

        return "UpLoadPage/Upload";
    }
}

小坑:使用idea时如果在服务器根目录下创建文件夹存放上传的文件,如果文件夹为空则idea不会部署空文件夹到服务器,可以随便放点东西进去

三,文件下载

我觉得下载有点困难,涉及的东西比较多,比如读,找,写,传输,缓冲

开发步骤:

  1. 确定哪些文件提供给用户下载
  2. 把确定的文件放入服务器下载目录
  3. 开发一个视图提供下载链接
  4. 开发下载的控制器(难)

1.开发视图

提供一个url带上值Filename给控制器,让控制器知道确定的下载文件

<body>
<a href="${pageContext.request.contextPath}/download/Resourse?Filename=LoadTXT.txt">下载LoadTXT.txt</a>
</body>
</html>

2.开发控制器

核心:

  1. 获取文件名字,从url传值方式获得
  2. 获取存放文件的文件夹位置
  3. 由文件名字和位置读取文件输入流
  4. 设置响应相关参数
  5. 通过response对象获取OutputStream流
  6. 设置缓冲区
    • 将FileInputStream流写入到buffer缓冲区
    • 使用OutputStream将缓冲区的数据输出到客户端浏览器
  7. 关闭流

获取文件名字,从url传值方式获得

在形参列表设置和url的key对应即可

//片段一:
public String DownLoadResourse(String Filename, HttpServletRequest request, HttpServletResponse response) throws IOException {
    //用于测试
    //System.out.println("Filename = " + Filename);

}

获取存放文件的文件夹位置

依然使用request作用域获取相对位置

//片段二:
String realpath=request.getSession().getServletContext().getRealPath("/DownLoadReourse");
//用于测试
//System.out.println("realpath = " + realpath);

由文件名字和位置读取文件的输入流

FileInputStream fileInputStream=new FileInputStream((new File(realpath,Filename)));

设置响应相关参数(也是常用的参数)

**Response.setContentType ** (表示后面的文档属于什么MIME类型,Servlet默认为text/plain)

设置发送到客户端的响应的内容类型,决定浏览器将以什么形式、什么编码读取这个文件。

作用是使得客户端的浏览器区分不同种类的数据,并根据不同的MIME调用浏览器内不同的程序嵌入模块来处理相应的数据。

因为流只是读入数据,那么数据还得有对应的程序来处理,比如这里MIME设置为text/plain,那么浏览器就会按照TXT后缀文件处理

Tomcat的安装目录\conf\web.xml 中就定义了大量MIME类型可以查看对应的K-V来确定响应类型

response.setHeader (设置HTTP协议中的头)

当Content-Type 的类型为要下载的类型时 , 这个信息头会告诉浏览器这个文件的名字和类型。

value:attachment意味着消息体应该被下载到本地;大多数浏览器会呈现一个“保存为”的对话框,将 Filename 的值预填为下载后的文件名,假如它存在的话

value:inline 浏览器会在浏览器内打开文件,如果无法打开则会附件下载这个文件

//片段三
//设置文件的类型和编码 免得文件中文乱码
response.setContentType("text/plain;UTF-8");
//设置响应头控制浏览器的行为
response.setHeader("Content-disposition","attachment;"+Filename);

通过response对象获取OutputStream流

ServletOutputStream  outputStream = response.getOutputStream();

设置缓冲区

将FileInputStream流写入到buffer缓冲区

使用OutputStream将缓冲区的数据输出到客户端浏览器

read( byte [ ] ):作用是返回读入缓冲区的字节总数,当返回为-1表示读取到文件末尾

outputStream.write:利用字节流作为底层输出流然后构建字符输出流,字符输出流输出字符到流中

例如:

文件大小为1028

read一次之后is流还剩4,4不等于-1,所以继续

len为1024

write(arry,0,len)os流读取字节数组之后又把数组设为0了

第二次读取

os流把剩下的4读完了,所有数据到outputStream准备就绪

第三次程序结束

//片段四
int len;
byte[] arry=new byte[1024];
while (true){
    len=fileInputStream.read(arry);
    if (len==-1)break;
    //将缓冲区数据输出到浏览器
    outputStream.write(arry,0,len);
}

关闭流

流是重量资源,关闭它

//片段5
fileInputStream.close();
outputStream.close();

完整代码

@Controller
@RequestMapping(value = "download")
public class MyDwonLoadController {

    @RequestMapping(value = "Resourse")
    public String DownLoadResourse(String Filename, HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println("Filename = " + Filename);
        String realpath=request.getSession().getServletContext().getRealPath("/DownLoadReourse");
        System.out.println("realpath = " + realpath);
        FileInputStream fileInputStream=new FileInputStream((new File(realpath,Filename)));
        response.setContentType("text/plain");
        response.setCharacterEncoding("UTF-8");
        //设置响应头控制浏览器的行为
        response.setHeader("Content-disposition","attachment;"+Filename);
            ServletOutputStream  outputStream = response.getOutputStream();
            int len;
            byte[] arry=new byte[1024];
            while (true){
                len=fileInputStream.read(arry);
                if (len==-1)break;
                //将缓冲区数据输出到浏览器
                outputStream.write(arry,0,len);
            }
            fileInputStream.close();
            outputStream.close();
        return "DownLoadPage/Download";
    }
}

3.开发补充

1.下载选项

一般除了直接下载,通常我们还会设置在线打开或者下载的选项

所以我们在控制器里这样设置

同时记得在url传值的时候随意给OpenStyle设置一个值即可

public String DownLoadResourse(String Filename,String OpenStyle){
    OpenStyle=OpenStyle==null?"inline":"attachment";
     response.setHeader("Content-disposition","OpenStyle;"+Filename);
}

就可以便捷的设置打开方式

2.IOUtils

操作Io使用IOUtils.copy(is,os)就可以代替上面的片段四

同时操作文件还有FileUtils可以使用

3.传送中文文件名乱码

response.setHeader("Content-disposition","OpenStyle;"+URLencoder.encoder(filename,"UTF-8"));

当然了为了所有地方统一,可以专门设置一个变量处理传值来的中文乱码问题

SpringMVC的拦截器和全局异常处理

一,拦截器

作用:

类似于javaweb中的Filter,用来对请求进行拦截,可以将多个Controller中执行的共同代码放入拦截器中执行,减少Controller类中代码的冗余

当然了底层依然是aop,额外功能是Controller中的方法

特点

  • 拦截器器只能拦截Controller的请求,不能拦截jsp

  • 拦截器可中断用户的请求轨迹

  • 请求先经过拦截器,之后之后还会经过拦截器

1.执行过程

绘图1

2.开发拦截器

(坑)由于JDK8的新特性HandlerInterceptor 接口中的方法定义都是defult,所以IDEA不会提示重写方法,所以就需要我们自己重写它的方法

public class MyInterceptor implements HandlerInterceptor {
 /**
     * preHandle方法是进行处理器拦截用的,顾名思义,该方法将在Controller处理之前进行调用,
     * SpringMVC中的Interceptor拦截器是链式的,可以同时存在多个Interceptor,
     * 然后SpringMVC会根据声明的前后顺序一个接一个的执行,
     * 而且所有的Interceptor中的preHandle方法都会在Controller方法调用之前调用。
     * SpringMVC的这种Interceptor链式结构也是可以进行中断的,
     * 这种中断方式是令preHandle的返回值为false,当preHandle的返回值为false的时候整个请求就结束了。
     *Object handler为当前的控制器对应方法
     *HttpServletRequest request, HttpServletResponse response为当前的请求和响应
     */
	
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptor.preHandle");
        return true;
    }
    
	 /**
     * 这个方法只会在当前这个Interceptor的preHandle方法返回值为true的时候才会执行。
     * postHandle是进行处理器拦截用的,它的执行时间是在处理器进行处理之 后, 也就是在Controller的方法调用之后执行,
     * 但是它会在DispatcherServlet进行视图的渲染之前执行,也就是说在这个方法中你可以对ModelAndView进行操作。
     * 这个方法的链式结构跟正常访问的方向是相反的,也就是说先声明的Interceptor拦截器该方法反而会后调用,
     *  ModelAndView modelAndView 为当前返回的视图和数据模型 视图也就是是Controller中返回值的字符串之后被底层组装 数据模型也就是比如
     *	request.setAttribute设置的k-v
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor.postHandle");

    }
	 /**
     * 该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。
     * 该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行, 这个方法的主要作用是用于清理资源的
     * 也用来执行异常对象
     */

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptor.afterCompletion");
    }
}

3.开发配置文件

mvc:mapping path:指的是请求路径 也可以使用通配符

mvc:exclude-mapping path:指的是排除 不拦截的路径

在最后要指明拦截器

<bean id="Interceptor" class="com.Interceptor.MyInterceptor"/>
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/Test/*"/>
        <mvc:exclude-mapping path="/Test/H2"/>
        <ref bean="Interceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

注意事项

如果遇到拦截器无效一定要clean一下项目(idea在MAVEN设置)

image-20210815135218813

执行一下看看效果

http://localhost:8080/SpringMVC02_war_exploded/Test/H

image-20210815135337686

二,全局异常处理

当控制器中某个方法在运行过程中突然发生运行时异常时,为了增加用户体验对于用户不能出现500错误代码,应该给用户良好展示错误界面,全局异常处理就能更好解决这个问题

1.开发全局处理类

1.自定义异常信息开发

可以做一个业务的自定义异常信息UserNameNOTFINDexception

public class UserNameNOTFINDexception extends RuntimeException{

    public UserNameNOTFINDexception(String message) {
        super(message);
    }
}

在控制器中这样

@RequestMapping(value = "testException")
public void EX(){
    throw new UserNameNOTFINDexception("自定义控制器测试:出错了");
}

在提示和业务方面更加灵活

可以因为不同的异常信息跳转到不同的视图去

2.实现HandlerExceptionResolver接口

参数:

HttpServletRequest request, HttpServletResponse response:指的是当前请求

Object handler:指的是当前异常控制器方法

Exception ex: 指的是当前异常

方法:

ex.getMessage()获得的是异常信息

modelAndView.setViewName:指的是设置视图名字

  • ​ forword方式:底层依然调用视图处理器组装url

  • ​ redirect方式:在设置了 modelAndView.addObject(和request.setAttribute()差不多)设置了异常信息会自动的拼接在url后面

    ​ 例如:redirect:/index.jsp拼接之后就变为了/index.jsp?msg=xxx

@Component
public class GlobeException implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("resolveException错误信息:"+ex.getMessage());
        ModelAndView modelAndView=new ModelAndView();
        if(ex instanceof UserNameNOTFINDexception){
            modelAndView.setViewName("redirect:/index.jsp");
        }
       else {
            modelAndView.setViewName("redirect:/Erro.jsp");
        }
       modelAndView.addObject("msg",ex.getMessage());


        return modelAndView;
    }
}

2.开发视图

直接接受错误信息即可

<body>
<h3>出错了。。。</h3>
<br>${param.msg}
</body>
posted on 2021-08-15 16:53  NathenJames  阅读(452)  评论(0编辑  收藏  举报