springmvc框架基础知识详解

SpringMvc

1、理解MVC设计模式

视图View——jsp和html,显示页面(el和jstl)
控制器controller——servlet
模型model——JavaBean
在这里插入图片描述

1.1、jsp model1

在这里插入图片描述

1.1、jsp model2(把servlet请求响应的控制分离出来)

JSP:负责生成动态网页
Servlet:负责流程控制
JavaBean:负责业务逻辑处理
在这里插入图片描述

1.3、MVC的处理过程

重点在于控制器
在这里插入图片描述

1.4、优缺点

1.4.1、优点

多视图共享一个模型,大大提高代码的可重用性
MVC三个模块相互独立,松耦合架构
控制器提高了应用程序的灵活性和可配置性
有利于软件工程化管理
完美的系统架构 = 松耦合+高重用性+高扩展性

1.4.2、 缺点

原理复杂
增加了系统结构和实现的复杂性
视图对模型数据的低效率访问

2、了解spring MVC的架构以及请求流程

2.1、spring MVC请求处理流程

1、用户发送请求给前端控制器DispatcherServlet,这个控制器决定该去找哪个具体控制器,根据URL决定调用哪个方法
2、页面控制器/处理器调用业务对象返回Model AND View给前端控制器,前端控制器根据Model AND View决定找哪个视图
3、然后把model的数据显示在视图中

前端控制器出来所有的请求
在这里插入图片描述

2.2、spring MVC的架构体系

结构最清晰的MVC Model2实现
Controller、ModelAndView
在这里插入图片描述

结构体系

在这里插入图片描述
DispatcherServlet(前端控制器)

  • Spring MVC最核心的类
  • web.xml中配置

Handler(处理器):对应MVC中C(Controller层)

  • 类型:Object
  • 作用:实际处理请求
  • 标注了@RequestMapping的所有方法都可以看作是一个Handler

ModelAndView:逻辑视图名、模型对象

核心组件

HandlerMapping(处理器映射)

  • BeanNameUrlHandlerMapping(默认)将请求URL映射到同名的控制器Bean上
  • DefaultAnnotationHandlerMapping将请求映射到标注@RequestMapping注解的控制器和处理方法上
    RequestMappingHandlerMapping

HandlerAdapter(适配器)

  • AnnotationMethodHandlerAdapter
  • RequestMappingHandlerAdapter

ViewResolver(视图解析器)

  • InternalResourceView

框架特点

  • 清晰地角色划分
  • 灵活的配置功能
  • 提供了大量的控制器接口和实现类
  • 真正做到与View层的实现无关(JSP、Velocity、Xslt等)
  • 国际化支持
  • 面向接口编程
  • Spring提供了Web应用开发的一整套流程,不仅仅是MVC,他们之间可以很方便的结合一起

3、springmvc框架开发环境搭建步骤

3.1、使用BeanNameUrlHandlerMapping方式

通过BeanNameUrlHandlerMapping的方式完成请求与Controller之间的映射关系
jar包
1、加入jar包
spring-web-3.2.13.RELEASE.jar
spring-webmvc-3.2.13.RELEASE.jar
在这里插入图片描述
配置文件
2、web.xml配置文件,接管了所有的web的请求
Tomcat的启动要配置文件
因为现在没有上下文对象了

static void start() {
		userService=(UserService) (new ClassPathXmlApplicationContext("applicationContext.xml").getBean("userService"));
	}

所以要配置一个监听器去启动,监听表示会监听启动、关闭、会话
DispatcherServlet前端控制器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
	<!-- 配置SpringMVC前端控制器 -->
	<servlet>
	<!-- name可以随便起,建议springmvc -->
	  	<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-servlet.xml</param-value>
	  	</init-param>
	  	
	  	<!-- Tomcat启动时启动,1是启动true,0不启动false -->
	  	<load-on-startup>1</load-on-startup>
	  	
  	</servlet>
    <servlet-mapping>
	  	<servlet-name>springmvc</servlet-name>
	  	<!-- 根目录所有的请求都提交给springmvc -->
	  	<url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

3、springmvc-servlet.xml配置文件,放入资源文件夹中

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
        
	  <bean class="cn.bdqn.smbms.web.cotroller.IndexControllerDemo" name="/index"></bean>
 
    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    	<!-- 路径的前缀 -->
    	<property name="prefix" value="/WEB-INF/jsp/" />
    	<!-- 路径的后缀 -->
    	<property name="suffix" value=".jsp"/>
    </bean>
    

控制器
4、创建indexController类继承AbstractController

package cn.bdqn.smbms.web.cotroller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

public class IndexControllerDemo extends AbstractController {

	//通过这个方法响应客户端请求,返回视图和模型,模型里面是数据
	@Override
	protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
	
		ModelAndView mv=new ModelAndView();
		//前缀和后缀在配置文件配置好了
		mv.setViewName("index");
		return mv;
	}
}

视图
5、新建一个index.jsp文件

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<p>xxxxxx</p>
</body>
</html>

部署运行
6、添加项目到Servers,启动服务器,在浏览器运行
在这里插入图片描述

3.2、注解驱动控制器

