SpringMVC扩展(一)

2024-08-06 13:15:14 浏览数 (2)

SpringMVC扩展

REST 软件架构:

Representational State Transfer,表述性状态转移,是一种软件架构风格 查看、修改、删除所对应的传统URL与REST风格的URL对比

/userview.html?id=12 VS /user/view/12 参数不再使用“?”传递 与传统不同就是 REST 不在通过?来传递参数,这种URL的可读性更好,项目架构清晰; 最关键是SpringMVC 它支持这种风格~ 弊端:对于中国的项目,URL中有时候存储中文, 中文乱码…(中文真让人头大呀~)

现在很多网站都是 REST 和 传统的URL 结合使用;

实例代码:

代码语言:javascript复制
	@RequestMapping(value="/xxx/{参数名1}/{参数名2}")				
	public String info(@PathVariable("参数名1") Integer id,@PathVariable("参数名2") String name){
				//@PathVariable注解: 将URL中的{xx} 占位符参数 绑定到对应的控制器方法参数中;
		//打印输出
		System.out.println("==============="   id   "============"   name);		
		return "/页面名";							//这里return "/页面名";  加了一个 /表示回到项目根目录,因为受上面的@RequestMapping(value="/xx/{}/{}")影响,当然相对路径不匹配~			
	}

假设有一条URL : localhost:8080/项目名/rest/1/wsm 重点是 /rest/1/wsm 控制器是代码是:

代码语言:javascript复制
	@RequestMapping(value="/rest/{id}/{name}")				
	public String info(@PathVariable("id") Integer id,@PathVariable("name") String name){
		//控制台打印
		System.out.println("==============="   id   "============"   name);		
		return "/index";		//随便就好~
	}

控制台输出:

需要注意的就是: return “/xx”; / 使其回归根目录~ 参数多个时候, xx/{}/{} 要对应,不然可能找不到控制器; 而且因为很多时候, 受REST影响返回页面的静态资源也可能存在路径异常: 通常建议使用绝对路径来解决此问题… 在路径前加 ${pageContext.request.contextPath }/ 表示绝对路径; 一般用于JSP页面上对的静态资源引用防止路径引用出错… 等价于<%=request.getContextPath()%> 小脚本引用内置对象调用方法来获取 绝对路径…

{pageContext.request.contextPath }/ 也就是取出部署的应用程序名或者是当前的项目名称 {pageContext.request.contextPath}或<%=request.getContextPath()%> 取出来的就是: /项目名 。 而"/"代表的含义就是 http://localhost:8080 比如我的项目名称是demo1在浏览器中输入为:http://localhost:8080/Demo/index.jsp。 取出来的就是: /Demo

使用Servlet API对象作为处理方法的入参

在SpringMVC中 控制器可以不依赖任何Servlet APl对象( 直接把类型作为,参数放在方法中 既可以使用) 可以将Servlet APl 对象作为处理方法的参数 进行使用; controller(控制器方法Demo)

代码语言:javascript复制
	@RequestMapping("/请求页面名")
	public String Demo(HttpSession session,HttpServletRequest request){
	
		session.setAttribute("session","session作用域");		//往session 作用域存储数据;
		request.setAttribute("request","request作用域");		//往request 作用域存储数据;
		
		return "响应页面";
	}

静态资源文件的引用:

SpringMVC 在引用外部的静态资源有时候存在失效情况… 因为: web.xml 中配置的 DispatcherServlet (分配器) 请求映射为 / 即: SpringMVC 将捕获的web 容器的所有请求; (包括静态的请求,而SpringMVC将它们当成了一个普通的请求, 但由于找不到对于的处理器,所有按照常规的方式引入: 静态文件"无法访问" …)

采用 <mvc:resources > 解决静态资源访问问题: 首先为了方便管理,一般将项目中的所有静态文件资源:(JS,HTML,CSS…) 放在一个目录下

JSP文件:

代码语言:javascript复制
<link rel="stylesheet" type="text/css" href="static/css/index.css">
<!-- 不详细介绍了就是一个 引用外部资源的代码... -->

Spring核心配置文件

代码语言:javascript复制
	<!-- 访问静态资源 css js html..  打开访问权限,SpringMVC 默认不允许静态资源的加载的;  -->
	<mvc:resources location="/static/" mapping="/static/**" />
