SpringMVC第一部分
MVC( Model-View-Controller )是一种程序设计思想,通过分离模型、视图和控制器在应用中的角色实现解耦,MVC允许各个组分单独改变而不会相互影响
环境搭建
配置流程
配置依赖项与构建项pom.xml
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.4.RELEASE</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>9.4.27.v20200227</version> <configuration> <scanIntervalSeconds>10</scanIntervalSeconds> <httpConnector> <port>8080</port> </httpConnector> <webAppConfig> <contextPath>/path</contextPath> </webAppConfig> </configuration> </plugin> </plugins> </build>
|
配置web.xml
<web-app id="webApp_ID" version="3.0" 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">
<filter> <description>char encoding filter</description> <filter-name>encodingFilter</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>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>springMvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:servlet-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMvc</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
|
servlet请求分发由servlet-context.xml配置,接下来配置该文件
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.xxxx.springmvc.controller"/> <mvc:default-servlet-handler/> <mvc:annotation-driven/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean>
</beans>
|
简单使用
编写页面控制器
@Controller public class HelloController {
@RequestMapping("/hello") public ModelAndView hello() { ModelAndView mv = new ModelAndView(); mv.addObject("hello", "hello spring mvc"); mv.setViewName("hello"); return mv; } }
|
在WEB-INF目录下新建jsp文件夹,新建hello.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML> <html> <head> <base href="<%=basePath %>"> <title>My JSP 'hello.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"> </head> <body> <!-- EL表达式接收参数值 --> ${hello} </body> </html>
|
启动jetty服务器即可
URL地址映射
通过注解@RequestMapping 将请求地址与方法进行绑定,可以在类级别和方法级别声明。类级别的注解负责将一个特定的请求路径映射到一个控制器上,将url和类绑定;通过方法级别的注解可以细化映射,能够将一个特定的请求路径映射到某个具体的方法上,将url和类的方法绑定
映射单个URL
使用@RequestMapping("")或指定value属性
@RequestMapping("/test01")
public ModelAndView test01() { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("hello", "test01"); modelAndView.setViewName("hello"); return modelAndView; }
|
映射多个URL
使用@RequestMapping("","")或指定value为数组类型即可
@RequestMapping({"/test03_01", "/test03_02"})
public ModelAndView test03() { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("hello", "test03"); modelAndView.setViewName("hello"); return modelAndView; }
|
映射URL在控制器上
提供了一种层级化的访问方式,需要先访问类层级路径才能访问方法层级
@Controller @RequestMapping("/url") public class UrlController { @RequestMapping("/test04") public ModelAndView test04() { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("hello", "test04"); modelAndView.setViewName("hello"); return modelAndView; } }
|
此时要访问到test04()方法需要访问/url/test04才可
设置URL映射请求格式
可以通过method属性设置支持的请求方式,如method=RequestMethod.POST如设置多种请求方式,以大括号包围,逗号隔开即可
@RequestMapping(value = "/test05", method = RequestMethod.POST) public ModelAndView test05() { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("hello", "test05"); modelAndView.setViewName("hello"); return modelAndView; }
|
通过参数名称映射URL
设置params属性,该方法要求客户端通过特定的参数去访问
@RequestMapping(params = "test06") public ModelAndView test06() { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("hello", "test06"); modelAndView.setViewName("hello"); return modelAndView; }
|
参数绑定
用于将后端使用的参数与前端传入的参数进行绑定,使用@RequestParam注解放在形参前表示绑定
该注解的name属性可与前端的的参数名进行绑定,defaultValue属性用于设置默认值,required用于明确该参数是否必须,请求中若没有该参数会向客户端抛出400错误
基本类型
基本类型传递时如果为空,可能会产生空指针异常
public void test(@RequestParam(name="age", defaultValue=0) int num) {}
|
包装类型
使用包装类型的优点在于其默认值为null,此时可以不设置defaultValue属性
public void test(Integer num) {}
|
可以不使用注解,但还是感觉用了会使代码逻辑更清晰
public void test(@RequestParam(name="age", defaultValue=0) Integer num) {}
|
数组类型
public void test(@RequestParam(name="age") Integer[] num) {}
|
适用于客户端传递数组参数,类似age=12 &age=18 &age=22
JavaBean类型
public void test(User user) {}
|
bean类型参数不适用@RequestParam注解,客户端传递该对象的属性参数,若该User类有id和role两个属性,客户端传入类似id=1 &role=admin
集合类型
对于集合类型,需要先传入一个JavaBean类型,后端再通过JavaBean对象中的属性进行接收
List列表
若传递元素为基本类型,在bean中设置集合属性
public class Test { private List<Integer> list;
public List<Integer> getList() { return list; }
public void setList(List<Integer> list) { this.list = list; } }
|
接收test对象
public void test(Test test) {}
|
客户端发送的参数需为list=1 &list=2 &list=3
若传递元素为JavaBean类型,在bean中设置集合属性
public class Test { private List<User> list;
public List<User> getList() { return list; }
public void setList(List<User> list) { this.list = list; } }
|
客户端发送的请求则应为list[0].id=1 &list[0].role=admin &list[1].id=2 &list[1].role=user
Set集合
绑定Set数据时,必须先在Set对象中添加相应数量的模型对象
public class Test { private Set<User> set = new HashSet<User>(); public Test() { set.add(new User()); set.add(new User()); set.add(new User()); }
public Set<User> getList() { return set; }
public void setList(Set<User> set) { this.Set = set } }
|
Map集合
设置bean载体
public class test { private Map<String,User> map =new HashMap<>();
public Map<String, User> getMap() { return map; }
public void setMap(Map<String, User> map) { this.map = map; } }
|
客户端发送的格式类似map['a'].id=1 &map['a'].role=admin &map['b'].id=2 &map['b'].role=user
请求域对象
ModelAndView
在先前的测试中
@Controller public class HelloController {
@RequestMapping("/hello") public ModelAndView hello() { ModelAndView mv = new ModelAndView(); mv.addObject("hello", "hello spring mvc"); mv.setViewName("hello"); return mv; } }
|
这里的mv就是一个请求域对象,它是要渲染的内容的载体,而返回值用于明确要渲染的模板。由于ModelAndView类可以用setViewName()方法指定要渲染的模板,所以可以直接返回对象,而除此之外还有许多方式可以达到一样的效果
ModelMap
ModelMap类使用addAttribute()方法设置jsp中的引用名attributeName和渲染内容attributeValue
@RequestMapping("/hello") public String hello(ModelMap modelMap) { modelMap.addAttribute("hello","Model"); return "hello"; }
|
Model
Model类的使用方法与ModelMap几乎一致
@RequestMapping("/hello") public String hello(Model model) { model.addAttribute("hello","ModelMap"); return "hello"; }
|
Map
由于map没有addAttribute()方法,但put()方法也可以实现一样的功能
@RequestMapping("/hello") public String hello(Map map) { Map.put("hello","Map"); return "hello"; }
|
HttpServletRequest
@RequestMapping("/hello") public String hello(HttpServletRequest request) { request.setAttribute("hello", "HttpServletRequest"); return "hello"; }
|
其实以上方法究其根本还是把属性放入了一次请求HttpServletRequest的 作用域
重定向与请求转发
重定向
返回值以redirect:开头会重定向到相应的jsp页面
@RequestMapping("/hello") public String hello(HttpServletRequest request) { return "redirect:halo"; }
|
这样会重定向到halo这个Controller,此时会再次建立一个HttpServletRequest,也可以传递参数比如redirect:halo?test=1
也可以通过RedirectAttributes类设置重定向的传递参数,该方法可以解决传参含中文乱码的问题
@RequestMapping("/hello") public String hello(RedirectAttributes redirectAttributes) { redirectAttributes.addAttribute("测试", "test"); return "redirect:halo"; }
|
ModelAndView类也可以实现
@RequestMapping("/hello") public String hello(ModelAndView modelAndView) { modelAndView.addObject("测试", "test"); modelAndView.setViewName("redirect:halo"); return modelAndView; }
|
请求转发
请求转发直接调用跳转的页面,此时不会销毁前一次建立的HttpServletRequest,不存在丢失数据的问题,请求转发使用forward:前缀,也可以不使用,spring默认使用请求转发
@RequestMapping("/hello") public String hello(HttpServletRequest request) { return "forward:halo"; }
|
请求转发也可以传递参数,并且此时可以直接传中文
JSON
JSON作为一种数据传输格式在实际开发中获得了良好的应用,可以利用其直接返回对象、集合等类型
@ResponseBody注解
该注解用于将 Controller 的方法返回的对象,通过适当的 HttpMessageConverter 转换为指定格式后,写入到 Response 对象的 body 数据区,返回的数据不是 html 标签的页面,而是其他某种格式的数据时(如 json、xml 等)使用(通常用于ajax 请求)
@RequestBody注解
该注解用于读取 Request 请求的 body 部分数据,使用系统默认配置的 HttpMessageConverter 进行解析,然后把相应的数据绑定到要返回的对象上,再把 HttpMessageConverter 返回的对象数据绑定到 controller 中方法的参数上
配置
添加依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.10.0</version> </dependency>
|
设置servlet-context.xml
<mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter" /> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" /> </mvc:message-converters> </mvc:annotation-driven>
|
使用
@ResponseBody注解的使用
可以将该注解标记在方法上
@RequestMapping("/test") @ResponseBody public User Test() { User user = new User(); user.setId(1); user.setName("admin"); return user; }
|
或方法签名的访问修饰符右侧
@RequestMapping("/test") public @ResponseBody User Test() { User user = new User(); user.setId(1); user.setName("admin"); return user; }
|
此时访问/test路径会返回格式化的JSON文本,若没有引入该注解,该页面无法正常访问
通过Ajax接收JSON数据
前面的例子中,我们实现了后端返回JSON格式数据,但此时是直接打印在页面上的,想要前端接收数据并实现页面渲染需要使用一个接收器Ajax去接收JSON数据,要使用Ajax需要下载其js文件并在jsp文件中进行引入
ajax调用函数
function testAjax(){ $.ajax({ type:"POST", url:"test", data:{ "testParam":"testValue" }, success: function(data){ console.log(data); } }); }
|
后端响应方法
@RequestMapping("/test") @ResponseBody public User testRes() { User user = new User(); user.setId(1); user.setUserName("admin"); return user; }
|
调用testAjax()方法后,则会在浏览器控制台打印{id:1,userName:"admin"}
@RequestBody注解的使用
该注解用来处理content-type为application/json格式的字符串
可以将该注解标记在方法上
@RequestBody public Void Test(User user) { System.out.println(user); }
|
或者标记在形参左侧
public Void Test(@RequestBody User user) { System.out.println(user); }
|
前端使用Ajax发送JSON数据
function testAjax(){ $.ajax({ type:"POST", url:"test", contentType:"application/json", data:'{"id":1,"userName":"admin"}', success: function(data){ console.log(data); } }); }
|