但是有多个请求时,要配置多个映射关系,并建立多个Controller来进行请求处理,步骤繁琐

此时可以用注解去生成bean

@Controller:标注一个普通的JavaBean成为可以处理请求的控制器
@RequestMapping:通过请求URL进行映射

3、springmvc-servlet.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
        
		<!-- 注解的驱动 ,启动注解-->
        <mvc:annotation-driven />
      <!-- 扫描包中的注解 -->
        <context:component-scan base-package="cn.bdqn.smbms.web.cotroller" />
        
        <!-- 视图解析器 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        	<property name="prefix" value="/WEB-INF/jsp/"></property>
        	<property name="suffix" value=".jsp"></property>
        </bean>
  </beans>

4、创建一个indexController类,但是需要添加上注解

package cn.bdqn.smbms.web.cotroller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import cn.bdqn.smbms.pojo.User;

@Controller
public class indexController {
	
	//value是URL,表示这两个值都可以在浏览器访问
	@RequestMapping(value= {"/index","/"},method=RequestMethod.GET)
	public String Index(Model model) {
		
		User user=new User();
		user.setUserName("mvc");
		model.addAttribute(user);
		model.addAttribute("message", "我的MVC");
		return "index";
	}
}

5、

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>${message }</h1>
<p>用户名:${user.userName}</p>
</body>
</html>

在这里插入图片描述
这种方式可以灵活的添加方法

在indexController类添加方法

	@RequestMapping(value= {"/xxx","/"},method=RequestMethod.GET)
	public String xxx(Model model) {
		return "xxx";
	}

新建xxx.jsp页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>我的xxxxxxx</h1>
</body>
</html>

在这里插入图片描述

4、掌握Controller和View之间的映射

5、掌握参数的传递(View-Controller)

5.1、从控制器传到视图

  • ModelAndView
ModelAndView model=new ModelAndView();
model.addAttribute("message", "我的MVC");

页面用el表达式接受参数

<h1>${message }</h1>
  • 在方法里传递参数Model
	@RequestMapping( {"/index","/"})
	public String Index(Model model) {
		model.addAttribute("message", "我的MVC");
		return "index";
	}

5.2、从视图传参到控制器(加参数)

在方法里添加同名参数,数据名一样即可绑定数据

@Controller
public class indexController {
	
	@RequestMapping({"/xxx"})
	public String login(String username,String password) {
		if("admin".equals(username) && "123".equals(password)) {
			return "welcome";
		}
		//重定向到/的URL
		return "redirect:/";

	}

action提交到URL
新建index.jsp

<body>
<form action="xxx" method="post">
	用户名:<input name="username">
	密码:<input name="password">
	<input type="submit" value="提交">
</form>
</body>
</html>

新建welcome.jsp页面

<body>
<h1>欢迎光临</h1>
</body>

在这里插入图片描述

使用实体类对象传递数据

	@RequestMapping( {"/xxx"})
	public String login(User user) {
		if("admin".equals(user.getUserName()) && "123".equals(user.getUserPassword())) {
			return "welcome"; 
		}
		//重定向到/的URL
		return "redirect:/";
	
	}

这里的userName和userPassword要和实体类参数一样

<body>
<form action="xxx" method="post">
	用户名:<input name="userName">
	密码:<input type="password" name="userPassword">
	<input type="submit" value="登陆">
</form>
</body>

在这里插入图片描述

在会话保存数据

在方法的参数里加入HttpSession

	@RequestMapping( {"/xxx"})
	public String login(User user,HttpSession session) {
		if("admin".equals(user.getUserName()) && "123".equals(user.getUserPassword())) {
			//把用户对象添加到session里
			session.setAttribute("LoginUser", user);
			return "welcome"; 
		}
		//重定向到/的URL
		return "redirect:/";
	}

在欢迎页面

<body>
<h1>欢迎${ sessionScope.LoginUser.userName }登陆</h1>
</body>

在这里插入图片描述

映射规则

方法名可以一样,参数不一样(重载)
但是映射不能一样,要保证bean全局唯一@RequestMapping(value= {"/xxx"})
如果想要区别,不同方法请求获得的方法也不一样

@RequestMapping(value= {"/xxx"},method=RequestMethod.GET)
	@RequestMapping(value= {"/xxx"},method=RequestMethod.POST)

可以不写表单的action="xxx",默认请求post方式的URL

<form  method="post">
	用户名:<input name="userName">
	密码:<input type="password" name="userPassword">
	<input type="submit" value="登陆">
</form>

如果在方法上加上参数,但是没有传递过去,会报错,可以用?=-1传递过去

也可以加上注释,表示这个参数必须要public String Index(Model model,@RequestParam int id) {}

如果参数是可选的,可加public String Index(Model model,@RequestParam(name="id" ,required=false) Integer id) {},不传,id默认参数为null

Controller方法中参数前加@RequestParam进行直接入参

在这里插入图片描述

6、掌握并理解单例模式

BaseDao:操作数据库的基类
init()方法
每个线程对系统操作都需new一个BaseDao实例
I/O操作消耗系统资源,影响系统性能

单例模式:系统运行期间,有且仅有一个实例
一个类只有一个实例——最基本的
只提供私有构造器
它必须自行创建这个实例
定义了静态的该类私有对象
它必须自行向整个系统提供这个实例
提供一个静态的公有方法,返回创建或者获取本身的静态私有对象

6.1、懒汉模式-延迟加载

在类加载时不创建实例,采用延迟加载的方式,在运行调用时创建实例

package MySingleton;
public class MySingleton {
	
