Spring MVC
三层架构
-
咱们开发服务器端程序,一般都基于两种形式,一种C/S架构程序,一种B/S架构程序
-
使用Java语言基本上都是开发B/S架构的程序,B/S架构又分成了三层架构
-
三层架构
-
表现层:WEB层,用来和客户端进行数据交互的。表现层一般会采用MVC的设计模型
-
-
业务层:处理公司具体的业务逻辑的
-
持久层:用来操作数据库的
-
-
MVC模型
-
MVC全名是Model View Controller 模型视图控制器,每个部分各司其职。
-
Model:数据模型,JavaBean的类,用来进行数据封装。
-
View:指JSP、HTML用来展示数据给用户
-
Controller:用来接收用户的请求,整个流程的控制器。用来进行数据校验等。
-
SpringMVC的入门案例
-
是一种基于Java实现的MVC设计模型的请求驱动类型的轻量级WEB框架。
-
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。
-
使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts2等。
-
SpringMVC在三层架构中的位置是表现层框架。
SpringMVC的入门程序
-
创建WEB工程,引入开发的jar包
-
在web.xml配置文件中核心控制器DispatcherServlet
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 5 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 6 id="WebApp_ID" version="2.5"> 7 <!-- 配置 spring mvc 的核心控制器 --> 8 <servlet> 9 <servlet-name>SpringMVCDispatcherServlet</servlet-name> 10 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 11 <!-- 配置初始化参数,用于读取 SpringMVC 的配置文件 --> 12 <init-param> 13 <param-name>contextConfigLocation</param-name> 14 <param-value>classpath:SpringMVC.xml</param-value> 15 </init-param> 16 <!-- 配置 servlet 的对象的创建时间点:应用加载时创建。 17 取值只能是非 0 正整数,表示启动顺序 --> 18 <load-on-startup>1</load-on-startup> 19 </servlet> 20 <servlet-mapping> 21 <servlet-name>SpringMVCDispatcherServlet</servlet-name> 22 <url-pattern>/</url-pattern> 23 </servlet-mapping> 24 </web-app>
-
编写springmvc.xml的配置文件
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:mvc="http://www.springframework.org/schema/mvc" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans.xsd 8 http://www.springframework.org/schema/mvc 9 http://www.springframework.org/schema/mvc/spring-mvc.xsd 10 http://www.springframework.org/schema/context 11 http://www.springframework.org/schema/context/spring-context.xsd"> 12 <!-- 配置创建 spring 容器要扫描的包 --> 13 <context:component-scan base-package="com.itheima"></context:component-scan> 14 15 <!-- 配置视图解析器 --> 16 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 17 <!-- 配置前缀和后缀 以后写转发路径的时候直接写文件名就可以了 --> 18 <property name="prefix" value="/WEB-INF/pages/"></property> 19 <property name="suffix" value=".jsp"></property> 20 </bean> 21 </beans>
-
编写index.jsp和HelloController控制器类
-
index.jsp
-
HelloController
-
-
在WEB-INF目录下创建pages文件夹,编写success.jsp的成功页面
-
启动Tomcat服务器,进行测试
入门案例的执行过程分析
-
入门案例的执行流程
-
当启动Tomcat服务器的时候,因为配置了load-on-startup标签,所以会创建DispatcherServlet对象, 就会加载springmvc.xml配置文件
-
开启了注解扫描,那么@Controller注解的对象就会被创建
-
从index.jsp发送请求,请求会先到达DispatcherServlet核心控制器,根据配置@RequestMapping注解 找到执行的具体方法
-
根据执行方法的返回值,再根据配置的视图解析器,去指定的目录下查找指定名称的JSP文件
-
Tomcat服务器渲染页面,做出响应
-
-
入门案例中的组件
-
前端控制器(DispatcherServlet)
-
-
处理器映射器(HandlerMapping)
-
处理器(Handler)
-
-
处理器适配器(HandlAdapter)
-
视图解析器(View Resolver)
-
-
视图(View)
-
<mvc:annotation-driven>说明
在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。
使 用 mvc:annotation-driven 自动加载 RequestMappingHandlerMapping (处理映射器) 和 RequestMappingHandlerAdapter ( 处 理 适 配 器 ) , 可 用 在 SpringMVC.xml 配 置 文 件 中 使 用mvc:annotation-driven替代注解处理器和适配器的配置。
-
RequestMapping注解
-
RequestMapping注解的作用是建立请求URL和处理方法之间的对应关系
-
RequestMapping注解可以作用在方法和类上
-
作用在类上:第一级的访问目录
-
作用在方法上:第二级的访问目录
-
细节:路径可以不编写 / 表示应用的根目录开始
-
细节:${ pageContext.request.contextPath }也可以省略不写,但是路径上不能写 /
-
-
RequestMapping的属性
-
path 指定请求路径的url
-
value value属性和path属性是一样的
-
mthod 指定该方法的请求方式
-
params 指定限制请求参数的条件
-
例如
params = {"accountName"},表示请求参数必须有 accountName
params = {"moeny!100"},表示请求参数中 money 不能是 100。
-
-
headers 发送的请求中必须包含的请求头
-
-
请求参数的绑定
-
请求参数的绑定说明
-
绑定机制
-
表单提交的数据都是k=v格式的 username=haha&password=123
-
SpringMVC的参数绑定过程是把表单提交的请求参数,作为控制器中方法的参数进行绑定的
-
要求:提交表单的name和参数的名称是相同的
-
-
支持的数据类型
-
基本数据类型和字符串类型
-
提交表单的name和参数的名称是相同的、严格区分大小写
-
实体类型(JavaBean)
-
提交表单的name和JavaBean中的属性名称需要一致。
-
如果一个JavaBean类中包含其他的引用类型
-
那么表单的name属性需要编写成:对象.属性
-
例如:address.name
-
-
-
-
集合数据类型(List、map集合等)
-
JSP页面List集合编写方式:name="accounts[0].name"
-
JSP页面Map集合编写方式:name="accountMap['one'].name"
-
-
-
请求参数中文乱码的解决
-
在web.xml中配置Spring提供的过滤器类
-
1 <filter> 2 <filter-name>CharacterEncodingFilter</filter-name> 3 <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> 4 <!-- 设置过滤器中的属性值 --> 5 <init-param> 6 <param-name>encoding</param-name> 7 <param-value>UTF-8</param-value> 8 </init-param> 9 <!-- 启动过滤器 --> 10 <init-param> 11 <param-name>forceEncoding</param-name> 12 <param-value>true</param-value> 13 </init-param> 14 </filter> 15 16 <!-- 过滤所有请求 --> 17 <filter-mapping> 18 <filter-name>CharacterEncodingFilter</filter-name> 19 <url-pattern>/*</url-pattern> 20 </filter-mapping> 21 22 <!--在 springmvc 的配置文件中可以配置,静态资源不过滤:--> 23 <!-- location 表示路径,mapping 表示文件,**表示该目录下的文件以及子目录的文件 --> 24 <mvc:resources location="/css/" mapping="/css/**"/> 25 <mvc:resources location="/images/" mapping="/images/**"/> 26 <mvc:resources location="/scripts/" mapping="/javascript/**"/> 27 <!-- get 请求方式:--> 28 <!-- tomacat 对 GET 和 POST 请求处理方式是不同的,GET 请求的编码问题,要改 tomcat 的 server.xml--> 29 <!-- 配置文件,如下:--> 30 <Connector connectionTimeout="20000" port="8080" 31 protocol="HTTP/1.1" redirectPort="8443"/> 32 改为: 33 <Connector connectionTimeout="20000" port="8080" 34 protocol="HTTP/1.1" redirectPort="8443" 35 useBodyEncodingForURI="true"/> 36 <!-- 如果遇到 ajax 请求仍然乱码,请把:--> 37 <!-- useBodyEncodingForURI="true"改为 URIEncoding="UTF-8"--> 38 <!-- 即可。-->
自定义类型转换器
表单提交的任何数据类型全部都是字符串类型,但是后台定义Integer类型,数据也可以封装上,说明Spring框架内部会默认进行数据类型转换。
-
如果想自定义数据类型转换,可以实现Converter的接口
-
自定义类型转换器
-
定义一个类,实现 Converter 接口
import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; public class StringToDateConverter implements Converter<String, Date> { /** * 用于把 String 类型转成日期类型 */ @Override public Date convert(String source) { DateFormat format = null; try { if (StringUtils.isEmpty(source)) { throw new NullPointerException("请输入要转换的日期"); } format = new SimpleDateFormat("yyyy-MM-dd"); Date date = format.parse(source); return date; } catch (Exception e) { throw new RuntimeException("输入日期有误"); } } }
-
-
注册自定义类型转换器,在springmvc.xml配置文件中编写配置
1 <!-- 配置类型转换器工厂 --> 2 <bean id="converterService" 3 class="org.springframework.context.support.ConversionServiceFactoryBean"> 4 <!-- 给工厂注入一个新的类型转换器 --> 5 <property name="converters"> 6 <array> 7 <!-- 配置自定义类型转换器 --> 8 <bean class="com.itheima.web.converter.StringToDateConverter"></bean> 9 </array> 10 </property> 11 </bean>
-
在 annotation-driven 标签中引用配置的类型转换服务
1 <mvc:annotation-driven conversion-service="converterService"></mvc:annotation-driven>
-
-
使用注解转换
-
在pojo声明的属性上添加注解@DateTimeFormat(pattern = "yyyy-MM-dd")
-
该注解也可以添加在参数前
-
在控制器中使用原生的ServletAPI对象
-
只需要在控制器的方法参数定义HttpServletRequest和HttpServletResponse对象
常用的注解
-
RequestParam注解
-
作用:把请求中的指定名称的参数传递给控制器中的形参赋值
-
属性
-
value:请求参数中的名称
-
required:请求参数中是否必须提供此参数,默认值是true,必须提供
-
代码如下
public String useRequestParam(@RequestParam("name")String username,@RequestParam(value = "age", required = false)Integer age)
-
-
-
RequestBody注解
-
作用:用于获取请求体的内容(注意:get方法不可以)
-
属性
-
required:是否必须有请求体,默认值是true
-
-
代码如下
public String useRequestBody(@RequestBody(required=false) String body)
-
-
PathVariable注解
-
作用:拥有绑定url中的占位符的。例如:url中有/delete/{id},{id}就是占位符,url 支持占位符是 spring3.0 之后加入的。是 springmvc 支持 rest 风格 URL 的一个重要标志。
-
属性
-
value:指定url中的占位符名称
-
-
Restful风格的URL
-
请求路径一样,可以根据不同的请求方式去执行后台的不同方法
-
restful风格的URL优点
-
结构清晰
-
符合标准
-
易于理解
-
扩展方便
-
-
伪代码优点展示
/account/1 HTTP GET : 得到 id = 1 的 account
/account/1 HTTP DELETE: 删除 id = 1 的 account
/account/1 HTTP PUT: 更新 id = 1 的 account
/account HTTP POST: 新增 account -
开启使游览器支持其他请求方式
-
在 web.xml 中配置该过滤器。
-
请求方式必须使用 post 请求。
-
按照要求提供_method 请求参数,该参数的取值就是我们需要的请求方式。
-
-
使用方式
<!--jsp代码-->
<!--更新-->
<form action="springmvc/testRestPUT/1" method="post">
用户名称:
<input type="text" name="username">
<br/>
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="更新">
</form>
<!--java代码-->
@RequestMapping(value = "/testRestPUT/{id}", method = RequestMethod.PUT)
public String testRestfulURLPUT(@PathVariable("id")Integer id,User user){
System.out.println("rest put "+id+","+user);
return"success";
}
-
-
代码如下
<a href="springmvc/usePathVariable/100">pathVariable 注解</a>
@RequestMapping("/usePathVariable/{id}")
public String usePathVariable(@PathVariable("id") Integer id){}
-
-
RequestHeader注解
-
作用:获取指定请求头的值
-
属性:value:请求头的名称
-
-
CookieValue注解
-
作用:用于获取指定cookie的名称的值
-
属性:value:cookie的名称
-
-
ModelAttribute注解
-
作用
-
出现在方法上:表示当前方法会在控制器方法执行前线执行。
-
出现在参数上:获取指定的数据给参数赋值。
-
-
应用场景
-
当提交表单数据不是完整的实体数据时,保证没有提交的字段使用数据库原来的数据。
-
-
具体的代码
-
修饰的方法有返回值
-
修饰的方法没有返回值
-
-
-
SessionAttributes注解
-
作用:用于多次执行控制器方法间的参数共享
-
属性.
-
value:指定存入属性的名称
-
type:用于指定存入的数据类型。
-
-
代码如下
@Controller("sessionAttributeController") @RequestMapping("/springmvc") @SessionAttributes(value = {"username", "password"}, types = {Integer.class}) public class SessionAttributeController { /** * 把数据存入 SessionAttribute * * @param model * @return Model 是 spring 提供的一个接口,该接口有一个实现类 ExtendedModelMap * 该类继承了 ModelMap,而 ModelMap 就是 LinkedHashMap 子类 */ @RequestMapping("/testPut") public String testPut(Model model) { model.addAttribute("username", "泰斯特"); model.addAttribute("password", "123456"); model.addAttribute("age", 31); //跳转之前将数据保存到 username、password 和 age 中,因为注解@SessionAttribute 中有 这几个参数 return "success"; } @RequestMapping("/testGet") public String testGet(ModelMap model) { System.out.println(model.get("username") + ";" + model.get("password") + ";" + model.get("a ge")); return "success"; } @RequestMapping("/testClean") public String complete(SessionStatus sessionStatus) { sessionStatus.setComplete(); return "success"; } }
-
响应数据和结果视图
返回值分类
-
返回字符串:
-
Controller方法返回字符串可以指定逻辑视图的名称,根据视图解析器为物理视图的地址。
-
springMVC方式的转发和重定向
转发:return "forward:/WEB-INF/pages/success.jsp";
重定向:return "redirect:testReturnModelAndView"
-
-
返回值是void
-
如果控制器的方法返回值编写成void,执行程序报404的异常,默认查找JSP页面没有找到。
-
可以把Servlet 原始 API 可以作为控制器中方法的参数
public void testReturnVoid(HttpServletRequest request,HttpServletResponse response)
throws Exception {}-
然后通过原始的重定向或者内部转发的方式跳转页面
-
-
-
返回值是ModelAndView对象
-
ModelAndView对象是Spring提供的一个对象,可以用来调整具体的JSP视图
-
具体的代码如下
//实例代码
public ModelAndView testReturnModelAndView(){
ModelAndView mv = new ModelAndView();
mv.addObject("username", "张三");
mv.setViewName("success");
return mv;
}
//响应的jsp代码
${requestScope.username}
-
ResponseBody响应json数据
需求:使用@ResponseBody 注解实现将 controller 方法返回对象转换为 json 响应给客户端
1 jsp 中的代码: 2 3 <script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery.min.js"></script> 4 <script type="text/javascript"> 5 $(function () { 6 $("#testJson").click(function () { 7 $.ajax({ 8 type: "post", 9 url: "${pageContext.request.contextPath}/testResponseJson", 10 contentType: "application/json;charset=utf-8", 11 data: '{"id":1,"name":"test","money":999.0}', 12 dataType: "json", 13 success: function (data) { 14 alert(data); 15 } 16 }); 17 }); 18 }) 19 </script> 20 <!-- 测试异步请求 --> 21 <input type="button" value="测试 ajax 请求 json 和响应 json" id="testJson"/>
Controller层java代码: @Controller("jsonController") public class JsonController { /** * 测试响应 json 数据 */ @RequestMapping("/testResponseJson") public @ResponseBody Account testResponseJson(@RequestBody Account account) { System.out.println("异步请求:" + account); return account; } }
SpringMVC实现文件上传
文件上传的必要前提
-
form 表单的 enctype 取值必须是:multipart/form-data
-
method 属性取值必须是 Post
文件上传的原理分析
当 form 表单的 enctype 取值不是默认值后,request.getParameter()将失效。 enctype=”application/x-www-form-urlencoded”时,form 表单的正文内容是:
key=value&key=value&key=value
当 form 表单的 enctype 取值为 Mutilpart/form-data 时,请求正文内容就变成:
每一部分都是 MIME 类型描述的正文
-----------------------------7de1a433602ac 分界符
Content-Disposition: form-data; name="userName" 协议头
aaa 协议的正文
-----------------------------7de1a433602ac
Content-Disposition: form-data; name="file";
filename="C:\Users\zhy\Desktop\fileupload_demofile\b.txt"
Content-Type: text/
plain 协议的类型(MIME 类型)
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-----------------------------7de1a433602ac--
借助第三方组件实现文件上传
使用 Commons-fileupload 组件实现文件上传,需要导入该组件相应的支撑 jar 包:Commons-fileupload 和commons-io。
实现步骤
-
导入文件上传所需jar包
-
编写 jsp 页面,表单form标签enctype属性设置为multipart/form-data
-
编写控制器
@Controller("fileUploadController") public class FileUploadController { /** * 文件上传 */ @RequestMapping("/fileUpload") public String testResponseJson(String picname, MultipartFile uploadFile, HttpServletRequest request) throws Exception { //定义文件名 String fileName = ""; //1.获取原始文件名 String uploadFileName = uploadFile.getOriginalFilename(); //2.截取文件扩展名 String extendName =uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1,uploadFileName.length()); //3.把文件加上随机数,防止文件重复 String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase(); //4.判断是否输入了文件名 if (!StringUtils.isEmpty(picname)) { fileName = uuid + "_" + picname + "." + extendName; } else { fileName = uuid + "_" + uploadFileName; } System.out.println(fileName); //2.获取文件路径 ServletContext context = request.getServletContext(); String basePath = context.getRealPath("/uploads"); //3.解决同一文件夹中文件过多问题 String datePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); //4.判断路径是否存在 File file = new File(basePath + "/" + datePath); if (!file.exists()) { file.mkdirs(); } //5.使用 MulitpartFile 接口中方法,把上传的文件写到指定位置 uploadFile.transferTo(new File(file, fileName)); return "success"; } }
-
配置文件解析器
1 <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> 2 <!-- id 的值是固定的--> 3 <!-- 设置上传文件的最大尺寸为 5MB --> 4 <property name="maxUploadSize"> 5 <value>5242880</value> 6 </property> 7 </bean>
SpringMVC的异常处理
异常处理思路
-
Controller调用service,service调用dao,异常都是向上抛出的,最终有DispatcherServlet找异常处理器进行异常的处理。
SpringMVC的异常处理
-
自定义异常处理器
public class GlobalExceptionHandle implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { ModelAndView mav = new ModelAndView(); mav.addObject("except", e); mav.addObject("obj", o); // 指定视图 mav.setViewName("error"); return mav; } }
-
配置异常处理器
<!-- 配置自定义异常处理器 -->
<bean id="handlerExceptionResolver" class="com.itheima.exception.GlobalExceptionHandle"/>
SpringMVC框架中的拦截器
拦截器的概述
-
SpringMVC框架中的拦截器用于对处理器进行预处理和后处理的技术。
-
可以定义拦截器链,连接器链就是将拦截器按着一定的顺序结成一条链,在访问被拦截的方法时,拦截器链 中的拦截器会按着定义的顺序执行。
-
拦截器和过滤器的功能比较类似,有区别
-
过滤器是Servlet规范的一部分,任何框架都可以使用过滤器技术。
-
拦截器是SpringMVC框架独有的。
-
过滤器配置了/*,可以拦截任何资源。
-
拦截器只会对控制器中的方法进行拦截。
-
-
拦截器也是AOP思想的一种实现方式
-
想要自定义拦截器,需要实现HandlerInterceptor接口。
自定义拦截器步骤
-
创建类,实现HandlerInterceptor接口,重写需要的方法
-
在springmvc.xml中配置拦截器类
1 <!-- 配置拦截器 --> 2 <mvc:interceptors> 3 <mvc:interceptor> 4 <mvc:mapping path="/**"/> 5 <bean id="handlerInterceptorDemo1" class="com.itheima.web.interceptor.HandlerInterceptorDemo1"></bean> 6 </mvc:interceptor> 7 </mvc:interceptors>
HandlerInterceptor接口中的方法
-
preHandle方法是controller方法执行前拦截的方法
-
可以使用request或者response跳转到指定的页面
-
return true放行,执行下一个拦截器,如果没有拦截器,执行controller中的方法。
-
return false不放行,不会执行controller中的方法。
-
-
postHandle是controller方法执行后执行的方法,在JSP视图执行前。
-
可以使用request或者response跳转到指定的页面
-
如果指定了跳转的页面,那么controller方法跳转的页面将不会显示。
-
postHandle方法是在JSP执行后执行
-
request或者response不能再跳转页面了
-
-
拦截器的简单案例(验证用户是否登录)
controller层代码: @RequestMapping("/login") public String login(Model model) throws Exception { return "login"; } //登陆提交 //userid:用户账号,pwd:密码 @RequestMapping("/loginsubmit") public String loginsubmit(HttpSession session, String userid, String pwd) throws Exception { //向 session 记录用户身份信息 session.setAttribute("activeUser", userid); return "redirect:/main.jsp"; } //退出 @RequestMapping("/logout") public String logout(HttpSession session) throws Exception { //session 过期 session.invalidate(); return "redirect:index.jsp"; } //拦截器代码 public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //如果是登录页面则放行 if (request.getRequestURI().indexOf("login.action") >= 0) { return true; } HttpSession session = request.getSession(); //如果用户已登录也放行 if (session.getAttribute("user") != null) { return true; } //用户没有登录就跳转到登录页面 request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response); return false; } }