本文章的架构如下图:
1.springMVC概述
MVC设计模式是一种通用的软件编程思想
在MVC设计模式中认为, 任何软件都可以分为三部分组成:
(1)控制程序流转的控制器(Controller)
(2)封装数据处理数据的模型(Model)
(3)负责展示数据的视图(view)
并且在MVC设计思想中要求一个符合MVC设计思想的软件应该保证上面这三部分相互独立,互不干扰,每一个部分只负责自己擅长的部分。
如果某一个模块发生变化,应该尽量做到不影响其他两个模块。这样做的好处是,软件的结构会变得更加的清晰,可读性强。有利于后期的扩展和维护,并且代码可以实现复用。
2.springMVC的产生及其作用
2.1 servlet的缺点
1、通常情况下,一个Servlet类只负责处理一个请求,若项目中有成百上千个请求需要处理,就需要有成百上千个Servlet类,这样会使得项目中Servlet类的个数暴增;
2、在Servlet3.0版本之前,每一个Servlet都需要在web.xml文件中至少做八行配置信息,配置内容多且繁琐。当Servlet特别多时,web.xml配置量太多,不利于团队开发;
3、当通过客户端提交参数到服务器,通过Servlet进行接收时,无论数据本身是什么格式,在Servlet中一律按照字符串进行接收,后期需要进行类型转换,复杂类型还需要特殊处理,特别麻烦!
4、servlet具有容器依赖性,必须放在服务器中运行,不利于单元测试;
2.2 springMVC的执行原理
Springmvc是spring框架的一个模块,spring和springmvc无需中间整合层整合Springmvc是一个基于mvc的web框架.
具体原理图如下:
(1) 用户发送请求 至 前端控制器(DispatcherServlet);
提示:DispatcherServlet的作用:接收请求,调用其它组件处理请求,响应结果,相当于转发器、中央处理器,是整个流程控制的中心
(2) 前端控制器(DispatcherServlet)收到请求后调用处理器映射器(HandlerMapping)处理器映射器(HandlerMapping)找到具体的Controller(可以根据xml配置、注解进行查找),并将Controller返回给DispatcherServlet;
(3) 前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)。处理器适配器经过适配调用具体的Controller;(Controller–>service --> Dao --> 数据库) Controller执行完成后返回ModelAndView,
提示:Model(模型数据,即Controller处理的结果,Map) View(逻辑视图名,即负责展示结果的JSP页面的名字)
处理器适配器(HandlerAdapter)将controller执行的结果(ModelAndView)返回给前端控制器(DispatcherServlet);
(4).前端控制器(DispatcherServlet)将执行的结果(ModelAndView)传给视图解析器(ViewReslover)
视图解析器(ViewReslover)根据View(逻辑视图名)解析后返回具体JSP页面
(5).前端控制器(DispatcherServlet)根据Model对View进行渲染(即将模型数据填充至视图中);
前端控制器(DispatcherServlet)将填充了数据的网页响应给用户。
其中整个过程中需要开发人员编写的部分有 Controller、Service、Dao、View;
3.springmvc快速入门
3.1 导入依赖
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
3.2 编写配置文件(web.xml),注入dispacherServlet对象.
因为jave 的web项目的入口是web.xml,并且servlet注册也是在web.xml中,所以我们需要在web.xml中注册SpringMVC的dispactherServlet对象.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<!-- 配置springmvc前端控制器, 将所有请求交给springmvc来处理 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置springmvc核心配置文件的位置,默认Springmvc的配置文件是在WEB-INF目录下,默认的名字为springmvc-servlet.xml,如果要放在其他目录,则需要指定如下配置:
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-config.xml</param-value>
</init-param>
</servlet>
<!-- 其中的斜杠(/)表示拦截所有请求(除JSP以外), 所有请求都要经过springmvc前端控制器 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
3.3 配置springMVC的配置文件
<?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: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-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 1.配置前端控制器放行静态资源(html/css/js等,否则静态资源将无法访问) -->
<mvc:default-servlet-handler/>
<!-- 2.配置注解驱动,用于识别注解(比如@Controller) -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 3.配置需要扫描的包:spring自动去扫描 base-package 下的类,
如果扫描到的类上有 @Controller、@Service、@Component等注解,
将会自动将类注册为bean
-->
<context:component-scan base-package="com.tedu.controller">
</context:component-scan>
<!-- 4.配置内部资源视图解析器
prefix:配置路径前缀
suffix:配置文件后缀
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
3.4创建并实现HelloController类
@Controller /* 这个注解表示当前类是属于控制层 */
public class HelloController {
/* http://localhost/项目名称/hello */
@RequestMapping("/hello/{age}")
/* 这个注解用于:映射请求的资源路径(/hello)和当前方法(hello)的对应关系
* 当浏览器请求 /hello 路径时, 将会访问(执行)当前这个方法 */
public String hello(@Param String name,@Pathvariable Integer age) {
System.out.println("hello springmvc...");
return "home";
}
}
4.SpringMVC编写Controller层的注意事项
4.1 请求方式
(1)get请求
@GetMapping(“url”)
或者
@RequestMapping(method = RequestMethod.GET,value = “url”)
(2)post请求
@PostMapping(“url”)
或者
@RequestMapping(method = RequestMethod.POST,value = “url”)
(3) delete请求
@DeleteMapping(“url”)
或者
@RequestMapping(method = RequestMethod.DELETE,value = “url”)
(4) 修改请求
@PutMapping(“url”)
或者
@RequestMapping(method = RequestMethod.PUT,value = “url”)
代码如下:
@GetMapping
@PostMapping
@DeleteMapping
@PutMapping
@RequestMapping(method = RequestMethod.POST,value = "{module}/{moduleUI}")
public String doModuleUI(
@PathVariable String moduleUI) {
return "sys/"+moduleUI;
}
4.2参数映射
(1)解析前台传过来的参数
@RequestParam和@RequestBody
@RequestParam(“参数名”)
用来处理Content-Type: 为
application/x-www-form-urlencoded编码的内容。(Http协议中,如果不指定Content-Type,则默认传递的参数就是application/x-www-form-urlencoded类型)
@GetMapping("doFindObjectById")
public JsonResult doFindObjectById(@RequestParam("id") Integer id) {
return new JsonResult(sysUserService.findObjectById(id));
}
@RequestBody(“参数名”)
处理HttpEntity传递过来的数据,一般用来处理非Content-Type:
application/x-www-form-urlencoded编码格式的数据。GET请求中,因为没有HttpEntity,所以@RequestBody并不适用。
POST请求中,通过HttpEntity传递的参数,必须要在请求头中声明数据的类型Content-Type,SpringMVC通过使用HandlerAdapter
配置的HttpMessageConverters来解析HttpEntity中的数据,然后绑定到相应的bean上。
@PostMapping("doUpdateObjectById")
public JsonResult doFindObjectById(@RequestBody("id") Integer id) {
return new JsonResult(sysUserService.findObjectById(id));
}
总结:
在GET请求中,不能使用@RequestBody。
在POST请求,可以使用@RequestBody和@RequestParam,但是如果使用@RequestBody,对于参数转化的配置必须统一。
注意:
@RequestBody和@RequestParam注解修饰的属性,如果没有传递是会报错的,因为如果没设置,默认是必须传递.
设置可以不传递
//@RequestParam表示参数名为id,可以不传递本参数
@GetMapping("doFindObjectById")
public JsonResult doFindObjectById(@RequestParam(value = "id",required = false) Integer id) {
return new JsonResult(sysUserService.findObjectById(id));
}
(2)url中的参数
注解:@PathVariable 修饰
从url路劲中获取参数
@RequestMapping("{module}/{moduleUI}")
public String doModuleUI(
@PathVariable String moduleUI) {
return "sys/"+moduleUI;
}
4.3 请求转发与重定向
(1) 请求转发
/* 测试请求转发(forward) */
@RequestMapping("testForward")
public String testForward(){
System.out.println("测试请求转发(forward)...");
return "forward:hello";//转发到路劲 hello下
}
(2) 重定向
/* 测试请求重定向(redirect) */
@RequestMapping("testRedirect")
public String testRedirect(){
System.out.println("测试请求重定向(redirect)...");
return "redirect:hello"; //重定向到 hello路劲下
}
4.4 响应
分类
1.响应页面
1.1 控制层的类上只标注@Controller类,没有标@ResponseBody;
1.2 方法是也没有@ResponseBody注解
@Controller
public class DoorController {
2.响应json数据
方式1:类上标注@ResponseBody注解.
@Controller
@ResponseBody
public class DoorController {
方式2:类上标注@RestController;
这个注解等于@Controller + @ResponseBody ;并且表示本类的所有方法都响应的是json数据
//等于 @Controller + @ResponseBody
@RestController
public class DoorController {
方式3:方法上标注注解@ResponseBody
@Controller
public class DoorController {
@Autowired
private DoorMapper doorMapper;
//方法上并没有标注@ResponseBody注解,且类上也没有表示本方法响应页面
@RequestMapping("/doorList") //响应页面返回值为String
public String toDoorlist(Model model) {
List<Door> doorList = doorMapper.findAll();
model.addAttribute("doorList", doorList);
//他会到我们在spring-mvc.xml中拿取,前缀和后缀,拼接起来,去找相应的页面
return "door_list";
}
4.5 乱码处理
springmvc也提供了解决请求参数乱码的方案,就是在web.xml中加入如下代码,可以解决POST提交参数乱码:
添加拦截器:
<!-- 乱码处理过滤器 -->
<filter>
<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>
4.6 异常处理
这是SpringMVC的异常处理,也就是Controller层的异常处理,如果出现异常怎么处理.
(1)局部异常
局部异常是指的定义在Controller类中的异常处理方式,只能指定本类的异常处理方式.
例如代码:
//局部异常处理
@ExceptionHandler(RuntimeException.class)
@ResponseBody
public JsonResult doHandleRuntimeException(RuntimeException e) {
JsonResult jsonResult = new JsonResult(e);
return jsonResult;
}
说明:
使用注解 @ExceptionHandler(RuntimeException.class)表示捕获RuntimeException类型的异常,如果本类执行过程中发生了这个类型的异常,则走本代码.
(2) 全局异常
全局异常控制所有的Controller收到或者发生的异常,这里有个规则,如果该类有局部异常,则走局部异常,没有局部异常则走全局异常.
具体代码:
/**
* @ControllerAdvice 描述的类表示一个全局异常处理类
* @ExceptionHandler 描述的方法为一个异常处理方法
* 全球异常处理类
*/
//@ControllerAdvice //这个是标注此类为处理controller层的全局异常类
//@ResponseBody
@RestControllerAdvice//==@ControllerAdvice+@ResponseBody
public class GlobalExceptionHandler {
@ExceptionHandler(RuntimeException.class)
//@ResponseBody
public JsonResult doHandleRuntimeException(
RuntimeException e) {
e.printStackTrace();
return new JsonResult(e);
}
@ExceptionHandler(ShiroException.class)
@ResponseBody
public JsonResult doHandleShiroException(
ShiroException e) {
JsonResult r=new JsonResult();
r.setState(0);
if(e instanceof UnknownAccountException) {
r.setMessage("账户不存在");
}else if(e instanceof LockedAccountException) {
r.setMessage("账户已被禁用");
}else if(e instanceof IncorrectCredentialsException) {
r.setMessage("密码不正确");
}else if(e instanceof AuthorizationException) {
r.setMessage("没有此操作权限");
}else {
r.setMessage("系统维护中");
}
e.printStackTrace();
return r;
}
}
5.restful风格
5.1 前端发起请求
前端发起增删改查的请求,并且使用不同传输方式,则有两种方式可以实现不同的增删改查的请求方式.
(1) 是ajax请求类型get/post/put/delete
$.ajax({
url: "${pageContext.request.contextPath}/emp/" + id,
type: "PUT",//请求方式
data: $("#app form").serialize(),
success: function (result) {
alert("操作成功!");
}
});
(2) 通过form表单将post请求转化成相应的请求方式
form action="http://localhost:8099/post" method="post">
<input type="text" name="username">
<br>
<input type="text" name="age">
<br>
<input type="submit" value="post提交">
</form>
<hr>
<form action="http://localhost:8099/put" method="post">
<input name="username" type="text">
<br>
<input type="text" name="age">
<br>
<input type="hidden" name="_method" value="put">
<input type="submit" value="put提交">
</form>
<hr>
<form action="http://localhost:8099/delete" method="post">
<input name="id" type="text">
<br>
<input type="hidden" name="_method" value="delete">
<input type="submit" value="delete提交">
</form>
注意:
1.form表单发起请求;
2.表单的属性method = “post”;
3.form表单中一定要有一个input标签,可以用delete/put/post等
5.2 后端的controller
如果后端请求想要接受到前端的rest风格的请求,根据前台发起请求的方式有两个步骤:
- 如果前台是ajax的形式发起请求,此步骤可省略.以表单的形式发起请求,则:
(1) 如果后台是普通的web项目则需要在web.xml中配置拦截器
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(2) 如果是spring boot项目,则springboot默认配置了一个类HiddenHttpMethodFilter .
该类的码如下:
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
private static final List<String> ALLOWED_METHODS;
public static final String DEFAULT_METHOD_PARAM = "_method";
private String methodParam = "_method";
2.配置具体的Controller映射
(1)get请求
@GetMapping(“url”)
或者
@RequestMapping(method = RequestMethod.GET,value = “url”)
(2)post请求
@PostMapping(“url”)
或者
@RequestMapping(method = RequestMethod.POST,value = “url”)
(3) delete请求
@DeleteMapping(“url”)
或者
@RequestMapping(method = RequestMethod.DELETE,value = “url”)
(4) 修改请求
@PutMapping(“url”)
或者
@RequestMapping(method = RequestMethod.PUT,value = “url”)
代码如下:
@GetMapping("/get")
public String getMethod(Integer id){
return "get方法成功!!"+ id;
}
@PostMapping("/post")
public String postMethod(String username,Integer age){
return "post方法成功!!"+ username + age;
}
@PutMapping("/put")
public String putMethod(String username,Integer age){
return "put方法成功!!"+ username + age;
}
@DeleteMapping("/delete")
public String deleteMethod(Integer id){
return "delete方法成功!!"+ id;
}