SpringMvc模式
SpringMvc基础----
如果你还不会spring请移步以下链接
Spring 基础入门教程
如果你还不会mybatis,请移步,请移步以以下链接
j假如你是大神,请悄悄看完,就当是复习了,如果文章中恰巧有什么跟你的认知有冲突的地方,还请指教!
SpringMvc的优势:
Spring MVC是Spring提供的一个轻量级Web框架,它实现了我们在tomcat时的WebMVC设计模式。
Spring MVC在使用和性能等方面要比较优秀
他灵活性强,易于与其他框架集成,同时xml配置文件的修改不需要重新编译应用和层序;
所以spring的基本框架在为微服务中很 流行;
要注意一点在环境搭建的时候
Spring MVC应用
在开始之前我们假定你已经有了Spring与mybatis基础,并且能理解tomcat的工作模式;
在具体的代码实现之前我们要先了解一下springmvc框架的执行流程,就像我们学习tomcat一样;
我们之前学习过MVC模式,
MVC模式大体上是这个样子的------>>>
MVC模式
一个简单的表格是这么创建的:
层 | 含义 |
---|---|
M 即model层 | Dao层的封装,像我们的数据库,我们将数据库中的表封装为一个实体类,然后通过Dao层去调用Service层(当然如果项目很小的话可以不用这个service层); |
V 即view层 | 用于前端展示的想什么html,jsp,php等 |
C 即Controller层 | 即servlet封装,用于业务逻辑的处理 |
SpringMvc模式即通过Spring将MVC及进行整合—
层 | 含义 |
---|---|
M层 | mybatis |
V层 | html,jsp,php |
C层 | SpringMVC |
SpringMVC是spring为展现层提供的基于MVC设计理念的优秀WEB框架,是目前最主流的MVC框架之一
SpringMVC通过一套注解,可以让普通的JAVA类成为contrllor控制器,无需继承Servlet,实现了控制层和Servlet之间的解耦
SpringMVC支持Rest风格的URL写法,可以增强url的安全性
SpringMVC采用了松耦合,可热插的主键结构,比其他的框架更具扩展性和灵活性
创建项目
项目的目录结构
注意,由于创建时找的一个maven-webapp股价创建的,发现当前的web.xml配置有一些过时,
过时的web.xml配置------------>>>
web–2.0
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
如果用比较新的jar包来运行,可能会有一些不兼容的问题
这里建议修改为新的配置------>>>
web–4.0
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
</web-app>
基础JAR包引入
这一步凭借maven可以快完成jar包依赖的导入;
回顾一下spring的核心jar包----->>
core ,beans,context ,expression
然后引入sprinmvc核心jar包
spring-web和spring-webmvc
如果要打印日志还需要 log的jar包
<!-- SPRING -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.2.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.18.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.18.RELEASE</version>
</dependency>
编写一个控制层---->>
在这里回顾一下传统的控制层该怎么写----->>
然后请求转发到welcome.jsp中,如果之前需要做处理还需要配置过滤器等等
在springmvc模式下该怎么写呢???
实现Springmvci相关jar包中的Controller接口---->>>
import org.springframework.web.servlet.mvc.Controller;
public class ControllerTest implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView modelAndView= new ModelAndView();
modelAndView.addObject("msg","Welcome to Yantai!");
modelAndView.setViewName("welcome.jsp");
return modelAndView;
}
}
可以看到在控制层中将model层和view层结合到一块,
控制层出处理请求,并向指定地址返回一个modelAndView对象,这样请求就会被转到发到welcome.jsp;
你可能会发现Sringmvc模式下与servlet的一些小区别----
控制层怎么定位到?
本质上都要定位到控制层,只不过这次是通过spring与web来完成控制层的定位
在resource下新建一个配置文件---->>>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置控制层实例-->
<bean name="/controllerTest" class="com.gavin.Controller.ControllerTest"/>
<!-- 处理url-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--控制适配器-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--视图层处理-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
</beans>
然后在web.xml中配置如下内容----->>>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<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:springmvc-config.xml</param-value>
</init-param>
<!-- 启动时加载-->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置映射-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- 过滤路径 拦截所有url并交由DispatcherServlet处理-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
运行—>>
在老版本的Spring中,配置文件内必须要配置处理器映射器、处理器适配器和视图解析器。但在Spring 4.0以后,如果不配置处理器映射器、处理器适配器和视图解析器,就会使用Spring内部默认的配置来完成相应的工作;
SpringMvc的工作流程----->>
在springmvc的流程中我们不需要关心DispatcherServlet、HandlerMapping、HandlerAdapter和ViewResolver对象是怎么工作的,我们只需要配置好前端控制器完成业务逻辑就行了;
注:实际上在spingmvc配置文件中只需要配置
控制层的bean就可以以了;
spingmvc配置文件
在spring2.5之前的版本中只能通过配置xml文件来完成控制层的定位,但是在这之后可以通过注解的方式来实现定位;
通过注解的方式完成控制层的定位
注解实现控制层
@Controller注解
import org.springframework.stereotype.Controller;
@Controller
public class ControllerTest {
}
这样通过注解的方式找到控制器后,还需要知道控制器内部对每个请求是如何 处理的;
@RequestMapping
@Controller
public class ControllerTest {
// @RequestMapping 当标注在一个方法上时,该方法将成为一个请求处理方法,
// 它会在程序接收到对应的URL请求时被调用。
@RequestMapping(value = "/controllerTest")
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", "你好Plus");
modelAndView.setViewName("welcome.jsp");
return modelAndView;
}
}
xml与注解对比-----<>
首先看一下逻辑----->>
基本逻辑一样
除了 @RequestMapping 标注在Controller的方法上,还可以标注在类上,
这表示该类下所有的方法都为请求映射方法,该类中的所有方法都将映射为相对于类级别的请求,即所有的请求都被映射到value属性所指定的的路径下;
定义多个处理方法
@Controller
@RequestMapping(value ="/controll")
public class ControllerTest {
// @RequestMapping 当标注在一个方法上时,该方法将成为一个请求处理方法,
// 它会在程序接收到对应的URL请求时被调用。
@RequestMapping(value="/controllerTest1.do")
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", "你好Plus");
modelAndView.setViewName("../welcome.jsp");
return modelAndView;
}
@RequestMapping(value="/controllerTest2.do")
public ModelAndView handleRequest1(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
String username = httpServletRequest.getParameter("username");
if ("gavin".equals(username)) {
PrintWriter writer = httpServletResponse.getWriter();
writer.println("你好,欢迎来到这里");
}
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", "你好,欢迎来到这里");
modelAndView.setViewName("../welcome.jsp");
return modelAndView;
}
}
部署后请求------>>
可以看到请求路径上多了一层controll
http://localhost:8080/myweb1_war_exploded/controll/controllerTest2.do
@RequestMapping注解除了基本的–默认为
value属性还可以指的别的属性
name—为地址取一个别名
method—请求方式
params —指定请求时的参数,只有包含该参数才可以通过处理;
headers—指定请求时包含的header
consumes -----指定请求的提交内容的类型
produces—指定返回的内容类型
组合注解
requsetmapping可以只指定请求的方式,当然在注解时可以直接用请求方式注解来实现一样的功能;
@Controller
public class ControllerTest {
// @RequestMapping 当标注在一个方法上时,该方法将成为一个请求处理方法,
// 它会在程序接收到对应的URL请求时被调用。
@GetMapping(value = "/controllerTest")
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", "你好Plus");
modelAndView.setViewName("welcome.jsp");
return modelAndView;
}
}
除了getMapping还有其他请求方式----
@GetMapping:匹配GET方式的请求。
@PostMapping:匹配POST方式的请求。
@PutMapping:匹配PUT方式的请求。
@DeleteMapping:匹配DELETE方式的请求。
@PatchMapping:匹配PATCH方式的请求
设置含有指定的请求参数时才可以生效的方法----->>>
@Controller
public class ControllerTest {
// @RequestMapping 当标注在一个方法上时,该方法将成为一个请求处理方法,
// 它会在程序接收到对应的URL请求时被调用。
@GetMapping(value = "/controllerTest" ,params = "name=gavin")
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", "你好Plus");
modelAndView.setViewName("welcome.jsp");
return modelAndView;
}
}
注意org.springframework.ui.Model类型不是一个Servlet API类型,而是一个包含Map对象的Spring MVC类型。如果方法中添加了Model参数,那么每次调用该请求处理方法时,Spring MVC都会创建Model对象,并将其作为参数传递给方法。
请求处理方法还可以返回其他类型的数据。
Spring MVC所支持的常见方法返回类型如下。
ModelAndView
Model
Map
View
String
void
HttpEntity<?>或ResponseEntity<?>
Callable<?> DeferredResult<?>
常见的返回类型有ModelAndView、String和void。
ModelAndView类型中可以添加Model数据,并指定视图;
String类型的返回值可以跳转视图,但不能携带数据;
而void类型主要在异步请求时使用,它只返回数据,而不会跳转视图。
当我们没有参数要传递时,可以用以下方法更加迅速---->>>>
定义一个返回值为String的方法
@Controller
public class ControllerTest {
// @RequestMapping 当标注在一个方法上时,该方法将成为一个请求处理方法,
// 它会在程序接收到对应的URL请求时被调用。
@GetMapping(value = "/controllerTest.do")
public String handleRequest() {
return "welcome.jsp";
}
}
这时候如果要传递一些信息可以通过Model
的参数类型来传递
@Controller
public class ControllerTest {
// @RequestMapping 当标注在一个方法上时,该方法将成为一个请求处理方法,
// 它会在程序接收到对应的URL请求时被调用。
@GetMapping(value = "/controllerTest.do")
public String handleRequest(Model model) {
model.addAttribute("msg","世界那么大");
return "welcome.jsp";
}
}
如果方法中添加了Model参数,那么每次调用该请求处理方法时,Spring MVC都会创建Model对象,并将其作为参数传递给方法。
整合案例----->>>
forward 与 redirect
请求转发与响应重定向
@Controller
public class ControllerTest {
// @RequestMapping 当标注在一个方法上时,该方法将成为一个请求处理方法,
// 它会在程序接收到对应的URL请求时被调用。
@GetMapping(value = "/controllerTest.do")
public String handleRequest(HttpServletRequest req,HttpServletResponse resp,Model model) {
System.out.println(req);
String name = req.getParameter("name");
String pwd = req.getParameter("pwd");
User user = new User();
user.setName(name);
user.setPwd(pwd);
model.addAttribute("msg",user);
return "forward:welcome.jsp";
//return "redirect:welcome.jsp";
}
}
视图解析器
在没使用注解的时候我们在xml中配置了一个视图解析器,
视图解析器有什么作用呢?
我们可以在定义多个方法时----即类上加requestMapping(getMapping/postMapping都可以)时 设置访问前缀与后缀
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="../"/>
<property name="suffix" value=".jsp"/>
</bean>
案例应用------>>
@Controller
@RequestMapping(value ="/controll")
public class ControllerTest {
// @RequestMapping 当标注在一个方法上时,该方法将成为一个请求处理方法,
// 它会在程序接收到对应的URL请求时被调用。
@RequestMapping(value="/controllerTest1.do")
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", "你好Plus");
modelAndView.setViewName("welcome");
return modelAndView;
}
@RequestMapping(value="/controllerTest2.do")
public ModelAndView handleRequest1(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
String username = httpServletRequest.getParameter("username");
if ("gavin".equals(username)) {
PrintWriter writer = httpServletResponse.getWriter();
writer.println("你好,欢迎来到这里");
}
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", "你好,欢迎来到这里");
modelAndView.setViewName("../welcome.jsp");//这个是为了对比
return modelAndView;
}
}
这样设置后,方法中所定义的view路径将可以简化,在访问时视图解析器会自动地增加前缀和后缀。
Springmvc中的数据绑定
在处理请求时我们会遇到各种各样的请求,springmvc框架是如何绑定并获取请求的呢?
试想一下,当我们编写springmvc程序时我们知道输入的什么参数类型能得到什么样子的反馈,但是若换做他人,该怎么 处理呢?
在执行程序时,Spring MVC根据客户端请求参数的不同将请求消息中的信息以一定的方式转换并绑定到控制器类的方法参数中
简单数据绑定
简单数据绑定包括绑定默认数据类型、绑定简单数据类型、绑定POJO类型、绑定包装POJO等。
常用的默认参数类型
HttpServletRequest:获取请求信息。
HttpServletResponse:处理响应信息。
HttpSession:得到session中存储的对象。
Model/ModelMap:作用是将Model数据填充到request域。
绑定默认数据类型
绑定简单数据类型
基本数据类型及其包装类
有时候定义请求的参数与前端参数不一样,这时候可以通过@RequestParam来绑定一个别名
@Controller
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public class ControllerDemo1 {
/**
* @return
*/
@RequestMapping(value ="/controller1.do" ,method= RequestMethod.GET)
public Object searchBookById(Integer bid, @RequestParam(value = "book_id") Integer id, Model model) {
model.addAttribute("msg",id);
model.addAttribute("message",bid);
// 参数处理
return "book.jsp";
}
}
book.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${bookInfo}</br>
你好</br>
${msg}
${message}
</body>
</html>
可以看到没有绑定的是得不到请求参数的;------>>
将请求中book_id参数的值1赋给方法中的id形参。这样通过输出语句就可以输出id形参中的值。
绑定pojo
当我们查询多个参数时,可以将这些参数封装到一个pojo类中,这样就减少了一些操作;
例如
下面这个案例----注册页面
@Autowired
private SqlSessionFactory sqlSessionFactory;
@Autowired
@Qualifier(value = "sqlSessionFactory")
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@RequestMapping(value = "/register.do")
public String register() {
return "register.jsp";
}
@RequestMapping("/RegisterUser.do")
public Object regUser(User user) {//参数为 user类对象
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.addUser(user);
ModelAndView modelAndView=new ModelAndView();
modelAndView.addObject("user",user.getName());
modelAndView.setViewName("welcome.jsp");
return modelAndView;
}
}
注册页面---->>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<meta charset="UTF-8">
<head>
<title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/RegisterUser.do" method="post">
账号:<input type="text" name="name" placeholder="请输入用户名"/>
密码:<input type="password" name="pwd" placeholder="请输入账号密码"/>
<input type="submit" value="注册"/>
</form>
</body>
</html>
跳转到的页面---->>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<html>
<meta charset="UTF-8">
<head>
<title>Title</title>
</head>
<body>
你好${user}
</body>
</html>
注册之后的页面---->>
还记得怎么处理这个的吗??
几种方式----
1,在回应的时候处理,
2,在前端加一个过滤器,进行转码处理;
所以此时适用于第二种
在web配置文件中添加过滤器
自己写的话,实现filter接口…
如果真的有一些要自定义过滤的话…
在框架中早就帮我们做好了
<filter>
<filter-name>CharacterEncoding</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>CharacterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
再次测试---->>
包装类Pojo
所谓包装类pojo就时一个类中包含了另一个类的信息,类似于mybatis中一对一或者一对多那样;
来一个一对一案例-----…
源码见链接----
复杂数据绑定
数组
举个不太恰当的例子----加入要注销员工-----即删除多个员工
映射文件—
<delete id="delEmpByempno" >
delete from gavin.emp where EMPNO in
<foreach collection="array" index="index"
item="item" open="(" separator="," close=")">
#{item}
</foreach>
</delete>
@RequestMapping("/toDel.do")
public String toDel(){
return "delemp.jsp";
}
@RequestMapping("/delEmp.do")
@Transactional(propagation = Propagation.REQUIRED ,isolation = Isolation.DEFAULT)
public Object delEmp(@RequestParam("ids") Integer []ids,Model model){
for (Integer id : ids) {
System.out.println(id);
}
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
int i = mapper.delEmpByempno(ids);
model.addAttribute("delInfo",i);
return "delInfo.jsp";
}
前端页面
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<html>
<meta charset="UTF-8"/>
<head>
<title>Title</title>
</head>
<body>
<form method="get" action="${pageContext.request.contextPath}/delEmp.do">
<input type="checkbox" name="ids" value="7369">SMITH<br>
<input type="checkbox" name="ids" value="7499">ALEEN<br>
<input type="checkbox" name="ids" value="7521">WARD<br>
<input type="submit" value="提交">
</form>
</body>
</html>
集合
前端请求传递过来的数据可能会批量包含各种类型的数据,如Integer、String等。这种情况使用数组绑定是无法实现的。针对这种情况,可以使用集合数据绑定
举个例子,假如我们要批量修改用户信息
好像字段太多,还是换部门表进行修改吧
加入要修改或者添加多个部门信息
准备方法—
但是发现前端无法绑定集合,那则么办呢?
只能去一个折中的方法
新建一个类用于包装一下集合
像这样
import com.gavin.pojo.Dept;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
@Data
@AllArgsConstructor
public class addDeptInfo {
private List<Dept> deptList;
}
映射接口就改为这样,以便用于接收集合
配置映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gavin.mapper.addDeptInfoMapper">
</mapper>
<insert id="addDept" parameterType="addDeptInfo">
insert into gavin.dept (deptno, dname, loc) values
<foreach collection="deptList" item="dept" separator=",">
(#{dept.deptno} ,#{dept.dname},#{dept.loc})
</foreach>
</insert>
写处理方法—
@RequestMapping("/toAdd.do")
public String toAdd(){
return "addDeptInfo.jsp";
}
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT)
@RequestMapping("/addDept.do")
public Object addDept( addDeptInfo deptList,Model model){
List<Dept> deptList1 = deptList.getDeptList();
//检查获得的参数
for (Dept dept:deptList1
) {
System.out.println(dept);
}
SqlSession sqlSession = sqlSessionFactory.openSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
int i = mapper.addDept(deptList);
model.addAttribute("num",i);
return "addSuccessInfo.jsp";
}
测试代码
图解
静态资源放行
案例演示---->>
在请求资源时,
@RequestMapping("/toreq.do")
public String toRequest(){
return "toReq.jsp";
}
前端资源
<%@ page contentType="text/html;charset=UTF-8" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt_rt" %>
<%@taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<html>
<head>
<title>欢迎</title>
</head>
<body>
<link rel="stylesheet" type="text/css" href="css/devc.css"/>
<h1>蒹葭</h1>
<dev id="devinfo" >
<p id="devp" style="text-align: justify-all">
蒹葭苍苍,白露为霜。所谓伊人,在水一方。溯洄从之,道阻且长。溯游从之,宛在水中央。<br>
蒹葭萋萋,白露未晞。所谓伊人,在水之湄。溯洄从之,道阻且跻。溯游从之,宛在水中坻。<br>
蒹葭采采,白露未已。所谓伊人,在水之涘。溯洄从之,道阻且右。溯游从之,宛在水中沚。<br>
</p>
</dev>
<img src="img/jianjiacangcang.jpeg">
</body>
</html>
访问结果—>>
由于前端需要css文件和图片,但是被disparture拦截了,
什么原理呢?
怎么解决呢?
除此之外,还可以简化为将静态资源放在一个文件夹下,这样就不用写那么多放行的资源了;
例如----->>
JSON数据交互和RESTful支持
Spring MVC中的数据绑定需要对传递数据的格式和类型进行转换
JSON数据交互
JSON与XML非常相似,都是用于存储数据的,但JSON相对于XML来说,解析速度更快,占用空间更小。
json是一种纯文本,比xml读写的速度更快
JSON 语法规则
JSON 语法是 JavaScript 对象表示语法的子集。
数据在键值对中对中
每个数据间用逗号分隔
用大括号 {} 保存对象
中括号 [] 保存数组,数组可以包含多个对象
key:value
json 值可以是
数字,字符串,逻辑值,数组([]),对象({}),null
数字:
{“age”:30}
对象:
{“name”:“hello”,“url”:“world”}
数组
数组可包含多个对象:
{
“jdbc”:[
{“driver”:“com.mysql.cj.jdbc.Driver”},
{“url”:“jdbc:mysql://localhost:3306?gavin”},
{“username”:“gavin”},
{“password”:“123456”}
]
}
这个表示一个数组对象,名为site,包含4个对象;
逻辑值
{“flag”:true}
null
{“hello”:null}
关键字必须为String类型的,值可以是任意数据类型;
JSON 使用 JavaScript 语法,所以无需额外的软件就能处理 JavaScript 中的 JSON。
json使用javaScript语法
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<script>
var jdbc=
[
{"driver":"com.mysql.cj.jdbc.Driver"},
{"url":"jdbc:mysql://localhost:3306?gavin"},
{"username":"gavin"},
{"password":"123456"}
];
// 访问 JavaScript 对象数组中的第一项(索引从 0 开始)
function wwwww(){
console.log(jdbc[0].driver);
}
</script>
<input type="button" onclick="wwwww()">
</body>
</html>
这样修改和查看
console.log(jdbc[0].driver);
jdbc[2].username="lim";
console.log(jdbc[2].username);
json与xml表相同内容时候的写法对比
<jdbc>
<driver>com.mysql.cj.jdbc.Driver
</driver>
</jdbc>
<jdbc>
<url>com.mysql.cj.jdbc.Driver
</url>
</jdbc>
<jdbc>
<username>com.mysql.cj.jdbc.Driver
</username>
</jdbc>
<jdbc>
<password>com.mysql.cj.jdbc.Driver
</password>
</jdbc>
xml需要xml解析器来解析,json可以使用标准的javaScript来解析
JSON.parse(): 将一个 JSON 字符串转换为 JavaScript 对象。 JSON.stringify(): JavaScript 值转换为 JSON 字符串。
案例开发—>>
java代码---->>
@RequestMapping("/testJson.do")//这里映射加上
@ResponseBody//先测试一下如果是简单的类型
public User testJson(@RequestBody User user){
System.out.println(user);
return user;
}
前端jsp
<%--
Created by IntelliJ IDEA.
User: Gavin
Date: 2021/12/21
Time: 11:16
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transition//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>json数据交互</title>
</head>
<body>
<form>
账号:<input type="text" name="name" id="name" placeholder="请输入账号"/><br>
密码:<input type="password" name="pwd" id="pwd" placeholder="请输入密码"/><br>
<input type="button" value="测试" onclick="testJson()"/><br>
</form>
<script type="text/javascript"
src="${pageContext.request.contextPath}/static/js/jquery-3.5.1.min.js">
</script>
<script type="text/javascript">
function testJson() {
console.log("你好" + name + pwd)
$.ajax({
url: "${pageContext.request.contextPath}/testJson.do",
type: "POST",
contentType: "application/json ;charset=UTF-8",
// data: [{"name": $("#name").val()},{"pwd":$("#pwd").val()}],
data: JSON.stringify({name: $("#name").val(), pwd: $("#pwd").val()}),
// data:{"name":name},
dataType: "json",
success: function (data) {
alert("Success")
alert(data.name + data.pwd)
},
err: function () {
alert("发生了错误");
}
}
);
}
</script>
</body>
</html>
Spring MVC除了支持JSON数据交互外,还支持RESTful风格的编程
Spring MVC除了支持JSON数据交互外,还支持RESTful风格的编程
什么是restful风格?
简单来说就是将请求参数变为请求路径的一种,比如我们在查询
http://localhost:8080/myweb2_war_exploded/searchByDeptNo.do?deptno=10
restful风格的路径是这么写的
http://localhost:8080/myweb2_war_exploded/DeptNo.do/deptno=10
RESTful风格中的URL将请求参数id=1变成了请求路径的一部分,并且URL中的searchByDeptNo.do也变成了DeptNo.do(RESTful风格中的URL不存在动词形式的路径
话不多说直接上------------------------------>>>>
将原来的查询方法直接拿过来改一下------------->>
@RequestMapping(value ="/DeptNo.do/{deptno}",method = RequestMethod.GET)
@ResponseBody
public Object searchByDeptno1(@PathVariable("deptno") Integer deptno) {
if (deptno != null) {
SqlSession sqlSession = sqlSessionFactory.openSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = mapper.selectEmpBydeptno(deptno);
sqlSession.close();
return dept;
}
return null;
}
前端页面—>>
<%--
Created by IntelliJ IDEA.
User: Gavin
Date: 2021/12/21
Time: 11:16
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transition//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>json数据交互</title>
</head>
<body>
<form>
部门号:<input type="text" name="deptno" id="deptno" placeholder="请输入部门号"/><br>
<input type="button" value="查询" onclick="testJson()"/><br>
</form>
<script type="text/javascript"
src="${pageContext.request.contextPath}/static/js/jquery-3.5.1.min.js">
</script>
<script type="text/javascript">
function testJson() {
var deptno= $("#deptno").val()
console.log(deptno);
$.ajax({
url: "${pageContext.request.contextPath}/DeptNo.do/"+deptno,
type: "get",
contentType: "application/json ;charset=UTF-8",
dataType: "json",
success: function (data) {
alert("Success");
alert("查询的部门信息为:"+data.deptno+":"+data.dname+":"+data.loc+data.empOfDept );
},
err: function () {
alert("发生了错误");
}
}
);
}
</script>
</body>
</html>
结果如下:
restful对不同请求方式的识别
import org.springframework.web.bind.annotation.*;
@RestController
public class testController {
@GetMapping(value = "/toInfo.do/{id}")
public String toGET(@PathVariable("id") Integer id){
System.out.println("get"+id);
return "GetSuccess";
}
@PostMapping("/toInfo.do/{id}")
public String toPost(@PathVariable("id") Integer id){
System.out.println("post"+id);
return "PostSuccess";
}
@PutMapping("/toInfo.do/{id}")
public String toPut(@PathVariable("id") Integer id){
System.out.println("put"+id);
return "PutSuccess";
}
@DeleteMapping("/toInfo.do/{id}")
public String toDel(@PathVariable("id") Integer id){
System.out.println("del"+id);
return "DelSuccess";
}
}
前端
--%>
<%@ page contentType="text/html;charset=UTF-8" %>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="get" action="toInfo.do/10" >
id号<input type="text" name="id" placeholder="请输入账号"/><br>
<input type="submit" value="toget" />
</form>
<form method="post" action="toInfo.do/10" >
id号<input type="text" name="id" placeholder="请输入账号"/><br>
<input type="submit" value="topost" />
</form>
<form method="post" action="toInfo.do/10" >
id号<input type="text" name="id" placeholder="请输入账号"/><br>
<input type="hidden" name="_method" value="PUT"/>
<input type="submit" value="toput" />
</form>
<form method="get" action="toInfo.do/10" >
id号<input type="text" name="id" placeholder="请输入账号"/><br>
<input type="hidden" name="_method" value="DELETE"/>
<input type="submit" value="todel" />
</form>
</body>
</html>
总结一下运行逻辑
我们知道前端提交时没有put和del的显式方式,那么表示相应的请求方式呢?restful风格又是怎么识别这些请求方式的呢?
springmvc中提供了一个过滤器专门来处理请求
<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>
HiddenHttpMethodFilter中的方法----->>
private static final List<String> ALLOWED_METHODS =
Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),
HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
/** Default method parameter: {@code _method}. */
public static final String DEFAULT_METHOD_PARAM = "_method";
private String methodParam = DEFAULT_METHOD_PARAM;
/**
* Set the parameter name to look for HTTP methods.
* @see #DEFAULT_METHOD_PARAM
*/
public void setMethodParam(String methodParam) {
Assert.hasText(methodParam, "'methodParam' must not be empty");
this.methodParam = methodParam;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
HttpServletRequest requestToUse = request;
//首先判断是否是post,然后提交时当中包含"_method" 然后根据value之来识别相应的请求方式
if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter(requestToUse, response);
}
然后开就可以识别put与delete两种请求方式了;
springmvc中的拦截器
主要用于拦截用户请求,并对请求做相应的处理
拦截器的实现方式-----类似于javaweb中的过滤器,实现接口或者继承接口实现类;
一种是通过实现 HandlerInterceptor接口或者继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来定义。
另一种是通过实现 WebRequestInterceptor接口或继承WebRequestInterceptor接口的实现类来定义。
过滤器的定义
package com.gavin.InterCeptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class AllInterceptor implements HandlerInterceptor {
/**
* @param request 请求
* @param response 回应
* @param handler handler选择要执行的处理程序,用于类型和/或实例计算
* @return 返回false, 则不会向下继续执行 默认是不做拦截处理的
* @throws Exception
* @desc 该方法会在控制器方法前执行,其返回值表示是否中断后续操作
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}
/**
* @param request 请求
* @param response 回应
* @param handler handler选择要执行的处理程序,用于类型和/或实例计算
* @param modelAndView 处理程序返回的modelAndView
* @throws Exception
* @desc 该方法会在控制器方法调用之后,且解析视图之前执行。
* 可以通过此方法对请求域中的模型和视图做出进一步的修改
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* *@desc 该方法在整个请求完成,即视图渲染结束之后执行。
* 可以通过此方法实现一些资源清理、记录日志信息等工作。
* @param response 当前HTTP响应
* @param handler 启动异步的处理程序执行,用于类型和/或实例检查
* @param请求当前HTTP请求
* @exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
看一下Api会发现,接口中的访问为default即可以去实现也可以不去实现;
下面模拟一下过滤器中方法执行顺序
首先主备一个controler层的方法
@RequestMapping("/testInterceptor.do")
public String testInterceptor(){
System.out.println("hello world");
return "WEB-INF/1234.jsp";
}
准备过滤器
在配置文件添加过滤器------springmvc.xml中
<mvc:interceptor>
<mvc:mapping path="/toAdd.do"/>
<bean class="com.gavin.InterCeptor.DeptInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
执行后看结果
由于没有对请求做任何处理,所以可以看到原生的结果
拦截器配置模板—>>
<!--模板过滤器 -->
<!-- <mvc: interceptor>中的子元素必须按照上述代码的配置顺序进行编写,
即<mvc: mapping…/>→<mvc: exclude-mapping…/>→<bean…/>的顺序,否则文件会报错。
-->
<mvc:interceptors>
<!-- 配置拦截器 全局拦截器 拦截所有的请求-->
<bean class="com.gavin.InterCeptor.AllInterceptor"/>
<mvc:interceptor>
<!-- 配置作用范围-->
<mvc:mapping path="/dataOne/**"/>
<!-- 配置例外情况 如果配置了,那么path不能为空-->
<mvc:exclude-mapping path="/interceptorDemo/**"/>
<!-- 对匹配路径的请求才拦截-->
<bean class="com.gavin.InterCeptor.EmpInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/toAdd.do"/>
<bean class="com.gavin.InterCeptor.DeptInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
多个拦截器
多个拦截起的情况下是怎样一种执行顺寻呢?
配置两个拦截器—在上述一个拦截器的基础上
第一种情况----
两个拦截器对同一个请求路径进行拦截----
执行结果—>
颠倒拦截器顺序后—>
可以看到执行时的顺序---->>
当然这种拦截根本用不到,谁会用两个拦截器拦截同一个请求呢…闲的,只是做个实验
第二种情况—
实际用到的情况是一个全局拦截器,和一个局部拦截器
哈哈,跟定义第一种情况时候顺序一样
下面做一个小案例就是验证用户登录
这个跟之前javaweb中的过滤器一个道理
验证用户登录案例---->>
本来想写两种方式的,但是发现两种方式从内来讲用的是一个原理,
先写一个吧------>>
import com.gavin.pojo.User;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class UserInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle--invoked");
// 检查登陆地址是否符合要求,不能直接去请求别的资源
//要放行的请求资源
String requestURI = request.getRequestURI();
// 放行一
int i = requestURI.indexOf("/toLogin.do");
if(i>=0){
return true;
}
// 放行二
int i1 = requestURI.indexOf("/toLoginIn.do");
if(i1>=0){
return true;
}
//还有一种情况 已经登陆了 放行三
HttpSession session = request.getSession();
User user =(User) session.getAttribute("user");
if(null!=user){
return true;
}
// 剩下的直接拦截然后 登录页
request.setAttribute("Info","请先登录!");
response.sendRedirect("index.jsp");
//request.getRequestDispatcher("index.jsp").forward(request,response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle--invoked");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion--invoked");
}
}
import com.gavin.mapper.UserMapper;
import com.gavin.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@Controller
public class userController {
@Qualifier
private SqlSessionFactory sqlSessionFactory;
@Autowired
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
SqlSession sqlSession = null;
// 请求 跳转到登陆页面
@RequestMapping("/toLogin.do")
public Object toLogin() {
return "index.jsp";
}
HttpSession session = null;
//登录检查
@RequestMapping(value = "/toLoginIn.do", method = RequestMethod.GET)
public String login(HttpServletRequest req) {
// 如果user不为null,说明已经登陆过
session = req.getSession();
User user = (User)session.getAttribute("user");
if(user!=null) {
return "forward:/WEB-INF/welcome.jsp";
}
//user 为null 则判断登录是否符合条件
String name = req.getParameter("name");
String pwd = req.getParameter("pwd");
/* System.out.println(name + pwd);*/
sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
/* System.out.println(mapper);*/
Integer integer = mapper.searchUser(name, pwd);
/* System.out.println(integer);*/
// 用户存在
if (null != integer) {
User userInfo = new User();
userInfo.setName(name);
userInfo.setPwd(pwd);
this.session = req.getSession();
this.session.setAttribute("user", userInfo);
return "forward:/WEB-INF/welcome.jsp";
}
this.session.setAttribute("Info", "用户名或密码错误,请重新输入");
sqlSession.close();
return "redirect:index.jsp";
}
@RequestMapping("/logout.do")
public String logout() {
session.invalidate();
return "index.jsp";
}
前端页面
<%@ page contentType="text/html;charset=UTF-8" %>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript"
src="${pageContext.request.contextPath}/static/js/jquery-3.5.1.min.js">
</script>
</head>
<body>
<form method="get" action="toLoginIn.do" >
账号:<input type="text" name="name" id="username" placeholder="请输入账号"/> <dev>${Info}</dev><br>
密码:<input type="password" name="pwd" id="userpwd" placeholder="请输入密码"/><dev>${Info}</dev><br>
<input type="submit" value="提交" />
</form>
</body>
</html>
Springmvc中的异常处理
在springweb中遇到异常我们可以通过web.xml配置异常时跳转到的页面
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/static/info/failedPage.jsp</location>
</error-page>
准备一个前端页面---->>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<html lang="en">
<meta charset="UTF-8"/>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style>
html,
body {
height: 100%;
}
body {
color: #444;
font: 12px/1.5 'Helvetica Neue', Arial, Helvetica, sans-serif;
background: #f2f2f2 url("${pageContext.request.contextPath}/static/info/img/blueprint.png") repeat 0 0;
}
div.da-wrapper {
width: 100%;
height: auto;
min-height: 100%;
position: relative;
min-width: 320px
}
div.da-wrapper .da-container {
width: 96%;
margin: auto
}
div.da-content {
clear: both;
padding-bottom: 58px
}
@media only screen and (max-width:480px) {
div.da-content {
margin-top: auto
}
}
div.da-error-wrapper {
width: 320px;
padding: 30px 0;
margin: auto;
position: relative
}
div.da-error-wrapper .da-error-heading {
color: #e15656;
text-align: center;
font-size: 24px;
font-family: Georgia, "Times New Roman", Times, serif
}
@-webkit-keyframes error-swing {
0% {
-webkit-transform: rotate(1deg)
}
100% {
-webkit-transform: rotate(-2deg)
}
}
@-moz-keyframes error-swing {
0% {
-moz-transform: rotate(1deg)
}
100% {
-moz-transform: rotate(-2deg)
}
}
@keyframes error-swing {
0% {
transform: rotate(1deg)
}
100% {
transform: rotate(-2deg)
}
}
div.da-error-wrapper .da-error-code {
width: 285px;
height: 170px;
padding: 127px 16px 0 16px;
position: relative;
margin: auto auto 20px;
z-index: 5;
line-height: 1;
font-size: 32px;
text-align: center;
background: url("${pageContext.request.contextPath}/static/info/img/error-hanger.png") no-repeat center center;
-webkit-transform-origin: center top;
-moz-transform-origin: center top;
transform-origin: center top;
-webkit-animation: error-swing infinite 2s ease-in-out alternate;
-moz-animation: error-swing infinite 2s ease-in-out alternate;
animation: error-swing infinite 2s ease-in-out alternate
}
div.da-error-wrapper .da-error-code .tip {
padding-top: 2px;
color: #e15656;
}
div.da-error-wrapper .da-error-code .tip2 {
padding-top: 15px;
}
div.da-error-wrapper .da-error-code .tip3 {
padding-top: 20px;
font-size: 16px;
color: #e15656;
}
div.da-error-wrapper .da-error-pin {
width: 38px;
height: 38px;
display: block;
margin: auto auto -27px;
z-index: 10;
position: relative;
background: url("${pageContext.request.contextPath}/static/info/img/error-pin.png") no-repeat center center
}
p {
margin: 0;
padding: 0;
}
</style>
<title>错误页面</title>
</head>
<body>
<div class="da-wrapper">
<div class="da-content">
<div class="da-container clearfix">
<div class="da-error-wrapper">
<div class="da-error-pin"></div>
<div class="da-error-code">
<p class="tip">STATUS:403</p>
<p class="tip2">服务器开小差了</p>
<p class="tip3">You don't have permission to access the URL on this server.</p>
</div>
<h1 class="da-error-heading">Sorry, 请稍后再试 !!!</h1>
</div>
</div>
</div>
</div>
</body>
</html>
准备后端页面
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
@Controller("/test.do")
public class ExceptController {
@RequestMapping("/test.do")
public String test1(){
return "/WEB-INF/ServiceData/success.jsp";
}
}
访问test.do
接着新建有几个带有错误的请求
@RequestMapping("/test2.do")
public String test2() {
System.out.println(10 / 0);
return "/WEB-INF/serviceData/success.jsp";
}
@RequestMapping("/test3.do")
public String test3() {
String str=null;
System.out.println(str.length());
return "/WEB-INF/serviceData/success.jsp";
}
依次访问
异常处理ExceptionHandler
下面注释掉web.xml中的error-page,定义一个异常处理的servlet
@ExceptionHandler(value = {NullPointerException.class ,ArithmeticException.class})
public String test4() {
return "/static/info/failedPage.jsp";
}
再次请求–>
可以看到响应的代码并不一样.web.xml中的异常页面在发生异常时并没有及你才能够处理而是直接跳转到一个指定页面,
在加入了springmvc异常处理之后,是对异常进行处理,相当与一次正常响应;
SpringMVC中的异常----->>>
系统中异常包括两类:预期异常(检查型异常)和运行时异常 RuntimeException,前者通过捕获异常从而获取异常信息, 后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的 dao、service、controller 出现都通过 throws Exception 向上抛出,最后由 springmvc 前端控制器交由异常处理器进行异常处理;
在开发中应尽可能在后端去处理一下异常,以便做出很好的应对;
error-page是tomcat去处理的,可作用于全局,也可指定异常
SpringMVC中的异常是由dispature去处理的;
使用@ExceptionHandler注解处理异常,只能处理当前Controller中的异常—即局部异常
如果想要扫描其他controller中的异常,这该怎么办呢?
下面就要用到ControllerAdvice了
@ControllerAdvice
@ControllerAdvice配合 @ExceptionHandler完成异常的处理
package com.gavin.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
@ControllerAdvice
public class ExceptController1 {
@ExceptionHandler(value = {NullPointerException.class ,ArithmeticException.class})
public String test4() {
return "/static/info/failedPage.jsp";
}
}
package com.gavin.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ExceptController {
//@ExceptionHandler
@RequestMapping("/test.do")
public String test1() {
return "forward:/WEB-INF/serviceData/success.jsp";
}
@RequestMapping("/test2.do")
public String test2() {
System.out.println(10 / 0);
return "/WEB-INF/serviceData/success.jsp";
}
@RequestMapping("/test3.do")
public String test3() {
String str=null;
System.out.println(str.length());
return "/WEB-INF/serviceData/success.jsp";
}
/*注销掉,以免影响结论
@ExceptionHandler(value = {NullPointerException.class ,ArithmeticException.class})
public String test4() {
return "/static/info/failedPage.jsp";
}
*/
}
再次测试…还有准备工作没做呢,要告诉他那些servlet是他管辖的
配置包扫描----使得在这个包下的异常都归其处理
<context:component-scan base-package="com.gavin.controller"/>
可在xml配置文件中进行配置,或者在ControllerAdvice注解上配置
将异常的类放在在com.gavin.controller包之外在测试一下
package com.gavin.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
@ControllerAdvice(basePackages = "com.gavin.controller")
public class ExceptController1 {
@ExceptionHandler(value = {NullPointerException.class ,ArithmeticException.class})
public String test4() {
return "/static/info/failedPage.jsp";
}
}
由于异常处理时扫描只扫这个包下的,所以…会报500错误;
注:web.xml中的error-page已开启
可以看到在包之外的异常是管不到的;所以web.xml中配置的异常页起作用了;
当局部跟全局异常处理都存在时,执行的优先级是怎样的呢?
局部异常
全局异常
先走局部,
局部无法处理,就会走全局,全局没法处理就可能会走error-page
自定义异常处理
SimpleMappingExceptionResolver
conroller层
package com.gavin.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ExceptController {
//@ExceptionHandler
@RequestMapping("/test.do")
public String test1() {
System.out.println(1233);
return "forward:/WEB-INF/serviceData/success.jsp";
}
@RequestMapping("/test2.do")
public String test2() {
System.out.println(10 / 0);
return "/WEB-INF/serviceData/success.jsp";
}
@RequestMapping("/test3.do")
public String test3() {
String str = null;
System.out.println(str.length());
return "/WEB-INF/serviceData/success.jsp";
}
//@ExceptionHandler(value = {ArithmeticException.class})
public String test4() {
return "/WEB-INF/serviceData/success.jsp";
}
}
注销局部异常处理与全局异常处理
自定义映射简单异常处理类
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import java.util.Properties;
@Configuration//配置类---如果选择这种,可以将xml中的部分去掉
public class simpleExceptionController {
@Bean //这里得到一个简单异常映射处理类
public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver(){
SimpleMappingExceptionResolver simpleMappingExceptionResolver= new SimpleMappingExceptionResolver();
Properties properties= new Properties();
properties.put("java.lang.NullPointerException","/static/info/failedPage.jsp");
properties.put("java.lang.ArithmeticException","/static/info/failedPage.jsp");
simpleMappingExceptionResolver.setExceptionMappings(properties);
return simpleMappingExceptionResolver;
}
}
或者在spring配置文件中添加如下内容,以实现根据异常不同来跳转到不同的页面;
<bean id="simpleExceptionController" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArithmeticException">redirect:/static/info/failedPage.jsp</prop>
<prop key="java.lang.NullPointerException">forward:/WEB-INF/serviceData/success.jsp</prop>
</props>
</property>
</bean>
请求—>
test2.do
test3.do
当有多个异常,每个异常需要跳转到不同的页面,这个时候可以用自定义简单映射异常类来配置映射完成(定义配置类或者在xml中配置)
自定义异常处理HandlerExceptionResolver
在简单异常处理上可以根据需要来定义处理方案;
实现HandlerExceptionResolver
package com.gavin.controller;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Configuration
public class ExceptionHandlerController implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView modelAndView=new ModelAndView();
if (ex instanceof NullPointerException ){
modelAndView.setViewName("/static/info/failedPage.jsp");
}
if (ex instanceof ArithmeticException){
modelAndView.setViewName("forward:/WEB-INF/serviceData/success.jsp");
}
return modelAndView;
}
}
根据异常不同跳转到不同的页面
可以看到
1,简单映射异常处理类--------得到一个的简单映射异常实例,这个实例也是实现了HandlerExceptionResolver,然后通过setExceptionMappings();方法来添加异常映射关系.
2,异常处理类—实现HandlerExceptionResolver
如果是配置类,那么就需要配置一下扫描的包;