	//1将构造私有化,不允许外部实例化对象
	private  MySingleton() {}
	//2在类中定义一个本类型的私有静态属性
	private static MySingleton instance;
	//3定义公有的静态方法,返回该类的对象
	public static MySingleton getInstance() {
		
		//4判断是否已经实例化,这样可以只实例化一次
		if(null==instance) {
			instance=new MySingleton();
		}
		return instance;
	}
}

用类名调用方法

MySingleton ms=MySingleton.getInstance();

线程安全问题

多个线程同一个时间调用方法,就会出现线程安全问题
特点:在类加载时不创建实例,采用延迟加载的方式,在运行调用时创建实例,延迟加载(lazy Loading)
解决:同步(synchronized)(不建议用)

6.2、饿汉模式-先实例化

package MySingleton;
public class MySingleton {
	
	//将构造私有化,不允许外部实例化对象
	private  MySingleton() {}
	//在类中定义一个本类型的私有静态属性,并且实例化
	private static MySingleton instance=new MySingleton();
	
	//定义公有的静态方法,返回该类的对象
	public static MySingleton getInstance() {
		return instance;
	}
}

问题

在类加载的时候,就完成初始化
特点:线程安全,不具备延迟加载特性

6.3、结合懒汉和饿汉-静态内部类

避免了线程安全问题同时要具备延迟加载的特性
只有当加载静态内部类时才会实例化对象,实例化是在类加载的时候才会实例化

package MySingleton;
public class MySingleton {
//静态内部类
	private static class InnerSingleton{
		static MySingleton instance=new MySingleton();
	}
	
	//将构造私有化,不允许外部实例化对象
	private  MySingleton() {}
	
	//定义公有的静态方法,返回该类的对象
	public static MySingleton getInstance() {
		return InnerSingleton.instance;
	}
}

7、Spring MVC框架1

搭建Spring MVC+Spring+JDBC框架,并掌握在此框架上进行项目开发
指示符—“redirect:”重定向
指示符—“forward:”转发

7.1、掌握SpringMVC访问静态文件

静态资源放到单独的statics文件夹内
在这里插入图片描述

在apringmvc-servlet.xml配置文件配置静态资源

<!-- 配置静态资源,这样放过一些资源的访问,可以直接访问/statics/根目录下的资源 -->
<!--  具体的文件夹的位置location="/statics/" ,mapping="/statics/**"浏览器访问地址 -->
        <mvc:resources  location="/statics/"  mapping="/statics/**" />

访问statics文件夹里的images文件夹里的login_img.png图片

http://localhost:8080/smbms/statics/images/login_img.png

在这里插入图片描述

mapping是可以改的

 <mvc:resources  location="/statics/"  mapping="/xxx/**" />

用这个地址访问

http://localhost:8080/smbms/xxx/images/login_img.png

Spring MVC-Controller的单例管理

Spring MVC—Controller:默认scope为singleton、速度和性能优越
Controller是单例的,其内部的成员变量都是公用的,开发时需要注意资源使用的问题
一般情况下,Controller内的成员变量就只有service对象

单例<bean id="zg" class="springdemo.Person" scope="singleton"/>
多例<bean id="zg" class="springdemo.Person" scope="prototype"/>

注解
在控制器类加上注解

@Scope(value="prototype")

7.2、掌握ServletAPI作为入参

Servlet API对象作为处理方法的入参
HttpSession
HttpServletRequest
页面上使用EL表达式显示相应信息

7.3、掌握Spring MVC异常(局部、全局)处理

局部异常HandlerExceptionResolver

仅能处理指定Controller中的异常
@ExceptionHandler
任何异常都会转动error页面

	@ExceptionHandler
	public String handlerException(RuntimeException exception,Model model) {
		model.addAttribute("error", exception);
		return "error";
	}

error是键

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>出错了!${error.message }</h1>
<a href="${pageContext.request.contextPath }">返回</a>
</body>
</html>

全局异常异常处理

对所有异常进行统一处理
配置SimpleMappingExceptionResolver
发生异常时使用对应的视图报告异常

     <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        	<property name="exceptionMappings"/>
        	<props>
        		<prop key="java.lang.Exception">error</prop>	
        	</props>
        </bean>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>出错了!${exception .message }</h1>
<a href="${pageContext.request.contextPath }">返回</a>
</body>
</html>

默认${exception .message }的exception是键

8、Spring MVC框架2

8.1、 格式化注解(@DateTimeFormat)

Spring MVC框架中时间类型的数据无法自动绑定,导致user对象的birthday属性(java.util.Date类型)绑定失败
结果:输入新增用户信息,点击保存之后系统报错
400状态码(请求错误)、控制台(BindException异常)

@DateTimeFormat(pattern = "yyyy-mm-dd")
private Date birthday;

转换日期显示格式
在这里插入图片描述