<!--
	location 	将静态资源映射到指定的路径下; 文件请求经过 DispatcherServlet 判断文件的在 static 目录下进行特殊处理.. 
	mapping		本地静态资源文件的目录; ** 文件下的所有文件;
-->

解决项目中的中文乱码问题:

web.xml

代码语言:javascript复制
	 <!-- 解决项目中的控制器 中文参数乱码问题 -->
	<filter>   									<!-- 过滤器 -->
   	<filter-name>myencoding</filter-name> 		<!-- 过滤器name -->
   												<!-- 引入过滤器 类 -->
   	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
   	<init-param>
   		<param-name>encoding</param-name> 		<!-- 指定编码形式 -->
   		<param-value>UTF-8</param-value>  		<!-- UTF-8编码 -->
   	</init-param>
   </filter>
   
   <filter-mapping>     						<!-- 过滤器映射  -->
   	<filter-name>myencoding</filter-name>    	<!-- 根据过滤器名,映射指定的过滤器.. -->
   	<url-pattern>/*</url-pattern>  				<!-- /* 对所有文件(包括JSP)进行过滤配置,设置文件编码UTF-8 -->
   </filter-mapping>

异常处理:

局部异常处理 方式一:控制器类继承 HandlerExceptionResolver

代码语言:javascript复制
@Override  									//控制器类继承了 HandlerExceptionResolver 重写resolveException(..); 方法;
	public ModelAndView resolveException(HttpServletRequest arg0,HttpServletResponse arg1, Object arg2, Exception arg3) {
		//当发生异常时,SpringMVC 会调用resolveException(..); 方法并返回一个 ModelAndView 给浏览器;
		return new ModelAndView("err");  		//指定异常之后跳转的页面;
	}

方式二:使用注解 @ExceptionHandler

代码语言:javascript复制
	@RequestMapping(value="/index")						//控制器接收的 index 
	public ModelAndView index(){
		ModelAndView mas = new ModelAndView();   		//注意导包错误!
		mas.setViewName("index");						//要响应的 视图页面;
		System.out.println(1/0);						//手动抛出一个运行时异常...运行时异常
		return mas; 
	}	
	
	@ExceptionHandler(value={RuntimeException.class})  						//注解声明运行时异常,当类中有运行时异常会进入该方法处理;
	public String err(RuntimeException e,HttpServletRequest request){		//参数异常对象,可以通过request..返回给异常页面查看
		request.setAttribute("e",e);
		return "err";														//处理异常方法,返回异常页面 err ;
	}
代码语言:javascript复制
 对于这些异常参数,可以存储在 request/session/Model 中返回异常页面.通过   ${参数名.message }  来查看  异常信息;   

全局异常处理

上面的局部异常, 一次只有作用于一个 controller控制器有效, 局部异常 如果需要对所有异常进行匹配就需要全局异常处理了

全局异常可以使用:SimpleMappingExceptionResolver

代码语言:javascript复制
	<!-- 全局的异常配置信息 -->
	<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
		<property name="defaultErrorView" value="err" />
	</bean>

可以在props中定义多种类型异常

代码语言:javascript复制
<!-- 可以在props中定义多种类型异常 -->
	<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" >
		<property name="exceptionMappings">
			<props>
				<prop key="java.lang.RuntimeException">err</prop> 
				<prop key="异常">异常类名映射的视图名</prop> 
			</props>
		</property>
	</bean>

格式化注解@DateTimeFormat

有时候SpringMVC 新增时候报错, BindException,是在对bean的属性进行数据绑定时出了问题。 这是springmvc框架的问题,若不解决次问题 页面传递回来的时间类型的数据就无法在controller中接受(实体类Date 页面传过是 String ) 也就无法完成新增用户的功能。 通常都是时间类型绑定失败; 解决方法有很多:使用@DateTimeFormat 引入:joda-time-2.9.9.jar 包 在实体类上引入注解:

代码语言:javascript复制
public class 实体类 {	
	@DateTimeFormat(pattern = "yyyy-MM-dd")
	private Date birthday;
}

在上述代码中,@DateTimeFormat(pattern="yyyy-MM-dd")可以将形如1988-12-01的字符串转换为java.util.Date类型。 算是一种解决方法;

注意:这个只是在中文赋值时候转换成 Date 类型的对象,如果想要查看 还是以英文的时间格式, 不过~ 这个在Spring 的表单中可以搭配使用, 显示对应的格式~

总结:

  • @DateTimeFormat 声明在实体属性上,前端发送的 字符串类型日期格式, 可以直接和对象进行绑定匹配!
  • 只是解决前端String——后端Date绑定,后端Date——前端展示还需另外处理!

Spring表单

现在前后端已经区分很明确了,JSP已经很少使用了,Spring表单也很少使用了! 我们在进行SpringMVC 项目开发时,一般会使用 EL表达式 和 JSTL标签 HTML表单… 来完成页面视图开发. Spring也有自己的一套标签库,通过Spring表单标签; 可以更容易的将模型数据表单, 命令对象绑定到 HTML 表单元素中; 首先和JSTL标签库一样,在使用Spring表单之前。必须在JSP页面下添加:引入标签库 <%@ taglib prefix="fm" uri="http://www.springframework.org/tags/form" %> 之后就可以使用Spring的表单标签库了…

实例代码有点不好解释, 就直接上项目Demo了

做一个模拟用户查看功能: SpringMVC 的配置就不锁了,web.xml ..applicationContext-mvc.xml

首先实体类user

代码语言:javascript复制
public class User {
	private Integer id;
	
	private String name;
	
	private String pwd;
	
	@DateTimeFormat(pattern="yyyy-MM-dd")		//使用了注解时间参数赋值问题,注意引用jar哦~
	private Date birthday;
	
	private Integer sex;

	//get/set 方法...;
}

控制器UserController.java

代码语言:javascript复制
package com.wsm.controller;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.wsm.entity.User;

@Controller
public class UserController {

	//因为这里我们没有数据库就模拟一些数据出来~
	//静态用户集合,类加载时候创建;
	private static  List<User> users  = new ArrayList<User>();
	//静态代码块,初始化一下用户出来;
	static{
		User u1 = new User();
		u1.setId(1);
		u1.setName("wsm");
		u1.setBirthday(new Date());
		u1.setPwd("123");
		u1.setSex(1);
		
		User u2 = new User();
		u1.setId(2);
		u2.setName("wsx");
		u2.setBirthday(new Date());
		u2.setPwd("321");
		u2.setSex(0);
		
		//把 u1 u2 放在users 集合中
		users.add(u1);
		users.add(u1);
	}
	
	//展示数据 index页面
	@RequestMapping("/index")
	public String index(Model model){
		
		model.addAttribute("user", users);
		
		return "index";
	}
	
	//去修改页面 
	@RequestMapping("/toupd")
	public String toupd(Integer id,Model model){
		
		User u = users.get(id-1);		//下标获取, 要注意数据下标 0 开始; 所以减一
		model.addAttribute("user",u);
		return "upd";
	}
	
}

index.jsp

代码语言:javascript复制
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="c"  uri="http://java.sun.com/jsp/jstl/core" %>
<%
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>My JSP 'index.jsp' starting 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>
   	<c:if test="${user==null }">
   		<script type="text/javascript">
   			location.href="index";
   		</script>
   	</c:if>
  <a href="toadd" >新增</a>     	
  	<table border="1" width="100%">
  		<tr>
  			<td>编号</td>
  			<td>用户名</td>
  			<td>密码</td>
  			<td>性别</td>
  			<td>生日</td>
  			<td>操作</td>
  		</tr>
  		<c:forEach items="${user }" var="u" >
  			<tr>
  				<td>${u.id }</td>
  				<td>${u.name }</td>
  				<td>${u.pwd }</td>
  				<td>${u.sex }</td>
  				<td>${u.birthday }</td>  <!-- 这时页面上的时间还是英文格式, 哎看着就不舒服-->
  				<td>  <a href="toupd?id=${u.id }" >修改</a>  </td>
  			</tr>
  		 </c:forEach>
  	</table>
   	
  </body>
</html>

点击修改擦看详情信息~ 这里不实现修改了… upd.jsp

代码语言:javascript复制
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="fm" uri="http://www.springframework.org/tags/form" %> <!-- 引入Spring表单库 -->
<%
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>My JSP 'upd.jsp' starting 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>
  
  	<!-- 相当于HTML 的 form: 
  		action/method不介绍了:	action,如果不指定提交默认为提交到获取表单页面的URL	 (即上一个地址!)
  		
  		modelAttribute:	用于表示绑定的 模型属性; 就是 Model 中存储的要修改对象; (不存在会报错)
  			如果不指定属性名,默认取名是 command (一般都手动取名)
  			而且可以同时对应 新增和修改;
  			新增在去新增的控制器model 这存储一个空的对象即可, 提交时候会把表单的数据存在这个属性中: 发送至控制器;
  			也就是说, 需要由控制器Modle给  modelAttribute 提供一个属性,
  			modelAttribute 提交时也把表单的数据 返回值控制器;...
  	 -->
   	<fm:form action="upd" method="post" modelAttribute="user" >
   		<table>
   			<tr>
   				<td>名称</td>
   				<td>
   					<fm:input path="name"/>  
   					  <!--  path:属性路径,表示表单的对象属性;  
   					  	 	如果model中存在 modelAttribute="对象属性" 且 path="又对应对象的属性" 则展示表单;
   					  	 	model中不存在     modelAttribute="属性名"		自动以属性名为名,提交时候把表单的值都放进入,发送至控制器;
   					  -->
   				</td>
   			</tr>
   			<tr>
   				<td>密码</td>
   				<td>
   					<fm:input path="pwd"/>
   				</td>
   			</tr>
   			<tr>
   				<td>性别</td>
   				<td>
   					<fm:radiobutton path="sex" value="1"/>男
  					<fm:radiobutton path="sex" value="0"/>女
   				</td>
   			</tr>
   			<tr>
   				<td>生日</td>
   				<td>
   					<fm:input path="birthday"/>			<!-- 注意看这里,Spring表单对 Date 不在是英文格式时间,而是跟随@DateTimeFormat一样了 -->
   				</td>
   			</tr>
   		</table>
   		<!-- 
   			<input type="submit" value="提交"> 
   			提交,就不提交了,不做修改了. 主要是想看看; SpringMVC的表单一些功能...;
   		--> 
   	</fm:form>
   	
  </body>
</html>

ok ,就是这样了~ 部署运行即可~ Spring常用表单标签

名称

说明

< fm:form />

输入框组件标签

< fm:password />

密码框组件标签

< fm:hidden />

隐藏框组件标签

< fm:textarea />

多行输入框组件标签

< fm:radiobutton />

单选框组件标签

< fm:checkbox />

复选框组件标签

< fm:select />

下拉列表组件标签

< fm:error />

显示表单数据校验所对应的错误信息 (通常可以搭配 JSR303约束使用)

标签属性

属性

描述

path

属性路径,表示表单对象属性。

cssClass

表单组件对应的CSS样式类名

cssErrorClass

当提交表单后报错(服务端错误),采用的CSS样式类

cssStyle

表单组件对应的CSS样式

htmlEscape

绑定的表单属性值是否要对HTML特殊字符进行转换,默认为true

注意: 表单组件标签也拥有HTML标签的各种属性,比如:id、onclick等等,都可以根据需要,灵活使用;


数据校验:JSR303

目前为止对于数据的验证;一般都是在前端 进行JS 表单验证; 而 一直没有加入 "服务器端的数据验证"

SpringMVC中有两种方式可以进行 数据验证:

  • 利用Spring自带的验证框架 fm
  • 利用JSR 303 实现; (一般都搭配 Spring框架一起使用) , 也可以和表单框架搭配使用!

JSR 303:Java为Bean数据合法性校验所提供的标准框架

  • Spring MVC支持JSR 303标准的校验框架
  • JSR 303通过在Bean属性上标注校验注解指定校验规则,并通过标准的验证接口对Bean进行验证; 可以通过 http://jcp.org/en/jsr/detail?id=303 下载 JSR303 Bean Validation

注意: Spring本身没有提供JSR303的实现 实现者:Hibernate Validator , 所以必须加入 Hibernate Validator 库的 jar; hibernate-validator-4.3.2.Final.jar jboss-logging-3.1.0.CR2.jar validation-api-1.0.0.GA.jar 导入这三个Jar Srping就会自动加载…

JSR 303 约束

约束

说明

@Null

被注释的元素必须为null

@NotNull

被注释的元素必须不为null

@AssertTrue

被注释的元素必须为 true

@AssertFalse

被注释的元素必须为 false

@Min(value)

被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@Max(value)

被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@DecimalMin(value)

被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@DecimalMax(value)

被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@Size(max, min)

被注释的元素的大小必须在指定的范围内

@Digits (integer, fraction)

被注释的元素必须是一个数字,其值必须在可接受的范围内

@Past

被注释的元素必须是一个过去的日期

@Future

被注释的元素必须是一个将来的日期

继续刚才的Demo 加一个新增 表单验证; 修改实体类; User.java

代码语言:javascript复制
package com.wsm.entity;
import java.util.Date;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;

public class User {
	private Integer id;
	
	@NotEmpty(message = "不能为空!") 		
	@NotNull(message = "不能为Null!")
	private String name;
	
	@Length(min = 6, max = 20,message="密码的长度必须6-20之间")
	private String pwd;
	
	@DateTimeFormat(pattern="yyyy-MM-dd")		//使用了注解时间参数赋值问题,注意引用jar哦~
	@Past(message="年龄必须是过去时")
	private Date birthday;
	
	private Integer sex;

	//get/set 方法...;
}

控制器:加两个方法:

代码语言:javascript复制
	//新增控制器
	//去新增
	@RequestMapping("/toadd")
	public String toadd(Model model){
		User u = new User();
		model.addAttribute("userForm",u);		//因为表单中需要一个 modelAttribute="userForm" 所以存一个空的给它咯~
		return "add";
	}
	
	//新增
	@RequestMapping("/add")
	public String add(@ModelAttribute("userForm") @Valid User user,BindingResult bindingResult){ 	
					//两个参数注解  @ModelAttribute() @Valid
					// @ModelAttribute(): 将表单modelAttribute="userForm" 赋值给 user;
					// @Valid			  	<mvc:annotation-driven /> 默认装配一个 LocalValidatorFactoryBean,通过注解使SpringMVC 在数据绑定使进行 校验;
					// @Valid注解标示的参数后面,必须紧挨着一个BindingResult参数,否则Spring会在校验不通过时直接抛出异常

		//参数 bindingResult 是必须的,或是Error类型. 用来判断是否发送异常~ : 表单输入与实体类注解不符合;
		if(bindingResult.hasErrors()){ 
			return "add";	//有误跳转回新增页面; 由 <fm:errors path="xx" /> 展示对应的异常信息;
		}
		//新增就是往集合中在放一个;
		users.add(user);
		return "index";		//新增成功index 展示;
	}

add.jsp

代码语言:javascript复制
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="fm" uri="http://www.springframework.org/tags/form" %> <!-- 引入Spring表单 -->
<%
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>My JSP 'add.jsp' starting 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>
   
   
    <fm:form action="add" method="post" modelAttribute="userForm">
  	<table>
  			
  			<tr>
  				<td>编号</td>
  				<td>
  					<fm:input path="id"/>
  					<fm:errors path="id"></fm:errors>
  				</td>
  			</tr>
  			<tr>
  				<td>名称</td>
  				<td>
  					<fm:input path="name"/>
  					<fm:errors path="name"></fm:errors>
  				</td>
  			</tr>
  			<tr>
  				<td>密码</td>
  				<td>
  				<fm:input path="pwd"/>
  				<fm:errors path="pwd"></fm:errors>
  				</td>
  			</tr>
  			<tr>
  				<td>生日</td>
  				<td>
  				<fm:input path="birthday"/>
  				<fm:errors path="birthday"></fm:errors>
  				
  				</td>
  			</tr>
  			<tr>
  				<td>性别</td>
  				<td>
  					<fm:radiobutton path="sex" value="1"/>男
  					<fm:radiobutton path="sex" value="0"/>女
  				</td>
  			</tr>
  		</table>
  		<input type="submit" value="提交">
  </fm:form>
  </body>
</html>

0 人点赞