自定义拦截器
实现HandlerInterceptor接口
preHandle()方法
签名:booleanpreHandle(HttpServletRequest, HttpServletResponse, Object)
在执行目标handler方法之前执行[请求进入控制器之前],如果返回true,则继续执行后续拦截器和目标handler方法;如果返回false则不执行。注意:返回false时最好借助转发或重定向等方式为客户端提供一个响应页面。
postHandle()方法
签名:voidpostHandle(HttpServletRequest, HttpServletResponse, Object, ModelAndView)
在执行目标handler方法之后、渲染视图之前执行。
afterCompletion()方法
在渲染视图之后、返回响应之前执行。
拦截器作用:拦截用户的请求,或者拦截一些非法操作!
步骤定义拦截器
publicclass MyInterceptor implements HandlerInterceptor {
//在进入控制器之前执行,只有返回true,才进行后续的执行!handler表示要拦截的那个方法。 @Override publicboolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle-----"); returntrue; } // 控制器执行完成,进入jsp之前 @Override publicvoid postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle-----");
} // 执行jsp之后 @Override publicvoid afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion-----"); } } |
配置拦截器
在springmvc.xml 中进行配置
第一种配置:这样表示所有请求都被拦截! <mvc:interceptors> <bean class="com.xypuxing.interceptor.MyInterceptor"></bean> </mvc:interceptors> |
第二种配置:设置哪些拦截 <mvc:interceptors> <mvc:interceptor> <!-- 只拦截哪些路径--> <mvc:mapping path="/hello"/> <!-- 拦截器 --> <bean class="com.xypuxing.interceptor.MyInterceptor"></bean> </mvc:interceptor> </mvc:interceptors> 注意:preHandle(); 此方法应该注意返回的true,还是false。 |
案例:
控制器 @RequestMapping("hello") public String hello(){ // 返回的视图名称 System.out.println("hello----"); return"hello"; } |
Hello.jsp <body> <% System.out.println("hello.jsp"); %> </body> |
浏览器请求被拦截的hello,从控制台发现
|
拦截器栈【多个拦截器】
配置多个拦截器的时候,则就会形成拦截器栈
<mvc:interceptors> <!-- 拦截器 --> <bean class="com.xypuxing.interceptor.MyInterceptor"></bean> <bean class="com.xypuxing.interceptor.MyInterceptor2"></bean> </mvc:interceptors> |
结果:
多个拦截器执行的顺序是:先进后出!
他们的执行顺序跟springmvc.xml 中配置拦截器的顺序有关系!
设置先配置拦截器A在配置拦截器B执行顺序为
preHandle(A) --> preHandle(B) --> 控制器方法 --> postHandle(B)
--> postHanle(A) --> JSP --> afterCompletion(B)--> afterCompletion(A)
登录验证
需求:用户登录之后,才能进行数据查询,否则不能进行查询
确保程序能够访问login.jsp,以及能够访问login的控制器
在springmvc.xml 中配置
<mvc:interceptors> <!-- 拦截器 --> <mvc:interceptor> <!-- 拦截所有 --> <mvc:mapping path="/**"/> <!-- 以下配置路径不需要拦截 --> <mvc:exclude-mapping path="/index"/> <mvc:exclude-mapping path="/login"/> <bean class="com.xypuxing.interceptor.MyInterceptor"></bean> </mvc:interceptor> </mvc:interceptors> |
页面 <body> <form action="login" method="post"> username:<input type="text" name="uname"/><br> pwd:<input type="password" name="pwd"/><br> <input type="submit" value="登录"/><br> </form> </body> |
Login 控制器 @RequestMapping("login") public String login(Student stu,HttpSession session){ if (stu.getUname().equals("admin")) { session.setAttribute("stu", stu); return"hello"; } return"login"; }
@RequestMapping("findAll") @ResponseBody public List<Student> findAll(){ List<Student> list = new ArrayList<>(); list.add(new Student(1, "张三", "123")); list.add(new Student(2, "李四", "123")); list.add(new Student(3, "王五", "123")); System.out.println(list); returnlist; } |
Hello.jsp <script type="text/javascript" src="js/jquery-1.7.2.js"></script> <script type="text/javascript"> $(function(){ $.ajax({ type:"post", url:"findAll", dataType:"json", success:function(data){ for(var i = 0;i<data.length;i++){ var con = "<tr><td>"+data[i].id+"</td><td>"+data[i].uname+"</td><td>"+data[i].pwd+"</td></tr>" $("#tab").append(con); } } }) }) </script> </head> <body> <table id="tab"> </table> </body> |
拦截器 publicclass MyInterceptor implements HandlerInterceptor {
//在进入控制器之前执行,只有返回true,才进行后续的执行! @Override publicboolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); Student stu = (Student) session.getAttribute("stu"); if (stu!=null) { returntrue; } response.sendRedirect("error.jsp"); returnfalse; } } |
数据类型转换
日期类型:如果实体类中有日期类型:
// 学生的生日
private Datebirth;
在进行数据提交的时候,则可能会出现如下情况
400 的原因是数据类型不匹配,必须借助@DateTimeFormat(pattern="yyyy-mm-dd") 进行转换!
页面
<body> <form action="login" method="post"> username:<input type="text" name="uname"/><br> pwd:<input type="password" name="pwd"/><br> birth:<input type="text" name="birth"><br> <input type="submit" value="登录"/><br> </form> </body> |
实体类
publicclass Student { private Integer id; private String uname; private String pwd; @DateTimeFormat(pattern="yyyy-mm-dd") private Date birth; } |
控制器
@RequestMapping("login") public String login(Student stu,HttpSession session){ System.out.println(stu+"login"); if (stu.getUname().equals("admin")) { session.setAttribute("stu", stu); return"hello"; } return"login"; } |
还可以添加数字类型的格式化
@NumberFormat(pattern="#,###,###.##") private Double salary; |
<form action="login" method="post"> username:<input type="text" name="uname"/><br> pwd:<input type="password" name="pwd"/><br> birth:<input type="text" name="birth"><br> salary:<input type="text" name="salary"><br> <input type="submit" value="登录"/><br> </form> |
SpringMVC和Spring容器
Web.xml
<?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">
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring.xml</param-value> </context-param>
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
<servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
<!-- Map all requests to the DispatcherServlet for handling --> <servlet-mapping> <servlet-name>springDispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
<!-- 字符编码过滤器 --> <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> <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> </web-app> |
Spring.xml 和 springmvc.xml 配置文件
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="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-4.0.xsd"> <context:component-scan base-package="com.xypuxing"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> </beans> |
<?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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" 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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.xypuxing" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> </beans> |
Service 层
package com.xypuxing.service;
import org.springframework.stereotype.Service;
@Service publicclass BookService { public BookService(){ System.out.println("BookService被创建了"); } } |
Controller层
package com.xypuxing.controller;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller;
import com.xypuxing.service.BookService; @Controller publicclass BookController {
@Autowired BookService bookService;
public BookController() { System.out.println("BookController 被创建了。"); } } |
如果在spring.xml 和springmvc.xml 中同时都扫描 com.xypuxing则会出现实体类会被扫描两次的情况!
RESTFUL----CRUD
REST
概述
Representational State Transfer——表现层(资源)状态转化。是目前最流行的一种互联网软件架构风格。它倡导结构清晰、符合标准、易于理解、扩展方便的Web架构体系,主张严格按照HTTP协议中定义的规范设计结构严谨的Web应用架构体系。由于REST所倡导的理念让Web应用更易于开发和维护,更加优雅简洁,所以正得到越来越多网站的采用。
基本概念
资源(Resources):网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。要获取这个资源,访问它的URI就可以,因此 URI 即为每一个资源的独一无二的识别符。
表现层(Representation):把资源具体呈现出来的形式,叫做它的表现层(Representation)。比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式。
状态转化(StateTransfer):每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”(StateTransfer)。而这种转化是建立在表现层之上的,所以就是 “表现层状态转化”。具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。
REST风格的URL
REST风格要求我们不要再使用问号键值对的方式携带请求参数,而是从URL地址中获取。下面我们进行一下对比:
①保存操作
TRADITIONAL URL |
http://localhost:8080/CRUD/saveEmp |
POST请求 |
RESTFUL URL |
http://localhost:8080/CRUD/emp |
POST请求 |
②删除操作
TRADITIONAL URL |
http://localhost:8080/CRUD/removeEmp?empId=2 |
GET请求 |
RESTFUL URL |
http://localhost:8080/CRUD/emp/2 |
DELETE请求 |
③更新操作
TRADITIONAL URL |
http://localhost:8080/CRUD/updateEmp |
POST请求 |
RESTFUL URL |
http://localhost:8080/CRUD/emp |
PUT请求 |
④查询操作(查询单个对象)
TRADITIONAL URL |
http://localhost:8080/CRUD/getEmp?empId=2 |
GET请求 |
RESTFUL URL |
http://localhost:8080/CRUD/emp/2 |
GET请求 |
REST风格URL地址的好处
TRADITIONAL URL |
http://localhost:8080/CRUD/removeEmp?empId=2 |
RESTFUL URL |
http://localhost:8080/CRUD/emp/2 |
①含蓄,安全
使用问号键值对的方式给服务器传递数据太明显,容易被人利用来对系统进行破坏。使用REST风格携带数据不再需要明显的暴露数据的名称。
②风格统一
URL地址整体格式统一,从前到后始终都使用斜杠划分各个内容部分,用简单一致的格式表达语义。
③无状态
在调用一个接口(访问、操作资源)的时候,可以不用考虑上下文,不用考虑当前状态,极大的降低了系统设计的复杂度。
④严谨,规范
严格按照HTTP1.1协议中定义的请求方式本身的语义进行操作。
⑤简洁,优雅
过去做增删改查操作需要设计4个不同的URL,现在一个就够了
传统方式 |
RESTFUL 风格 |
/CRUD/saveEmp |
/CRUD/emp配合POST请求方式 |
/CRUD/removeEmp?empId=2 |
/CRUD/emp/2配合DELETE请求方式 |
/CRUD/updateEmp |
/CRUD/emp配合PUT请求方式 |
/CRUD/editEmp?empId=2 |
/CRUD/emp/2配合GET请求方式 |
⑥丰富的语义
通过URL地址就可以知道资源之间的关系。
http://localhost:8080/shop |
http://localhost:8080/shop/product |
http://localhost:8080/shop/product/cellPhone |
http://localhost:8080/shop/product/cellPhone/iPhone |
环境搭建
配置web.xml
<?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">
<!-- The front controller of this Spring Web application, responsible for handling all application requests --> <servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
<!-- Map all requests to the DispatcherServlet for handling --> <servlet-mapping> <servlet-name>springDispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
<!-- 字符编码过滤器 --> <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> <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> </web-app> |
配置springmvc.xml
<?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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" 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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.xypuxing" /> <mvc:default-servlet-handler /> <mvc:annotation-driven /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 配置视图解析器前缀 --> <property name="prefix" value="/WEB-INF/pages/"></property> <!-- 配置视图解析器后缀 --> <property name="suffix" value=".jsp"></property> </bean> </beans> |
在WebContent目录下创建一个页面访问控制
<body> <a href="hello">hello</a> </body> |
创建控制器
package com.xypuxing.controller;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller publicclass HelloController {
@RequestMapping("hello") public String index(){ return"success"; } } |
导入实体类,dao
员工列表展示
页面
Index.jsp <a href="emps">查找所有员工</a> |
控制器 package com.xypuxing.controller;
@Controller publicclass EmpController { @Autowired EmployeeDao employeeDao;
@RequestMapping("emps") public String getEmps(Model model){ Collection<Employee> all = employeeDao.getAll(); model.addAttribute("emps",all); return"list"; } } |
<body> <h1>员工列表</h1> <table> <tr> <th>ID</th> <th>lastName</th> <th>email</th> <th>gender</th> <th>departmentName</th> <th>EDIT</th> <th>DELETE</th> </tr> <c:forEach items="${emps }" var="emp"> <tr> <td>${emp.id }</td> <td>${emp.lastName}</td> <td>${emp.email }</td> <td>${emp.gender==0?"女":"男" }</td> <td>${emp.department.departmentName }</td> <td><a href="emp/${emp.id }">edit</a></td> <td><a href="emp/${emp.id }" class="delBtn">delete</a></td> </tr> </c:forEach> </table> </body> |
员工添加
在list.jsp页面添加一个连接,跳转到添加控制器,来到添加页面,保存之后,在列表中展示。在来到添加之前,一定要先查出所有部门信息!
<a href="toaddemppage">添加员工</a> |
该控制器查出所有的部门信息并保存 @RequestMapping("toaddemppage") public String toAddPage(Model model){ // 查出所有部门信息 Collection<Department> departments = departmentDao.getDepartments(); // 保存所有部门信息,到页面显示 model.addAttribute("depts", departments); return"add"; } |
Add.jsp <body> <form action="emp" method="post"> lastName:<input type="text" name="lastName" /><br> email:<input type="text" name="email" /><br> gender:<br /> 男:<input type="radio" name="gender" value="1" /><br /> 女:<input type="radio" name="gender" value="0" /><br /> dept:<select name="department.id"> <c:forEach items="${depts }" var="deptItem"> <option value="${deptItem.id }">${deptItem.departmentName }</option> </c:forEach> </select><br> <input type="submit" value="保存" /> </form> </body> |
添加数据的控制器 @RequestMapping("emp") public String addEmp(Employee employee){ employeeDao.save(employee); return"redirect:/emps"; } |
员工修改
点击edit按钮,来到员工修改页面,修改的时候要加载当前员工的信息。
@RequestMapping("/emp/{id}") public String updateEmp(@PathVariable("id")Integer id,Model model) { Employee employee = employeeDao.get(id); System.out.println("要修改的员工:" + employee); // 将查询到的数据保存到作用域中 model.addAttribute("employee", employee); // 查出所有部门信息 Collection<Department> departments = departmentDao.getDepartments(); model.addAttribute("depts", departments); return"edit"; } |
<form action="${employee.id }" method="post"> <input type="hidden" name="_method" value="put"/> lastName:<input type="text" value="${employee.lastName }" name="lastName" /><br> email:<input type="text" value="${employee.email }" name="email" /><br> gender:<br /> <c:if test="${employee.gender==1 }"> 男:<input type="radio" checked="checked" name="gender" value="1" /><br /> 女:<input type="radio" name="gender" value="0" /><br /> </c:if> <c:if test="${employee.gender==0 }"> 男:<input type="radio" name="gender" value="1" /><br /> 女:<input type="radio" checked="checked" name="gender" value="0" /><br /> </c:if> dept:<select name="department.id"> <c:forEach items="${depts }" var="deptItem"> <option value="${deptItem.id }">${deptItem.departmentName }</option> </c:forEach> </select><br> <input type="submit" value="修改" /> </form> |
@RequestMapping(value="/emp/{id}",method=RequestMethod.PUT) public String updEmp(Employee employee) { employeeDao.save(employee); return"redirect:/emps"; } |
隐藏域: <input type="hidden" name="_method"value="put"/>
1. 是因为有了相同的控制器路径!
2. 根据处理的业务不同,应该使用它Restful的不同请求方式!put请求方式表单没有提供,所以必须加入隐藏域!
员工删除
页面list.jsp
<form id="deleteForm" action="emp/${emp.id }" method="post"> <input type="hidden" name="_method" value="DELETE" /> </form> </body> <script type="text/javascript"> $(function(){ $(".delBtn").click(function(){ $("#deleteForm").attr("action",this.href); $("#deleteForm").submit(); returnfalse; }) }) </script> |
控制器 @RequestMapping(value="emp/{id}",method=RequestMethod.DELETE) public String delEmp(@PathVariable("id")Integer id){ employeeDao.delete(id); return"redirect:/emps"; } |
Restful:风格的CRUD:
最主要的是注意put,delete 请求!
Put:修改
Delete :删除
以上两个操作都需要隐藏域的参与:
<input type="hidden" name="_method"value="DELETE" />
<!-- 配置restful一个过滤器设计到一个隐藏域提交 --> <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> |