Spring实战-SpringMVC

SpringMVC学习

1. 简介

spring-webmvc-4.1.6.RELEASE

1.1. 重要组件

DispatcherServlet 前端控制器,接收所有请求,不包含jsp

HandlerMapping 解析请求格式,判断希望执行那个具体方法

HandlerAdapter 负责调用具体方法

ViewResovler 视图解析器,准备跳转到具体的物理视图

1.2. 原理

web.xml中设置DispatcherServlet的url-pattern为/时,当用户发起请求,请求一个控制器,首先执行HandlerMapping,由DispatcherServlet调用HandlerMapping的实现类DefaultAnntationHandlerMapping解析URL,解析后调用HandlerAdapter组件的实现类AnnotationMethodHandlerAdapter调用Controller的HandlerMethod,当HandlerMethod执行完成后会返回View,被ModelAndView进行视图解析,解析后调用jsp对象的class文件并运行,最终将class文件的响应给客户端

2. 环境搭建

2.1. web.xml

配置DispatcherServlet前端控制器

url-pattern – 拦截除jsp以外所有映射

contextConfigLocation 加载 mvc配置文件springmvc.xml

不指定xml 会默认寻找[servlet-name]-serlvet.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- spring-webmvc.jar -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 加载xml文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 随tomcat加载加载 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- /拦截除了jsp /*拦截所有 -->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

2.2. springmvc.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- 控制器 --> 
<bean id="demo1" class="com.runaccpeted.controller.UserController"/>

<!-- HandlerMapping -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<!-- 解析控制器逻辑名 -->
<entry key="demo" value-ref="demo1"/>
<!-- <entry key="list" value="list"></entry> -->
</map>
</property>
</bean>

<!-- HandlerAdapter -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

<!-- ViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="./"></property>
<!-- 后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>

2.3. UserController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.runaccpeted.controller;

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

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class UserController implements Controller {

public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//默认为跳转
//指定视图名 逻辑地址
ModelAndView view = new ModelAndView("main");
return view;
}
}

3. Spring – SpringMVC

3.1. DispatcherServlet

结构

所有组件

1
2
3
4
5
6
7
8
9
10
11
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}

doService

1
2
3
protected void doService(HttpServletRequest request, HttpServletResponse response)throws Exception{
doDispatch(request, response);
}

doDispatch

1
2
3
4
5
6
7
8
9
10
11
12
13
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;

ModelAndView mv = null;

processedRequest = checkMultipart(request);
mappedHandler = getHandler(processedRequest);

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}

3.2. 父类FrameworkServlet

spring 为springmvc的父容器

springmvc能调用spring的内容

HttpServletBean

1
2
3
4
@Override
public final void init() throws ServletException {
initServletBean();
}

3.3. SimpleControllerHandlerAdapter

1
2
3
4
5
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {

return ((Controller) handler).handleRequest(request, response);
}

3.4. SimpleUrlHandlerMapping

1
2
3
4
5
6
7
8
9
10
11
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
urlMap.forEach((url, handler) -> {
if (!url.startsWith("/")) {
url = "/" + url;
}
if (handler instanceof String) {
handler = ((String) handler).trim();
}
registerHandler(url, handler);
});
}

4. 注解方式

mvc:annotation-driven 自动配置了HandlerMapping和HandlerAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
<beans 
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!-- 扫描注解 -->
<context:component-scan base-package="com.runaccpeted.controller"/>
<!-- 扫描驱动 -->
<mvc:annotation-driven/>
</beans>

4.1. @Controller 单例控制器

4.2. @RequestMapping(“/url”) 映射路径

无论方法返回值是什么,都进行跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.runaccpeted.controller;

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

@Controller
public class LoginController {

@RequestMapping("/demo")
public String demo(){
return "/main.jsp";
}
}

5. 放行静态资源

1
2
3
4
<mvc:resources location="/js/" mapping="/js/**"></mvc:resources>
<mvc:resources location="/css/" mapping="/css/**"></mvc:resources>
<mvc:resources location="/images/" mapping="/images/**"></mvc:resources>
<mvc:resources location="/files/" mapping="/files/**"></mvc:resources>

http://localhost:8080/Spring9.28/js/jquery.min.js

mapping 虚拟路径 /js/* js下所有资源* js下所有子文件

location 实地址 /js/ 在与WEB-INF同级文件下

6. 字节编码过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 字符编码过滤器 -->
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

7. 传参

参数放在方法参数中,所有参数都可以被赋值

7.1. 对象

1
2
3
4
5
6
public class User {

private int id;
private String username;
private String password;
}

传入的参数name为对象成员变量名,必须有get/set方法

1
2
3
4
5
<form action="./demo" method="get">
<input type="text" name="username" id="name"/>
<input type="password" name="password" id="pwd"/>
<input type="submit" value="提交"/>
</form>

Controller

1
2
3
4
5
6
@RequestMapping("/demo")
public String demo(User user,HttpServletRequest request,HttpServletResponse response){
//User [id=0, username=张三, password=123]
System.out.println(user);
return "/main.jsp";
}

7.2. 参数不匹配@RequestParam

value=”” 指定传入的参数

defaultValue=”” 指定默认值

required=true 必须有该参数值 == 表列 not null

1
2
3
4
5
6
7
import org.springframework.web.bind.annotation.RequestParam;

@RequestMapping("/demo")
public String demo(@RequestParam(value="username")String name,@RequestParam(value="password")String pwd,HttpServletRequest request,HttpServletResponse response){
System.out.println(name+" "+pwd);
return "/main.jsp";
}

7.3. list参数-多选参数

1
public String demo(@RequestParam("fav") List<String> list){}

7.4. 类中对象.属性作为参数

1
<input type="text" name="peo.name"/>

java中.表示调用

1
2
3
4
5
6
7
8
9
10
11
12
13
public class User{
private People peo;
}

public class People{
private String name;
}

@RequestMapping("/demo")
public String demo(User user){
System.out.println(user);
return "/main.jsp";
}

7.5. @PathVariable

1
2
3
4
5
@RequestMapping("/demo/{id}/{name}")
public String demo(@PathVariable String id,@PathVariable String name){
System.out.println(id+" "+name);
return "/main.jsp";
}

7.6. HashMap传值

1
2
3
4
5
@RequestMapping("demo2")
public String test2(HashMap<String,String> map){
map.put("m","map传值");
return "/main.jsp";
}

作用域在request

1
${requestScope.m}

7.7. Model传值

1
2
3
4
5
@RequestMapping("demo3")
public String test3(Model model){
model.addAttribute("model", "model传值");
return "/main.jsp";
}

作用域在request

1
${requestScope.model}

7.8. ModelAndView传值

1
2
3
4
5
6
7
@RequestMapping("demo4")
public ModelAndView test4(){

ModelAndView view = new ModelAndView("/main.jsp");
view.addObject("view", "view");
return view;
}

作用域在request

1
${requestScope.view}

8. 跳转方式

默认为请求转发

写在return的string语句中

1
2
3
return "redirect:/main.jsp";
//==return "forward:/main.jsp";
return "/main.jsp";

9. 自定义视图解析器

1
2
3
4
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>

添加跳转方式后自定义视图解析器无效,调用默认解析器

1
2
return "redirect:main";
return "forward:main";

10. @ResponseBody

返回值满足key-value形式 将数据转为json 以流的形式输出

响应标头 Content-Type: application/json;charset=UTF-8

1
2
3
4
5
6
7
8
9
@RequestMapping("demo1")
@ResponseBody
public User test(){
User user = new User();
user.setId(1);
user.setUsername("abc");
user.setPassword("123");
return user;
}

转不成json,以文本输出

响应标头 Content-Type: text/html 无法解决中英文乱码

1
2
3
4
5
@RequestMapping("demo1")
@ResponseBody
public String test(){
return "Hello";
}

设置响应头

1
2
@RequestMapping(value="demo1",produces="text/html;charset=utf-8")
@ResponseBody

11. 文件下载

1
<a href="download?fileName=a.jpg"></a>

commons-fileupload-1.3.1

commons-io-2.2

设置响应流文件进行下载

response.setHeader(“Content-Disposition”,”attachment;filename=”+file);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RequestMapping("/download")
public void demo(String file,HttpServletRequest request,HttpServletResponse response){
response.setHeader("Content-Disposition", "attachment;filename="+file);

try {
ServletOutputStream os = response.getOutputStream();
String path=request.getServletContext().getRealPath("files");
File f = new File(path,file);

byte[] bytes = FileUtils.readFileToByteArray(f);
os.write(bytes);
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}

12. 文件上传

12.1. 前端

form设置enctype为multipart/form-data,以流形式输出

1
2
3
4
<form action="./upload" enctype="multipart/form-data" method="post">
<input type="file" name="file">
<input type="submit" value="下载">
</form>

12.2. 文件视图解析器

id是必须要的,图片大小小于10kB

1
2
3
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="MaxUploadSize" value="10240"></property>
</bean>

12.3. 控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RequestMapping("upload")
public String upload(MultipartFile file,HttpServletRequest request){
//files文件本地路径
String path=request.getServletContext().getRealPath("files");
//文件名
String fileName = file.getOriginalFilename();
//文件名后缀
String suffix=fileName.substring(fileName.lastIndexOf("."));
//生成一系列数字
String name=UUID.randomUUID().toString();
///Users/Learning/Resource/workspace/myEclipse/.metadata/.me_tcat85/webapps/Spring9.28/files/0612f926-6527-4d70-99cf-815540c0e52d.jpg
System.out.println(path+"/"+name+suffix);
try {
FileUtils.copyInputStreamToFile(file.getInputStream(), new File(path,name+suffix));
} catch (IOException e) {
e.printStackTrace();
}
return "/main.jsp";
}

12.4. 异常视图

可用于处理服务器异常

1
2
3
4
5
6
7
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.springframework.web.multipart.MaxUploadSizeExceededException">error</prop>
</props>
</property>
</bean>

error.jsp

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Images error</title>
</head>
<body>
Maximum upload size of 10240 bytes exceeded;
</body>
</html>

13. 拦截器-针对控制器

发送请求时被拦截器拦截,在控制器前后添加额外功能

仅针对控制器,只拦截控制器

13.1. implements HandlerInterceptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.runaccpeted.interceptor;

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

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.sun.istack.internal.logging.Logger;

public class DemoInterceptor implements HandlerInterceptor {

//进入控制器之前执行,false则不进入控制器
//权限拦截
public boolean preHandle(HttpServletRequest request , HttpServletResponse response, Object object) throws Exception {

return true;
}

//控制器执行完成,进入到jsp之前执行
public void postHandle(HttpServletRequest request, HttpServletResponse response , Object obj, ModelAndView view)
throws Exception {
//视图名
String viewName=view.getViewName();
//传入的Model键值对
String value=view.getModel().get("key").toString();
//敏感词汇过滤
value.replace("国家", "**");
view.getModel().put("key", value);

}

//日志记录
//jsp执行完成后执行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception exception)
throws Exception {
Logger logger = Logger.getLogger(DemoInterceptor.class);
logger.info(exception.getMessage());

}
}

13.2. 注册拦截器

mvc:interceptor 拦截一些控制器

1
2
3
4
5
6
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/demo"/>
<bean class="com.runaccpeted.interceptor.DemoInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>

拦截所有控制器

1
2
3
<mvc:interceptors>
<bean class="com.runaccpeted.interceptor.DemoInterceptor"></bean>
</mvc:interceptors>

13.3. 拦截器栈

执行顺序和springmvc.xml中配置顺序有关

1
2
3
4
<mvc:interceptors>
<bean class="com.runaccpeted.interceptor.DemoInterceptor1"></bean>
<bean class="com.runaccpeted.interceptor.DemoInterceptor2"></bean>
</mvc:interceptors>

preHandler1 –> preHandler2 -> controller –> postHandler2 –> postHandler1 –> jsp –> afterCompletion2 –> afterCompletion1

14. 登陆验证

DispatcherServlet放行jsp ,为了保证jsp不被放行

jsp放在WEB-INF –> pages下

控制器

1
2
3
4
@RequestMapping("{page}")
public String page(@PathVariable String page){
return "/WEB-INF/pages/"+page+".jsp";
}

需要视图解析器配置前后缀

1
2
3
4
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>

优先匹配最适合的

1
2
3
4
5
6
7
8
9
10
@RequestMapping("login")
public String login(@RequestParam("username")String uname,@RequestParam("password")String pwd,HttpServletRequest request){
User user=service.selByNamePwd(uname,pwd);
if(user!=null){
request.getSession.setAttribute("user",user);
return "main";
}else{
return "redirect:/login.jsp";
}
}

拦截器拦截所有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.runaccpeted.interceptor;

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

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.sun.istack.internal.logging.Logger;

public class DemoInterceptor implements HandlerInterceptor {

//进入控制器之前执行,false则不进入控制器
//权限拦截
public boolean preHandle(HttpServletRequest request , HttpServletResponse response, Object object) throws Exception {
if(request.geturi().endWith("login")){
return true;
}else{
if(request.getSession().getAttribute("user")!=null){
return true;
}else{
response.sendRedirect("./login.jsp");
return false;
}
}
return false;
}
}

拦截器

1
2
3
<mvc:interceptors>
<bean class="com.runaccpeted.interceptor.DemoInterceptor"></bean>
</mvc:interceptors>
本文结束  感谢您的阅读
  • 本文作者: Wang Ting
  • 本文链接: /zh-CN/2019/09/22/Spring实战-SpringMVC/
  • 发布时间: 2019-09-22 06:53
  • 更新时间: 2022-10-24 20:36
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!