 <p><strong>出生日期:</strong><span><fmt:formatDate value="${user.birthday }" pattern="yyyy-MM-dd"/></span></p>

在这里插入图片描述

8.2、REST风格(Representational State Transfer)

表述性状态转移,是一种软件架构风格

传统URLREST风格的URL
/userview.html?id=12/user/view/12

请求方式:GET、POST、DELETE、PUT

/view/{id}URL中的{xxx}占位符参数
@PathVariable String id接收REST风格URL中的参数

	@RequestMapping(value="/admin/user/view/{id}")
	public String view(@PathVariable Integer id,Mode l model) {
		model.addAttribute("user", userService.get(id));
		return "/user/userview";
	}

修改userlist.jsp页面href="${pageContext.request.contextPath }/admin/user/view/${user.id}"

<span><a class="viewUser" href="${pageContext.request.contextPath }/admin/user/view/${user.id}" ><img src="${pageContext.request.contextPath }/statics/images/read.png" alt="查看" title="查看"/></a></span>

在这里插入图片描述

@PathVariable(“id”) Integer id@PathVariable Integer id
变量名不一样,需要写出来不写参数,默认和变量名一样
	@RequestMapping(value="/admin/user/view/{xxx}")
	public String view(@PathVariable("xxx") Integer id,Model model) {
		model.addAttribute("user", userService.get(id));
		return "/user/userview";
	}

8.3、掌握Spring表单标签

Spring提供的轻量级标签库,可在JSP页面中渲染HTML元素的标签

1、引入标签声明之后就可使用Spring表单标签

<%@ taglib uri="http://www.springframework.org/tags/form" prefix="fm" %>

2、标签
fm:form标签

  • modelAttribute指定绑定的模型属性,默认为command(建议指定)
  • action指定表单提交的目标URL,可不指定,则自动提交到获取表单页面的URL
  • method:GET、POST

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

fm:input/属性
path属性路径,表示表单对象属性,如userName、userCode等
cssClass表单组件对应的CSS样式类名
cssErrorClass当提交表单后报错(服务端错误),采用的CSS样式类
cssStyle表单组件对应的CSS样式
htmlEscape绑定的表单属性值是否要对HTML特殊字符进行转换,默认为true
常用标签作用
fm:form/渲染表单元素
fm:input/输入框组件标签
fm:password/密码框组件标签
fm:hidden/隐藏框组件标签
fm:textarea/多行输入框组件标签
fm:radiobutton/单选框组件标签
fm:checkbox/复选框组件标签
fm:select/下拉列表组件标签
fm:error/显示表单数据校验所对应的错误信息

8.4、掌握数据验证框架-JSR 303

  • 服务器端的数据校验
    • 利用Spring自带的验证框架
    • 利用JSR 303实现
    • Spring本身没有提供JSR 303的实现(利用Hibernate Validator实现)
  • JSR 303
    • Java为Bean数据合法性校验所提供的标准框架
    • Spring MVC支持JSR 303标准的校验框架
    • JSR 303通过在Bean属性上标注校验注解指定校验规则,并通过标准的验证接口对Bean进行验证
约束说明
@Null被注释的元素必须为null
@NotNull被注释的元素必须不为null
@AssertTrue被注释的元素必须为 true
@AssertFalse被注释的元素必须为 false
@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min)被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past被注释的元素必须是一个过去的日期
@Future被注释的元素必须是一个将来的日期

步骤
1、添加jar包
hibernate-validator-4.3.2.Final.jar
jboss-logging-3.1.0.CR2.jar
validation-api-1.0.0.GA.jar
2、修改User.java,给需要验证的属性增加相应的校验注解
实体类

private Integer id;
	@NotEmpty(message = "用户编码不能为空")
	private String userCode;
	@NotEmpty(message = "用户名称不能为空")
	private String userName;
	@Length(message = "密码长度不得小于6且不得大于20",min=6,max=20)
	private String userPassword;
	private Integer gender;
	@DateTimeFormat(pattern = "yyyy-mm-dd")
	@Past(message = "日期不能是过去的日期")
	private Date birthday;
	@Pattern(regexp = "^1[0-9]{10}$" , message = "手机号码要是1开头的11位数字")
	private String phone;

jsp页面

<div>
       <label for="userCode">用户编码:</label>
        <fm:input path="userCode" /> 
		<!-- 放置提示信息 -->
		<font color="red"><fm:errors path="userCode"/></font>
</div>

控制类,参数加上@Valid User user,BindingResult result这两个要紧接着

