Spring MVC 说是框架,对Tomcat来讲其实就是一个Servlet,关于如何从上古时期的Servlet演化到现在的SpringMVC的,能够看看这篇博文:Spring MVC是如何逐步简化Servlet的编程的html
业务分层以后既方便解耦,条例也更清晰。所以对于后台web层中又分了MVC三层:前端
然而如今前端都在使用MVVM模型 😑java
环境铺垫:
web
建立maven工程并设置打包方式为war
ajax
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <packaging>war</packaging>
而后是添加依赖:spring
<dependencies> <!--springmmvc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.9.RELEASE</version> </dependency> <!--日志--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.28</version> <scope>test</scope> </dependency> <!--jsp相关--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <scope>provided</scope> <version>2.2</version> </dependency> </dependencies>
还用个吊毛的JSP啊🙃apache
日志配置文件log4j.properties:编程
### direct log messages to stdout ### ### 输出源的配置 语法 log4j.appender.输出源的名字=输出源的实现类 ### ### log4j.appender.输出源的名字.属性=属性值 ### log4j.appender.a=org.apache.log4j.ConsoleAppender log4j.appender.a.Target=System.out log4j.appender.a.layout=org.apache.log4j.PatternLayout log4j.appender.a.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n ### set log levels - for more verbose logging change 'info' to 'debug' ### ### 日志记录器的配置,固定语法 log4j.rootLogger=输出级别, 输出源,输出源... ### log4j.rootLogger=debug, a
springMVC主配置文件springMVC.xml:json
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 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.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"> <!-- 开启注解扫描 --> <context:component-scan base-package="com.bilibili"></context:component-scan> </beans>
jsp页面:本文使用的是老旧的方式进行展现,新方式先后端分离,文章末尾有介绍JSP方式与先后端分离方式的不一样后端
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>hello world!!!</h1> </body> </html>
在web.xml中配置映射:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <servlet> <servlet-name>DispatcherServlet</servlet-name> <!--配置springmvc的核心控制器 --> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!-- 配置springmvc的核心配置文件的位置,Key是固定的 --> <param-name>contextConfigLocation</param-name> <param-value>classpath:springMVC.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <!-- 配置springmvc的核心控制器拦截的请求,咱们拦截全部已 do为后缀的全部请求 --> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
建立自定义处理器:
/** * @Controller:声明当前类是一个控制器 */ @Controller public class MyController { /** * @RequestMapping: 配置指定的请求由指定的方法来处理 * @return */ @RequestMapping("/show1.do") public ModelAndView test1(){ ModelAndView mv = new ModelAndView(); mv.setViewName("/WEB-INF/views/hello.jsp"); mv.addObject("msg","springMVC入门"); return mv; } }
Spring MVC中的组件执行流程:
组件说明:
其中大部分组件框架都已经提供而且已经配置完成,好比映射器和适配器,我么须要配置的是视图解析器:
<context:component-scan base-package="com.bilibili"></context:component-scan> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 配置视图解析器的后缀,去找以 .jsp结尾的视图 --> <property name="suffix" value=".jsp"></property> <!-- 配置视图解析器的 ,也就是去哪里找视图--> <property name="prefix" value="/WEB-INF/views/"></property> </bean> <!-- 低版本须要主动配置注解驱动 --> <!-- <mvc:annotation-driven></mvc:annotation-driven> -->
@Controller
:表明这是一个Handler,也就是执行器。@RequestMapping()
:规定映射规则Ant映射风格:
?
:匹配一个字符*
:匹配任意个字符**
:匹配任意路径Rest映射风格:
路径中使用{key}
来传参
举例说明:
/** * Rest风格的映射 * {key}表示占位符,key为URL中容许的字符 * 可是注意,咱们能够经过@PathVariable(key) 获取地址中的参数。 * @PathVariable 注解中的key必须和{key}占位符中的key一致,才能获取。形参名称能够是任意名 * @return */ @RequestMapping("show5/{name}/{id}") public ModelAndView test5(@PathVariable("name")String name,@PathVariable("id") int id){ ModelAndView mv = new ModelAndView(); mv.setViewName("hello"); mv.addObject("msg","Rest风格的使用:name="+name+" id="+id); return mv; }
@RequestMapping()
注解的属性:
value
:映射路径method
:请求方法RequestMethod.GET
、RequestMethod.POST
等,能够为多个params
:参数为字符串数组"id"
必须携带参数(id)"!id"
不能携带参数(id)"id=2"
参数id必须为2(必须有参数id)"id!=2"
参数id不能为2(能够没有)组合注解:
@GetMapping
:至关于@RequestMapping(method = RequestMethod.GET)@PostMapping
:至关于@RequestMapping(method = RequestMethod.POST)@PutMapping
:至关于@RequestMapping(method = RequestMethod.PUT)@DeleteMapping
:至关于@RequestMapping(method = RequestMethod.DELETE)@RequestMapping注解的方法返回值是String
:
@RequestMapping("show17") //springMVC自动传递model public String test17(Model model){ model.addAttribute("msg","控制器优化后的代码"); return "hello";//springMVC默认将返回的字符串直接做为视图名 }
@RequestMapping注解的方法返回值是void:
@ResponseStatus(HttpStatus.OK)//若是不响应页面,须要设置响应的状态为ok @RequestMapping("show18") public void test18(Model model){ System.out.println("返回值是void类型"); }
接收servlet经常使用对象:
@ResponseStatus(HttpStatus.OK) @RequestMapping("show19") //直接写参数类型便可,springMVC自动传递参数,不考虑参数顺序 public void test19(Model model, HttpServletRequest request, HttpServletResponse response, HttpSession session){ System.out.println(request); System.out.println(response); System.out.println(session); }
接收普通参数:
@RequestParam(value="", required=true/false, defaultValue="")
@RequestMapping("show20") public String test20(Model model,@RequestParam(value = "username",required = true,defaultValue = "lisi") String username){ model.addAttribute("msg",username); return "index"; }
获取Cookie:
HttpServletRequest request
,使用request.getCookies()
方法@CookieValue
注解//@CookieValue : 使用方式和@RequestParam一致,须要注意,value值就是cookie中的名字,区分大小写。 @RequestMapping("show22") public String test22(Model model,@CookieValue("JSESSIONID")String sessionId){ model.addAttribute("msg",sessionId); return "index"; }
直接在参数中添加POJO类型能够实现前端数据直接绑定为POJO对象。
集合绑定:
集合属性为简单类型:
当前端同一个name有多个值时能够直接使用List来接收:@RequestParam("chenkBoxName") List<String> box
集合属性为POJO类型:
表单的name值须要是集合属性名[索引].Pojo属性名
,好比users[0].age
,表明第一个表单的age属性。
后端这么获取:
public class Users { private List<User> userList; public List<User> getUserList() { return userList; } public void setUserList(List<User> userList) { this.userList = userList; } } @RequestMapping("show26") public String test26(Model model, Users users){ model.addAttribute("msg",userVo); return "hello"; }
哦,上帝,这种先后端交互这太蠢了。
Json才是王道
首先引入依赖:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.9</version> </dependency>
注意须要在SpringMVC.xml文件中添加注解驱动(为何???):
<mvc:annotation-driven></mvc:annotation-driven>
这样就能够返回Json类型数据:
@ResponseBody//响应json数据 @RequestMapping("show28") public List<User> test28(){ List<User> userList = new ArrayList<>(); User u1 = new User(); u1.setName("张三1"); u1.setAge(23); u1.setIncome(10000); u1.setIsMarry(true); User u2 = new User(); u2.setName("张三2"); u2.setAge(24); u2.setIncome(10002); u2.setIsMarry(false); userList.add(u1); userList.add(u2); return userList; }
接收JSON为POJO对象:
直接在参数里添加@RequestBody
修饰的POJO对象便可:
@RequestMapping("show29") public String test29(Model model,@RequestBody User user){ }
若是出现乱码现象:
即springMVC.xml中配置:
<mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg value="utf-8"></constructor-arg> </bean> </mvc:message-converters> </mvc:annotation-driven>
@RestController
有时若是在一个Contoller中全部的方法都是用来响应json格式数据的,那么若是有多个方法,就须要在多个方法上使用@ResponseBody,这样太麻烦,springmvc提供了一个@RestController,将该注解使用在Controller类上,那么该controller中的全部方法都默认是响应json格式的数据了
文件上传
Spring有两个web相关的包,spring-webmvc :这个里面存放的是springmvc的核心功能。spring-web:这里面存放的是web相关的功能,例如监听器,文件上传等,而文件上传依赖apache的commons-fileupload依赖。
首先导入依赖:
<!-- 文件上传依赖的包,apache的 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
建立控制器:
/** * @RequestParam: 注意此注解的值,须要和表单提交的名字一致 * @param model * @param file 接受上传的文件 * @return * @throws Exception */ @RequestMapping("show34") public String test34(Model model, @RequestParam("file") MultipartFile file) throws Exception{ if(file!=null){ file.transferTo(new File("f://download/"+file.getOriginalFilename())); } model.addAttribute("msg","上传成功"); return "hello"; }
前端HTML中建立表单:
<!-- 这里提交方式必须是post,而且须要有 enctype="multipart/form-data" 这一属性 --> <form action="http://localhost:8080/hello/show34.do" method="post" enctype="multipart/form-data"> <input type="file" name="files"> <input type="submit" value="上传"> </form>
在spring配置文件中添加文件解析器:
<!-- 配置文件上传解析器,注意,此处id必须为 multipartResolver --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 配置文件上传的最大值,这里是5M --> <property name="maxUploadSize" value="5242880"></property> <!-- 配置上传文件的名字编码格式 --> <property name="defaultEncoding" value="utf-8"></property> </bean>
上传多个文件:
首先在前端HTML的input标签中添加multiple
属性:
<!-- 添加multiple属性以支持多个文件 --> <input type="file" multiple name="files">
后端控制器须要修改MultipartFile参数为数组:
@RequestMapping("show35") public String files(Model model, @RequestParam("files") MultipartFile[] files)throws Exception { for (MultipartFile file : files) { if (file != null) { file.transferTo(new File("D:/dir/B/"+file.getOriginalFilename())); } } model.addAttribute("msg", "成功"); return "index"; }
此时即可支持多个文件上传。
返回值为字符串时,默认为视图名称。当返回值字符串是以”forward:”或者”redirect:”开头,则会被认为是转发或者重定向。 方式以下: 转发:forward:/hello/show.do(绝对路径)或者forward:show.do(相对路径) 重定向:redirect:/hello/show.do(绝对路径)或者redirect:show.do(相对路径) /:表示绝对路径,指的是localhost:8080/springmvc(项目名称能够省略) 不带/:表示相对路径,相对于当前请求的路径 若是当前请求是:localhost:8080/springmvc(项目名称能够省略)/hello/show32 那么不带/:表示localhost:8080/springmvc(项目名称能够省略)/hello/
拦截器的执行过程:
自定义拦截器:
建立类并实现HandlerInterceptor,而后注册到spring中便可。
拦截器类:
public class MyInterceptor1 implements HandlerInterceptor { /** * 在handler方法执行以前执行, * * @return true,拦截器放行,返回false,拦截器不放行,后续业务逻辑进行处理。 * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("myInterceptor1,预处理方法执行执行"); return true; } /** * handler执行以后执行。 * @throws Exception */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("myInterceptor1,后置处理方法执行执行"); } /** * 在视图渲染以后执行 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("MyInterceptor1,请求完成回调方法正在执行"); } }
而后在spring中注册拦截器:
<!-- /: 表示绝对路径: http://localhost:8080/springmvc /*:表示绝对路径下的任意一级路径: http://localhost:8080/springmvc/xxx /**:表示绝对路径下的任意多级目录: http://localhost:8080/springmvc/xxx http://localhost:8080/springmvc/xxx/xxx/xxx --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.bilibili.interceptor.MyInterceptor1"></bean> </mvc:interceptor> </mvc:interceptors>
拦截器的执行顺序为注册的顺序
此时只需在web.xml中配置spring过滤器便可便可:
<filter> <filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
这种方式若是文件太大,会致使浏览器接收不到服务器返回的ModelAndView,文件小一点的话却能够
整体流程就是建立一个类实现HandlerExceptionResolver接口,而后在在spring中注册便可:
public class MyException implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ModelAndView mv = new ModelAndView(); if (ex instanceof MaxUploadSizeExceededException) { System.out.println("error"); mv.setViewName("error"); mv.addObject("msg", "文件过大"); } return mv; } }
而后在Spring容器中注册便可
附:JSP与先后端分离
之前老的方式是:
新的方式是:
附:URL中的字符
URL的特殊字符 当几种特定的字符集合出如今URL中时,你必须特别注意:
首先,在URL中有特殊意义的字符,也就是保留字符:
;
、/
、?
、:
、@
、&
、=
、+
、$
、,
,10个,这意味着,这些字符一般在URL中使用时,是有特殊含义的(如 ":"把每个部分分隔开来), 若是一个URL的某一部分(如查询参数的一部分)可能包含这些字符之一,则应该在放入URL以前 对其进行转义处理.
第二组须要注意的字符集是非保留字符集.以下: -
、_
、.
、!
、~
、*
、'
、(
、)
,9个,这些字符能够被用于URL的任何位置(有些地方,不容许它们出现). 使用它们做为URL的一部分时,你不须要进行编码/转义处理.你能够对它们进行转义操做且不影响URL 的语义,但不建议这么作.
第三组 不推荐字符 也就是避用字符集合使用它们是不明智的: {
、}
、|
、\
、^
、[
、]
、`(数字1键前),8个,不明智的缘由:网关有时会修改这样的字符,或者将其做为分隔符使用.这并不意味着网关总会修改这些字符,但这种状况可能发生.若是真是要使用这些字符,请作转义处理.
第四组,例外字符集,这组字符集是全部的ASCII控制字符组成.包含空格字符如下列字符:<
、>
、#
、%
、"
、` ,6个,控制字符是不可打印的US-ASCII字符(十六进制00~1F及7F)若是使用,请转义处理.有些字符#(哈希)和%(百分比)在URL上下文中有着特殊含义,你能够把它们看成保留字符对待.这个集合中的其它字符没法被打印,所以对它们进行转义是惟一的表示方式,
<、
>、
"`、这三个字符须要被转义,由于这些字符一般用来在文本中分隔URL。
参考知乎
写的有点乱,不过这只是做为学习笔记而已。┓( ´∀` )┏