SpringMVC入门总结
博客:
地址:https://www.cnblogs.com/ShanYu-Home/
目录表
SpringMVC基础知识
一,MVC设计模式
- 视图层(View):负责格式化数据并把它们呈现给用户,包括数据展示、用户交互、数据验证、界面设计等功能。
- 控制层(Controller):负责接收并转发请求,对请求进行处理后,指定视图并将响应结果发送给客户端。
- 数据模型层(Model):模型对象拥有最多的处理任务,是应用程序的主体部分,它负责数据逻辑(业务规则)的处理和实现数据操作(即在数据库中存取数据)。
MVC应该是耳熟能详的基本知识,从最开始学习JavaEE开始,最早接触的就是JSP+JavaBean的开发模式,JSP 用于处理用户请求,JavaBean 用于封装和处理数据。
学过JSP+JavaBean之后又接触Servlet+JSP+JavaBean的开发模式
Servlet+JSP+JavaBean 模式的结构清晰,是一个松耦合架构模式
我记得在这这本书,里面说Servlet消灭了大量的JSP中的代码,在实际学习过程中确实深有体会(但是仍然不能否认这是本十八流教材)。
二.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标准的
(坑)在此处可以最好更换为自己的仓库,以及加上archetypeCatalog=internal 否则构建项目极慢(因为下载不了某个配置文件)
创建好项目之后记得补全Maven标准目录
2.插件的准备
为了提高效率和心情愉悦,必须安排JRebel等热部署插件,大大缩短编码等待时间
由于插件收费,作为初学者实在负担不起
所以如下步骤激活
1.生成一个GUID
可以使用这个地址获取Generate GUIDs online (guidgen.com)
2.根据反向代理服务器地址拼接激活地址
服务器地址: https://jrebel.qekang.com/{GUID}
如果失效刷新GUID替换就可以
来自:博客园-工匠精神
3.Tomcat的配置
在这里直接选择Tomcat即可,进入后选择自己的项目部署,不多说了。
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
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
可以看见正常转发
接着取消第三行注释
发现转发失败
再接着加上类的访问路径
又可以看见正常转发
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} 没有指定哪个范围
那么它会以:
- Page
- Request
- Session
- 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="提交" />
<input type="submit" value="重置" />
<hr>
num1:${param.num1}<br />
num2:${paramValues.num[0]}<br />
num3:${paramValues.num[1]}<br />
</form>
参考:
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层的一些实现
以及Mybaits的配置
配置Spring SpringMVC也同样可以参考之前的笔记
这里只是打通这个流程方便快速查阅,非常简单只不过是对之前的知识进行联系和衔接而已,建议自己多搭建几次熟悉流程
1.Spring和Mybaits的整合
1.建立数据库表
ID | NAME | 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&allowPublicKeyRetrieval=true&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>
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.开发视图
提供一个url带上值Filename给控制器,让控制器知道确定的下载文件
<body>
<a href="${pageContext.request.contextPath}/download/Resourse?Filename=LoadTXT.txt">下载LoadTXT.txt</a>
</body>
</html>
2.开发控制器
核心:
- 获取文件名字,从url传值方式获得
- 获取存放文件的文件夹位置
- 由文件名字和位置读取文件输入流
- 设置响应相关参数
- 通过response对象获取OutputStream流
- 设置缓冲区
- 将FileInputStream流写入到buffer缓冲区
- 使用OutputStream将缓冲区的数据输出到客户端浏览器
- 关闭流
获取文件名字,从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.执行过程
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设置)
执行一下看看效果
http://localhost:8080/SpringMVC02_war_exploded/Test/H
二,全局异常处理
当控制器中某个方法在运行过程中突然发生运行时异常时,为了增加用户体验对于用户不能出现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>