@RequestMapping(value="/admin/user/add",method=RequestMethod.POST)
	public String add(@Valid User user,BindingResult result,Model model) {
		
		model.addAttribute("roleList", roleService.listAll());
		
		//判断验证是否通过
		if(result.hasErrors()) {
			return "user/useradd";
		}
		
		if(userService.add(user)) {
			return "redirect:/admin/user/list";
		}

在这里插入图片描述

8.5、掌握文件上传

8.5.1、使用Spring MVC实现单文件上传

MultipartResolver接口

用于处理上传请求,将上传请求包装成可以直接获取文件的数据,方便操作

两个实现类
  1. StandardServletMultipartResolver使用了Servlet3.0标准的上传方式
  2. CommonsMultipartResolver使用了Apache的commons-fileupload来完成具体的上传操作
实现

1、导入jar文件
commons-io-2.4.jar
commons-fileupload-1.2.2.jar

2、配置MultipartResolver(springmvc-servlet.xml)
使用CommonsMultipartResolver配置一个MultipartResolver解析器

        <bean id="multipartResolver" class=" org.springframework.web.multipart.commons.CommonsMultipartResolver">
        	<property name="maxUploadSize" value="5000000" />
        	<property name="defaultEncoding" value="utf-8" />
        </bean>

3、编写文件上传表单页useradd.jsp
在method="POST"指定表单内容类型,支持文件上传<fm:form method="post" modelAttribute="user" enctype="multipart/form-data">
用来上传文件的file组件

<div>
       <label for="idPic">头像上传:</label>
        <!-- 不要和user实体类里属性一样,会映射到User里面去(参数) -->
       <input type="file" name="idPic" id="idPic"/>
</div>

4、MultipartFile对象
Spring MVC会将上传文件绑定到MultipartFile对象中,并通过该对象完成文件的上传操作

控制类添加参数MultipartFile idPic

@RequestMapping(value="/admin/user/add",method=RequestMethod.POST)
	public String add(@Valid User user,BindingResult result,Model model,HttpSession session,MultipartFile idPic) {

		//得到真实的路径
		String filename=session.getServletContext().getRealPath("/statics/upload/1.jpg");
		try {
			idPic.transferTo(new File(filename));
		} catch (IllegalStateException | IOException e) {
			e.printStackTrace();
		}
	}

添加成功后,到Tomcat的服务器的项目里的/statics/upload/位置查看图片是否保存成功,里面的文件名为1.jpg

也可以写固定磁盘路径,但是可能无权操作c盘,或者没有d盘

	//String filename=session.getServletContext().getRealPath("/statics/upload/1.jpg");
		try {
			idPic.transferTo(new File("c:\\xxx.jpg"));
		} catch (IllegalStateException | IOException e) {
			e.printStackTrace();
		}

如果用本身文件名容易重名,可以使用随机文件名并上传到数据库

对上传文件进行重命名

当前系统时间+随机数+“_Personal.jpg”,文件上传成功后,更新数据库字段以及存储路径

		//得到扩展名
		String ext=FilenameUtils.getExtension(idPic.getOriginalFilename());
		//后缀,.currentTimeMillis()当前毫秒数
		String randName=""+System.currentTimeMillis()+(new Random().nextInt(999999))+"_idPic";
		//完整的路径
		String filename=session.getServletContext().getRealPath("/statics/upload/"+randName+"."+ext);
		try {
			idPic.transferTo(new File(filename));
		} catch (IllegalStateException | IOException e) {
			e.printStackTrace();
		}

在这里插入图片描述
把文件名写到数据库里

@RequestMapping(value="/admin/user/add",method=RequestMethod.POST)
	public String add(@Valid User user,BindingResult result,Model model,HttpSession session,MultipartFile idPic) {
		
		model.addAttribute("roleList", roleService.listAll());
		
		//如果有错误,就返回控制类
		if(result.hasErrors()) {
			return "user/useradd";
		}
		
		//判断文件域是否为空
		if(!idPic.isEmpty()) {
		
			//得到扩展名
			String ext=FilenameUtils.getExtension(idPic.getOriginalFilename());
			
			//判断扩展名是否是jpg和png
			if("jpg".equals(ext) && "png".equals(ext)) {
				model.addAttribute("fileerror", "文件类型只能是jpg或者png");
				return "user/useradd";
			}
			//1024*1024等于1兆
			if(idPic.getSize()>1024*1024) {
				model.addAttribute("fileerror", "文件大小不能超过2Mm");
				return "user/useradd";
			}
			
			//后缀,.currentTimeMillis()当前毫秒数
			String randName=""+System.currentTimeMillis()+(new Random().nextInt(999999))+"_idPic";
			//完整的路径
			String filename=session.getServletContext().getRealPath("/statics/upload/"+randName+"."+ext);
			
			try {
				idPic.transferTo(new File(filename));
				//把文件名上传到数据库
				user.setIdPicPath(randName + "." + ext);
				
			} catch (IllegalStateException | IOException e) {
				e.printStackTrace();
				model.addAttribute("fileerror", e.getMessage());
				return "user/useradd";
			}
		
		}
		
		//有时候保存的创建者为空,因为会话失时效了就会无法添加进去,所以单独放一条
		user.setCreatedBy(((User)session.getAttribute(CommonData.LOGIN_USER)).getId());
		
		if(userService.add(user)) {
			return "redirect:/admin/user/list";
		}
		
		/*添加不成功,把user里的数据写到model里面,然后jsp页面需要写el表达式接收数据(可用表单标签)
		 * 可以在参数里添加@ModelAttribute User user,则不用写这行代码,放入get方法里即可
		 * 	model.addAttribute("user", user);
		 */
	
		return "user/useradd";
	
	}

在这里插入图片描述

9、

掌握JSON对象的处理

json对象的处理-使用Ajax异步判断实现同名验证

客户端响应的是一个ajax方法,直接把return的结果返回到客户端而不返回视图

步骤:
1、添加jar包
fastjson-1.2.13.jar

2、改造Controller层—UserController.java

@ResponseBodyRestController
/@RestController=@ResponseBody + @Controller
则每一个方法要单独写, 将标注该注解的处理方法的返回结果直接写入HTTP Response Body中则Controller中的方法无法返回jsp页面,或者html,配置的视图解析器 InternalResourceViewResolver不起作用,返回的内容就是Return 里的内容
@ResponseBody           
	@RequestMapping(value="/admin/user/codeExists/{userCode}")
	public String CodeIsExit(@PathVariable String userCode) {
		return "" + userService.userCodeExists(userCode);
	}

userCodeIsExit()方法:同名验证
入参:userCode
验证结果:HashMap
返回值:Object(JSON对象),return JSONArray.toJSONString(HashMap);

3、改造View层
useradd.js

$(function(){
	$("#userCode").on("keyup", function(){
		$.ajax({
			url: $("#path").val() + "/admin/user/codeExists/" + $("#userCode").val(),
			success: function(result){
				if("true" == result){
					$("#userCode").next().text("该编号已被使用");
				} else {
					//不写else,就会一直符合if条件输出
					$("#userCode").next().text("可使用");
				}
			}
		})
	})
})

4、把js资源添加到useradd.jsp

<script type="text/javascript" src="${pageContext.request.contextPath }/statics/js/useradd.js"></script>

在这里插入图片描述
其他js示例

$(function(){
	$("#userCode").on("keyup", function(){
		$.ajax({
			url: $("#path").val() + "/admin/user/codeExists/" + $("#userCode").val(),
			success: function(result){
				if("true" == result){
					$("#userCode").next().text("该用户编号已经被使用");
				} else {
					$("#userCode").next().text("");
				}
			}
		})
	})
	
	$("form").on("submit", function(){
		var pass = true;
		if($("#userCode").val()==""){
			$("#userCode").next().text("用户编号不能为空");
			pass = false;
		} else {
			if($("#userCode").next().text() != "该用户编号已经被使用"){
				$("#userCode").next().text("");
			}
		}
		
		if($("#userName").val()==""){
			$("#userName").next().text("用户名称不能为空");
			pass = false;
		} else {
			$("#userName").next().text("");
		}
		
		if($("#userPassword").val().length<6 || $("#userPassword").val().length>20){
			$("#userPassword").next().text("用户密码不能为空,且必须是6-20个字符");
			pass = false;
		} else {
			$("#userPassword").next().text("");
		}
		
		if($("#ruserPassword").val() != $("#userPassword").val()){
			$("#ruserPassword").next().text("两次输入密码不一致");
			pass = false;
		} else {
			$("#ruserPassword").next().text("");
		}
		
		if($("#birthday").val()==""){
			$("#birthday").next().text("必须选择出生日期");
			pass = false;
		} else {
			$("#birthday").next().text("");
		}
		
		if($("#userCode").next().text()!=""){
			pass = false;
		}
		return pass;
	})
})

JSON数据的传递处理

通过Ajax异步调用来获取用户信息

控制器的处理方法返回bean对象转换成JSON对象输出

改造Controller层—UserController.java
返回值:Object(JSON对象)return JSON.toJSONString(user)

	@ResponseBody
	@RequestMapping(value="/admin/user/get/{id}")
	public String get(@PathVariable Integer id) {
		return JSON.toJSONString(userService.get(id));
	}

访问http://localhost:8080/smbmsDemo/admin/user/get/1
页面返回了json格式的字符串

{"address":"?????????207?","birthday":434563200000,"createdBy":1,"creationDate":1363855927000,"gender":1,"id":1,"phone":"13688889999","userCode":"admin","userName":"?????","userPassword":"1234567","userRole":1,"userRoleName":"?????"}

改造view层
userlist.jsp
增加一个div区域用于用户明细信息的展示

          	<div class="providerView">
	            <p><strong>用户编号:</strong><span></span></p>
	            <p><strong>用户名称:</strong><span></span></p>
	            <p><strong>用户性别:</strong><span></span></p>
	            <p><strong>出生日期:</strong><span></span></p>
	            <p><strong>用户电话:</strong><span></span></p>
	            <p><strong>用户地址:</strong><span></span></p>
	            <p><strong>用户角色:</strong><span></span></p>
	            <br/>
        	</div>

绑定table标签<tr id="${user.id }">.....</tr>

userlist.js
给页面元素进行赋值操作

$(function(){
	$("table.providerTable tr:gt(0)").on("mouseover", function(){
		var id = $(this).attr("id");
		$.ajax({
			url: $("#path").val() + "/admin/user/get/" + id,
			dataType: "json",
			success: function(user){
				$(".providerView span").eq(0).text(user.userCode);
				$(".providerView span").eq(1).text(user.userName);
				$(".providerView span").eq(2).text(user.gender==1?"男":"女");
				$(".providerView span").eq(3).text(user.birthday);
				$(".providerView span").eq(4).text(user.phone);
				$(".providerView span").eq(5).text(user.address);
				$(".providerView span").eq(6).text(user.userRoleName);
			}
		})
	})
})

鼠标停留在当前用户时,页面显示信息
在这里插入图片描述

JSON数据传递的中文乱码

消息转换器(StringHttpMessageConverter)中固定了转换字符编码为“ISO-8859-1”

1、在方法的参数里添加produces = {"application/json;charset=utf-8"},指定返回的内容类型为json格式数据,并且字符串的转换编码为 “UTF-8”

	@ResponseBody
	@RequestMapping(value="/admin/user/get/{id}",produces = {"application/json;charset=utf-8"})
	public String get(@PathVariable Integer id) {
		return JSON.toJSONString(userService.get(id));
	}

缺点:每个方法都要添加参数

2、装配消息转换器StringHttpMessageConverter

在springmvc的配置文件中添加,value的值可以设定不同的对象是什么编码格式(注意是写在注解的驱动中)

		<!-- 注解的驱动 ,启动注解-->
       <mvc:annotation-driven>
	       <mvc:message-converters>
	       	  <bean class="org.springframework.http.converter.StringHttpMessageConverter">
	        	<property name="supportedMediaTypes">
		        	<list>
		        	<value>application/json;charset=utf-8</value>
		        	</list>
	        	</property>
       			 </bean>
	       </mvc:message-converters>
       </mvc:annotation-driven>
      

在这里插入图片描述

bean对象中含有日期类型数据如何处理

那么在Spring MVC输出JSON数据时,日期格式是否需要处理?
在实体类加上注解@JSONField(format="yyyy-MM-dd")

	@DateTimeFormat(pattern = "yyyy-mm-dd")
	@Past(message = "日期不能是过去的日期")
	@JSONField(format="yyyy-MM-dd")
	private Date birthday;
@DateTimeFormat(pattern = “yyyy-mm-dd”)表单的字符串转换到data类型,映射到URL的类型客户端提交到服务器的格式转换
@Past(message = “日期不能是过去的日期”)填写时的格式要求
@JSONField(format=“yyyy-MM-dd”)服务器的日期发到客户端的格式的转换

缺点:也要每个都要加注解

配置文件统一修改
配置FastJson的消息转换器-FastJsonHttpMessageConverter
设置features属性:指定输出时的日期转换器为WriteDateUseDateFormat
缺点:格式固定的
FastJson规定了默认的返回日期类型DEFFAULT_DATE_FORMAT:yyyy-MM-dd HH:mm:ss故对于特殊类型字段,可使用@JSONField来控制

 <!-- 启用注解 -->
    <mvc:annotation-driven>
    	<mvc:message-converters>
			<!-- 定义FastJson的消息转换器 -->
			<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
				<property name="supportedMediaTypes">
					<list>
						<value>application/json;charset=utf-8</value>
					</list>
				</property>
				<!-- 格式化日期 -->
				<property name="features">
					<list>
						<value>WriteDateUseDateFormat</value>
					</list>
				</property>
			</bean>
    	</mvc:message-converters>
    </mvc:annotation-driven>

控制类
要返回json格式,不然会无法接受数据

	@ResponseBody
	@RequestMapping(value="/admin/user/get/{id}")
	public Object get(@PathVariable Integer id) {
		return userService.get(id);
	}

在这里插入图片描述

JSON、Ajax异步调用删除用户

在userlist.js添加

var id;
	$(".deleteUser").on("click",function(){
		id = $(this).attr("id");
		$(".zhezhao").show();
		$("#removeUser").show();
	})

	$("#no").on("click",function(){
		$(".zhezhao").hide();
		$("#removeUser").hide();
	})
	
	$("#yes").on("click",function(){
		
		$.ajax({
			url: $("#path").val() + "/admin/user/remove/" + id,
			success:function(result){
				if("true"==result){
//					var x=$("#"+id).parents("tr");
//					alert(x.find("td").eq(1).text())
					$("#"+id).parents("tr").remove();
				}
			}
		})
		
		$(".zhezhao").hide();
		$("#removeUser").hide();
	})

userlist.jsp关键代码

<span><a class="deleteUser" id="${user.id }" href="javascript:void(0)"><img src="${pageContext.request.contextPath }/statics/images/schu.png" alt="删除" title="删除"/></a></span>
						
<!--点击删除按钮后弹出的页面-->
<div class="zhezhao"></div>
<div class="remove" id="removeUser">
    <div class="removerChid">
        <h2>提示</h2>
        <div class="removeMain">
            <p>你确定要删除该用户吗?</p>
            <a href="javascript:void(0)" id="yes">确定</a>
            <a href="javascript:void(0)" id="no">取消</a>
        </div>
    </div>
</div>

控制类修改删除代码

/**
	 * 删除用户
	 */
	@ResponseBody
	@RequestMapping(value="/admin/user/remove/{id}")
	public String remove(@PathVariable Integer id,Model model,HttpSession session) {
		model.addAttribute("user", userService.get(id));
		
		//如有已上传照片,则删除
		String oldPicPath = userService.get(id).getIdPicPath();
		if(null!=oldPicPath) {
			File f= new File(session.getServletContext().getRealPath(CommonData.UPLOAD_PATH)+oldPicPath);
			f.delete();
		}
		//然后再删除用户
		//这是使用json页面删除用户的返回一个字符串
		return "" + userService.remove(id);
//		//重定向list的URL
//		return "redirect:/admin/user/list";
	}

理解数据转换和格式化

数据绑定流程

  1. DataBinder:数据转换器,数据绑定的核心部件,核心调度
  2. ConversionService:Spring类型转换体系的核心接口,解决前台form表单中时间字符串到后台Date数据类型的转换问题(转换服务-类型转换)把表单的数据类型转换为实体类型
  3. ValiDator进行验证,用实体类的注解去验证,是否为空是否超过当前日期
  4. BindingResult验证结果绑定到result里

配置了mvc:annotation-driven/标签,并没有配置ConversionService,也能通过格式化注解来解决日期的转换问题
mvc:annotation-driven/标签
DefaultAnnotationHandlerMapping
AnnotationMethodHandlerAdapter
FormattingConversionServiceFactoryBean(默认加载,无法转换日期)

日期格式的转换

不需要单独注解,就可以给全部关于日期的转换格式

使用配置转换格式

编写自定义转换器,自定义转换的规则,实现日期格式的转换

自定义转换器(StringToDateConverter.java)继承接口Converter,参数是<String, Date>
实现convert()方法:完成字符串到java.util.Date类型指定格式的转换

package cn.bdqn.smbms.util;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.core.convert.converter.Converter;
/**
 * 字符串和日期的转换器
 */
public class StringToDateConvserter implements Converter<String, Date> {
	//如果可以灵活传递参数,实例化时,可以通过构造传递参数
	//日期格式
	private String pattern;
	public StringToDateConvserter(String pattern) {
		this.pattern=pattern;
	}

	@Override
	public Date convert(String dateStr) {
		
		SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
		Date date = null;
		
		try {
			 date=sdf.parse(dateStr);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return date;
	}
}

装配自定义的ConversionService
在springmvc的配置文件添加
要注意添加conversion-service="myConversionService"

    <mvc:annotation-driven conversion-service="myConversionService">
    	<mvc:message-converters>
			略
    	</mvc:message-converters>
    </mvc:annotation-driven>

    <!-- 日期转换 -->
    <bean id="myConversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
		<property name="converters">
			<list>
				<bean class="cn.bdqn.smbms.util.StringToDateConvserter">
					<constructor-arg type="java.lang.String" value="yyyy-MM-dd"/>
				</bean>
			</list>
		</property>
    </bean>
使用控制器转换格式

通过自定义的编辑器实现日期格式的转换
使用@InitBinder装配自定义编辑器
注意:标注了@InitBinder注解的方法会在控制器初始化时调用

在userController类加上,主要不要导错包

/**
	 * 编辑自定义日期类型编译器
	 */
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
	}
	

删掉配置文件的conversion-service="myConversionService"

如果不想每个类都写一个方法
可以新建一个类实现自定义编辑器
然后控制类继承此类

了解本地化

掌握Spring MVC+Spring+MyBatis的框架整合

applicationContext-mybatis.xml

数据源相关配置
配置sql心跳包
在校验连接的同时,解决数据库重新连接的问题,从而确保连接池中连接是真实有效的连接
initialSize
maxActive
maxIdle
minIdle
maxWait
removeAbandoned
removeAbandonedTimeout
事务管理
配置SqlSessionFactoryBean
配置MapperScannerConfigurer

springmvc-servlet.xml

配置mvc:annotation-driven/标签(包括消息转换器配置)
通过mvc:resources/标签配置静态文件访问
配置支持文件上传- multipartResolver
配置多视图解析器-ContentNegotiatingViewResolver
配置拦截器-interceptors

拦截器interceptors

基于HandlerMapping,对请求实施拦截,根据业务需求,基于不同的HandlerMapping定义多个拦截器
HandlerInterceptor接口,实现类Adapter,方法如下

  1. preHandle()之前
  2. postHandle()之后
  3. afterCompletion()全部完成之后

自定义拦截器的配置-SysInterceptor
新建AuthInterceptor.java

package cn.bdqn.smbms.web.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import cn.bdqn.smbms.util.CommonData;

public class AuthInterceptor extends HandlerInterceptorAdapter {

	//true不拦截,false拦截
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		
		if(request.getSession().getAttribute(CommonData.LOGIN_USER)==null) {
			response.sendRedirect(request.getContextPath());
			return false;
		}
		return true;
	}
}

springmvc配置文件

    <mvc:interceptors>
    	<mvc:interceptor>
    		<mvc:mapping path="/admin/**"/>
    		<bean class="cn.bdqn.smbms.web.interceptor.AuthInterceptor" />
    	</mvc:interceptor>
    </mvc:interceptors>

此时访问http://localhost:8080/smbms/admin/user/list
也会显示登陆页面,因为不登陆无法访问

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值