Springmvc之JSR303和拦截器

2023-11-16

  • JSR303
  • 拦截器

1.JSR303

什么是JSR303

JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。 JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint(约束) 的实现,除此之外还有一些附加的 constraint。

> 验证数据是一项常见任务,它发生在从表示层到持久层的所有应用程序层中。通常在每一层都实现相同的验证逻辑,这既耗时又容易出错。为了避免重复这些验证,开发人员经常将验证逻辑直接捆绑到域模型中,将域类与验证代码混在一起,而验证代码实际上是关于类本身的元数据。

为什么要使用JSR303

前端不是已经校验过数据了吗?为什么我们还要做校验呢,直接用不就好了?草率了,假如说前端代码校验没写好又或者是对于会一点编程的人来说,直接绕过前端发请求(通过类似Postman这样的测试工具进行非常数据请求),把一些错误的参数传过来,你后端代码不就危险了嘛。

所以我们一般都是前端一套校验,后端在一套校验,这样安全性就能够大大得到提升了。

常用注解

注解名称    注解作用说明
@NotNull    主要用于对象的校验,校验对象不能为null,无法检查长度为0的字符串
@NotBlank    用于String类型参数校验,检查字符串不能为null且trim()之后的size>0
@NotEmpty    主要用于集合校验,校验集合不能为null且不能size>0,也可以用于String的校验
@Size    用于对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Range    控制一个数值的范围
@Length    用于String对象的大小必须在指定的范围内
@Pattern    用于String对象是否符合正则表达式的规则
@Email    用于String对象是否符合邮箱格式
@Min    用于Number和String对象是否大等于指定的值
@Max    用于Number和String对象是否小等于指定的值
@AssertTrue    用于Boolean对象是否为true
@AssertFalse    用于Boolean对象是否为false

package com.zlj.web;

import com.zlj.biz.StuBiz;
import com.zlj.model.Stu;
import com.zlj.util.PageBean;
import com.zlj.util.PropertiesUtil;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zlj
 * @create 2023-09-08 16:55
 */
@Controller
@RequestMapping("stu")
public class StuController {
    @Autowired
    private StuBiz stuBiz;

    //    增
    @RequestMapping("/add")
    public String add(Stu stu,HttpServletRequest request) {
        int i = stuBiz.insert(stu);
        return "redirect:list";
    }
    //    给数据添加服务端校验
    @RequestMapping("/valiAdd")
    public String valiAdd(@Validated Stu stu, BindingResult result, HttpServletRequest req){
//        如果服务端验证不通过,有错误
        if(result.hasErrors()){
//            服务端验证了实体类的多个属性,多个属性都没有验证通过
            List<FieldError> fieldErrors = result.getFieldErrors();
            Map<String,Object> map = new HashMap<>();
            for (FieldError fieldError : fieldErrors) {
//                将多个属性的验证失败信息输送到控制台
                System.out.println(fieldError.getField() + ":" + fieldError.getDefaultMessage());
                 map.put(fieldError.getField(),fieldError.getDefaultMessage());
            }
            req.setAttribute("errorMap",map);
        }else {
            this.stuBiz.insertSelective(stu);
            return "redirect:list";
        }
        return "stu/edit";
    }

    //            删
    @RequestMapping("/del/{sid}")
    public String del(@PathVariable("sid") Integer sid) {
        stuBiz.deleteByPrimaryKey(sid);
        return "redirect:/stu/list";
    }

//        改
@RequestMapping("/edit")
public String edit(Stu stu) {
    stuBiz.updateByPrimaryKeySelective(stu);
    return "redirect:list";
}
//文件上传
    @RequestMapping("/upload")
    public String upload(Stu stu,MultipartFile xxx){
        try {
//    3.后端可以直接利用mutipartFile类,接受前端传递到后台的文件
//    4.将文件转成流,然后写到服务器(某一个硬盘)
//    上传的图片真实的地址
        String dir= PropertiesUtil.getValue("dir");
//        网络访问的地址
        String server=PropertiesUtil.getValue("server");;
        //文件名
        String filename=xxx.getOriginalFilename();
        System.out.println("文件名"+filename);
        System.out.println("文件类别"+xxx.getContentType());
        FileUtils.copyInputStreamToFile(xxx.getInputStream(),new File(dir+filename));
//      /upload/0703.png
        stu.setSpic(server+filename);
        stuBiz.updateByPrimaryKeySelective(stu);
    } catch (IOException e) {
        e.printStackTrace();
    }
    return "redirect:list";
}


//                查
    @RequestMapping("/list")
    public String list(Stu stu, HttpServletRequest request) {
        //stu是用来接收前台传递后台的参数
        PageBean pageBean = new PageBean();
        pageBean.setRequest(request);
        List<Stu> stus = stuBiz.ListPager(stu, pageBean);
        request.setAttribute("lst", stus);
        request.setAttribute("pageBean", pageBean);
//      WEB-INF/jsp/stu/list.jsp
        return "stu/list";

    }
//下载文件
    @RequestMapping(value="/download")
    public ResponseEntity<byte[]> download(Stu stu,HttpServletRequest req){

        try {
            //先根据文件id查询对应图片信息
            Stu stus = this.stuBiz.selectByPrimaryKey(stu.getSid());
            String diskPath = PropertiesUtil.getValue("dir");
            String reqPath = PropertiesUtil.getValue("server");
//            /upload/0703.png  -->D:/temp/upload/0703.png
            String realPath = stus.getSpic().replace(reqPath,diskPath);
            String fileName = realPath.substring(realPath.lastIndexOf("/")+1);
            //下载关键代码
            File file=new File(realPath);
            HttpHeaders headers = new HttpHeaders();//http头信息
            String downloadFileName = new String(fileName.getBytes("UTF-8"),"iso-8859-1");//设置编码
            headers.setContentDispositionFormData("attachment", downloadFileName);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            //MediaType:互联网媒介类型  contentType:具体请求中的媒体类型信息
            return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file),headers, HttpStatus.OK);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
    //多文件上传
    @RequestMapping("/uploads")
    public String uploads(HttpServletRequest req, Stu stu, MultipartFile[] files){
        try {
            StringBuffer sb = new StringBuffer();
            for (MultipartFile file : files) {
                //思路:
                //1) 将上传图片保存到服务器中的指定位置
                String dir = PropertiesUtil.getValue("dir");
                String server = PropertiesUtil.getValue("server");
                String filename = file.getOriginalFilename();
                FileUtils.copyInputStreamToFile(file.getInputStream(),new File(dir+filename));
                sb.append(filename).append(",");
            }
            System.out.println(sb.toString());

        } catch (Exception e) {
            e.printStackTrace();
        }
        return "redirect:list";
    }



    //数据回显
    @RequestMapping("/preSave")
    public String preSave(Stu stu, Model model) {
        if (stu != null && stu.getSid() != null && stu.getSid() != 0) {
            Stu s = stuBiz.selectByPrimaryKey(stu.getSid());
            model.addAttribute("s", s);
        }
        return "stu/edit";
    }
}
<%@ page language="java" pageEncoding="UTF-8"%>
<%@include file="/common/header.jsp"%>
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>博客的编辑界面</title>
</head>
<body>
<form action="${ctx }${empty s ? 'valiAdd' : 'edit'}" method="post">
    学生id:<input type="text" name="sid" value="${s.sid }"><span style="color: red;">${errorMap.sid}</span><br>
    学生姓名:<input type="text" name="same" value="${s.same }"><span style="color: red;">${errorMap.same}</span><br>
    学生年龄:<input type="text" name="sage" value="${s.sage }"><span style="color: red;">${errorMap.sage}</span><br>
    <input type="submit">
</form>
</body>
</html>

2.拦截器

什么是拦截器

​SpringMVC的处理器拦截器,类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。依赖于web框架,在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。

> 什么是过滤器(Filter)
> 依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。

拦截器与过滤器的区别

- 过滤器(filter)

  1.filter属于Servlet技术,只要是web工程都可以使用

  2.filter主要由于对所有请求过滤

  3.filter的执行时机早于Interceptor

- 拦截器(interceptor)

  1.interceptor属于SpringMVC技术,必须要有SpringMVC环境才可以使用

  2.interceptor通常由于对处理器Controller进行拦截

  3.interceptor只能拦截dispatcherServlet处理的请求

应用场景

- 日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
- 权限检查:如登录检测,进入处理器检测是否登录,如果没有直接返回到登录页面;
- 性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
- 通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个Controller中的处理方法都需要的,我们就可以使用拦截器实现。

package com.zlj.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  OneInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("【OneInterceptor】:preHandle...");

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("【OneInterceptor】:postHandle...");

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("【OneInterceptor】:afterCompletion...");
    }
}
package com.zlj.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 TwoInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("【TwoInterceptor】:preHandle...");

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("【TwoInterceptor】:postHandle...");

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("【TwoInterceptor】:afterCompletion...");
    }
}

 单拦截器,多拦截器配置如下,区别:单拦截器只会拦截一次多拦截器会拦截多次。

//spring-mvc.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:aop="http://www.springframework.org/schema/aop"
       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/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
   <!--1) 扫描com.zking.zf及子子孙孙包下的控制器(扫描范围过大,耗时)-->
    <context:component-scan base-package="com.zlj"/>

    <!--2) 此标签默认注册DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter -->
    <mvc:annotation-driven />

    <!--3) 创建ViewResolver视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- viewClass需要在pom中引入两个包:standard.jar and jstl.jar -->
        <property name="viewClass"
                  value="org.springframework.web.servlet.view.JstlView"></property>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--4) 单独处理图片、样式、js等资源 -->
<!--     <mvc:resources location="/css/" mapping="/css/**"/>-->
<!--     <mvc:resources location="/js/" mapping="/js/**"/>-->
<!--     <mvc:resources location="WEB-INF/images/" mapping="/images/**"/>  -->
    <mvc:resources location="/static/" mapping="/static/**"/>

    <!--    处理文件上传下载问题-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 必须和用户JSP 的pageEncoding属性一致,以便正确解析表单的内容 -->
        <property name="defaultEncoding" value="UTF-8"></property>
        <!-- 文件最大大小(字节) 1024*1024*50=50M-->
        <property name="maxUploadSize" value="52428800"></property>
        <!--resolveLazily属性启用是为了推迟文件解析,以便捕获文件大小异常-->
        <property name="resolveLazily" value="true"/>
    </bean>

<!--  配置拦截器-->
    <mvc:interceptors>
        <bean class="com.zlj.interceptor.LoginInterceptor"></bean>
    </mvc:interceptors>

<!--单拦截器-->
<!--    <mvc:interceptors>-->
<!--        <bean class="com.zlj.interceptor.OneInterceptor"></bean>-->
<!--    </mvc:interceptors>-->

<!--    <mvc:interceptors>-->
<!--        &lt;!&ndash;2) 多拦截器(拦截器链)&ndash;&gt;-->
<!--        <mvc:interceptor>-->
<!--            <mvc:mapping path="/**"/>-->
<!--            <bean class="com.zlj.interceptor.OneInterceptor"/>-->
<!--        </mvc:interceptor>-->
<!--        <mvc:interceptor>-->
<!--            <mvc:mapping path="/stu/**"/>-->
<!--            <bean class="com.zlj.interceptor.TwoInterceptor"/>-->
<!--        </mvc:interceptor>-->
<!--    </mvc:interceptors>-->


    <!--    处理controller层发送请求到biz,会经过切面拦截处理-->
    <aop:aspectj-autoproxy/>
</beans>

单拦截器控制台显示

k

多拦截器控制台显示

package com.zlj.web;

import com.zlj.biz.StuBiz;
import com.zlj.model.Stu;
import com.zlj.util.PageBean;
import com.zlj.util.PropertiesUtil;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zlj
 * @create 2023-09-08 16:55
 */
@Controller
@RequestMapping("stu")
public class StuController {
    @Autowired
    private StuBiz stuBiz;

    //    增
    @RequestMapping("/add")
    public String add(Stu stu,HttpServletRequest request) {
        int i = stuBiz.insert(stu);
        return "redirect:list";
    }
    //    给数据添加服务端校验
    @RequestMapping("/vliAdd")
    public String vliAdd(@Validated Stu stu, BindingResult result, HttpServletRequest req){
//        如果服务端验证不通过,有错误
        if(result.hasErrors()){
//            服务端验证了实体类的多个属性,多个属性都没有验证通过
            List<FieldError> fieldErrors = result.getFieldErrors();
            Map<String,Object> map = new HashMap<>();
            for (FieldError fieldError : fieldErrors) {
//                将多个属性的验证失败信息输送到控制台
                System.out.println(fieldError.getField() + ":" + fieldError.getDefaultMessage());
                 map.put(fieldError.getField(),fieldError.getDefaultMessage());
            }
            req.setAttribute("errorMap",map);
        }else {
            this.stuBiz.insertSelective(stu);
            return "redirect:list";
        }
        return "stu/edit";
    }

    //            删
    @RequestMapping("/del/{sid}")
    public String del(@PathVariable("sid") Integer sid) {
        stuBiz.deleteByPrimaryKey(sid);
        return "redirect:/stu/list";
    }

//        改
@RequestMapping("/edit")
public String edit(Stu stu) {
    stuBiz.updateByPrimaryKeySelective(stu);
    return "redirect:list";
}
//文件上传
    @RequestMapping("/upload")
    public String upload(Stu stu,MultipartFile xxx){
        try {
//    3.后端可以直接利用mutipartFile类,接受前端传递到后台的文件
//    4.将文件转成流,然后写到服务器(某一个硬盘)
//    上传的图片真实的地址
        String dir= PropertiesUtil.getValue("dir");
//        网络访问的地址
        String server=PropertiesUtil.getValue("server");;
        //文件名
        String filename=xxx.getOriginalFilename();
        System.out.println("文件名"+filename);
        System.out.println("文件类别"+xxx.getContentType());
        FileUtils.copyInputStreamToFile(xxx.getInputStream(),new File(dir+filename));
//      /upload/0703.png
        stu.setSpic(server+filename);
        stuBiz.updateByPrimaryKeySelective(stu);
    } catch (IOException e) {
        e.printStackTrace();
    }
    return "redirect:list";
}


//                查
    @RequestMapping("/list")
    public String list(Stu stu, HttpServletRequest request) {
        //stu是用来接收前台传递后台的参数
        PageBean pageBean = new PageBean();
        pageBean.setRequest(request);
        List<Stu> stus = stuBiz.ListPager(stu, pageBean);
        request.setAttribute("lst", stus);
        request.setAttribute("pageBean", pageBean);
//      WEB-INF/jsp/stu/list.jsp
        return "stu/list";

    }
//下载文件
    @RequestMapping(value="/download")
    public ResponseEntity<byte[]> download(Stu stu,HttpServletRequest req){

        try {
            //先根据文件id查询对应图片信息
            Stu stus = this.stuBiz.selectByPrimaryKey(stu.getSid());
            String diskPath = PropertiesUtil.getValue("dir");
            String reqPath = PropertiesUtil.getValue("server");
//            /upload/0703.png  -->D:/temp/upload/0703.png
            String realPath = stus.getSpic().replace(reqPath,diskPath);
            String fileName = realPath.substring(realPath.lastIndexOf("/")+1);
            //下载关键代码
            File file=new File(realPath);
            HttpHeaders headers = new HttpHeaders();//http头信息
            String downloadFileName = new String(fileName.getBytes("UTF-8"),"iso-8859-1");//设置编码
            headers.setContentDispositionFormData("attachment", downloadFileName);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            //MediaType:互联网媒介类型  contentType:具体请求中的媒体类型信息
            return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file),headers, HttpStatus.OK);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
    //多文件上传
    @RequestMapping("/uploads")
    public String uploads(HttpServletRequest req, Stu stu, MultipartFile[] files){
        try {
            StringBuffer sb = new StringBuffer();
            for (MultipartFile file : files) {
                //思路:
                //1) 将上传图片保存到服务器中的指定位置
                String dir = PropertiesUtil.getValue("dir");
                String server = PropertiesUtil.getValue("server");
                String filename = file.getOriginalFilename();
                FileUtils.copyInputStreamToFile(file.getInputStream(),new File(dir+filename));
                sb.append(filename).append(",");
            }
            System.out.println(sb.toString());

        } catch (Exception e) {
            e.printStackTrace();
        }
        return "redirect:list";
    }



    //数据回显
    @RequestMapping("/preSave")
    public String preSave(Stu stu, Model model) {
        if (stu != null && stu.getSid() != null && stu.getSid() != 0) {
            Stu s = stuBiz.selectByPrimaryKey(stu.getSid());
            model.addAttribute("s", s);
        }
        return "stu/edit";
    }
}
//edit.jsp
<%@ page language="java" pageEncoding="UTF-8"%>
<%@include file="/common/header.jsp"%>
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>博客的编辑界面</title>
</head>
<body>
<form action="${ctx }${empty s ? 'vliAdd' : 'edit'}" method="post">
    学生id:<input type="text" name="sid" value="${s.sid }"><span style="color: red;">${errorMap.sid}</span><br>
    学生姓名:<input type="text" name="same" value="${s.same }"><span style="color: red;">${errorMap.same}</span><br>
    学生年龄:<input type="text" name="sage" value="${s.sage }"><span style="color: red;">${errorMap.sage}</span><br>
    <input type="submit">
</form>
</body>
</html>

 

package com.zlj.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 LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("【implements】:preHandle...");
        StringBuffer url = request.getRequestURL();
        if (url.indexOf("/login") > 0 || url.indexOf("/logout") > 0){
            //        如果是 登录、退出 中的一种
            return true;
        }
//            代表不是登录,也不是退出
//            除了登录、退出,其他操作都需要判断是否 session 登录成功过
        String name = (String) request.getSession().getAttribute("name");
        if (name == null || "".equals(name)){
            response.sendRedirect("/page/login");
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
package com.zlj.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * @author zlj
 * @create 2023-09-12 17:47
 */
@Controller
public class loginController {
    @RequestMapping("/login")
    public String login(HttpServletRequest req){
        String name = req.getParameter("name");
        HttpSession session = req.getSession();
        if ("zs".equals(name)){
            session.setAttribute("name",name);
        }
        return "redirect:/stu/list";
    }

    @RequestMapping("/logout")
    public String logout(HttpServletRequest req){
        req.getSession().invalidate();
        return "redirect:/stu/list";
    }
}
//login.jsp
<%--
  Created by IntelliJ IDEA.
  User: 朱
  Date: 2023/9/12
  Time: 17:46
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>登录</h1>
<form action="/login" method="post">
    用户:<input name="name">
    <input type="submit">
</form>
</body>
</html>

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Springmvc之JSR303和拦截器 的相关文章

  • C++状态模式:State Pattern

    状态模式 允许对象在其内部状态改变时改变它的行为 对象看起来好像修改类它的类 状态模式是有限状态机 Finite State Machine 的一种实现方式 我们都知道 http请求报文由请求行 请求头 请求空行和请求体四部分组成 服务器在
  • 21 个简洁的 JavaScript单行代码技巧

    JavaScript 发展至今已经变得越来越强大 且广泛用于前端和后端开发 作为一名前端程序员 不断的学习精进技巧 了解JS的最新发展也是非常必要的 而简洁的一行代码示例就是很好的方法 今天 我们有 21 个JavaScript单行代码技巧
  • 23种设计模式之状态模式和策略模式的区别

    文章目录 概述 状态模式 策略模式 区别 总结 概述 在行为类设计模式中 状态模式和策略模式是亲兄弟 两者非常相似 我们先看看两者的通用类图 把两者放在一起比较一下 状态模式 状态模式 状态模式的类图与策略模式一模一样 区别在于它们的意图
  • SpringMVC的@InitBinder的作用

    一 前言 在SpringMVC中 InitBinder注解用于自定义数据绑定的方法 在使用表单提交数据时 SpringMVC会将请求参数绑定到Java对象中 但是 有些时候请求参数的数据格式可能与Java对象的属性格式不一致 这时就需要使用
  • java设计模式——状态模式

    状态模式的定义 一个对象在其内部状态改变的时候改变其行为 状态驱动 由上下文负责 代替if else 代替 switch case 普通方式 public String orderState String state if state eq
  • Sping Security前后端分离两种方案

    前言 本篇文章是基于Spring Security实现前后端分离登录认证及权限控制的实战 主要包括以下四方面内容 Spring Seciruty简单介绍 通过Spring Seciruty实现的基于表单和Token认证的两种认证方式 自定义
  • 设计模式_19 状态模式(含 UML图 和 C++代码)

    设计模式 19 状态模式 19 状态模式 19 1 概念 19 2 结构 19 3 实现 19 3 1 UML图 19 3 2 代码 19 3 3 测试结果 19 4 优缺点 19 4 1 优点 19 4 2 缺点 19 5 使用场景 re
  • cpp:State Pattern

    file Gold h brief State Pattern 状态模式 C 14 2023年5月29日 涂聚文 Geovin Du Visual Studio 2022 edit author geovindu date May 2023
  • java设计模式——状态模式(State Pattern)

    概述 状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题 当系统中某个对象存在多个状态 这些状态之间可以进行转换 而且对象在不同状态下行为不相同时可以使用状态模式 状态模式将一个对象的状态从该对象中分离出来 封装到专门的状
  • 设计模式:状态机模式

    首先状态机模式是处理一个类在内部状态改变的时候 其方法处理信息的模式也会改变 这里说一个在RTS游戏里的应用 有限状态机 我们要赋予每个战斗单位一个智能 比如一定范围内检测到地方单位 且自身处于游荡或者Patrol状态 那么就转换为攻击状态
  • Java设计模式-状态模式

    1 概述 定义 对有状态的对象 把复杂的 判断逻辑 提取到不同的状态对象中 允许状态对象在其内部状态发生改变时改变其行为 例 通过按钮来控制一个电梯的状态 一个电梯有开门状态 关门状态 停止状态 运行状态 每一种状态改变 都有可能要根据其他
  • 【GOF23设计模式】_状态模式_UML状态图_酒店系统房间状态_线程对象状态切换JAVA250

    来源 http www bjsxt com 一 S03E250 01 GOF23设计模式 状态模式 UML状态图 酒店系统房间状态 线程对象状态切换 package com test state public interface State
  • 设计模式【15】——状态模式(State模式)

    文章目录 前言 一 状态模式 State模式 二 具体源码 1 State h 2 State cpp 3 Context h 4 Context cpp 5 main cpp 三 运行结果 总结 前言 每个人 事物在不同的状态下会有不同表
  • 【TS第三讲】完善TS开发环境

    文章目录 写在前面 ts node nodemon nodemon文件类型 nodemon文件范围 写在最后 写在前面 探索TypeScript世界 驭Vue3 Ts潮流 开启前端之旅 欢迎来到前端技术的精彩世界 无论你是刚刚踏入编程大门的
  • 【面试题】前端开发中如何高效渲染大数据量?

    前端面试题库 面试必备 推荐 地址 前端面试题库 国庆头像 国庆爱国 程序员头像 总有一款适合你 在日常工作中 较少的能遇到一次性往页面中插入大量数据的场景 数栈的离线开发 以下简称离线 产品中 就有类似的场景 本文将分享一个实际场景中的前
  • Springmvc之JSR303和拦截器

    JSR303 拦截器 1 JSR303 什么是JSR303 JSR是Java Specification Requests的缩写 意思是Java 规范提案 是指向JCP Java Community Process 提出新增一个标准化技术规
  • 【业务功能篇78】微服务-前端后端校验- 统一异常处理-JSR-303-validation注解

    5 前端校验 我们在前端提交的表单数据 我们也是需要对提交的数据做相关的校验的 Form 组件提供了表单验证的功能 只需要通过 rules 属性传入约定的验证规则 并将 Form Item 的 prop 属性设置为需校验的字段名即可 校验的
  • 行为型模式-状态模式

    package per mjn pattern state after 环境角色类 public class Context 定义对应状态对象的常量 public final static OpeningState OPENING STAT
  • 如何利用Requestly提升前端开发与测试的效率

    前端测试 在进行前端页面开发或者测试的时候 我们会遇到这一类场景 在开发阶段 前端想通过调用真实的接口返回响应 在开发或者生产阶段需要验证前端页面的一些 异常场景 或者 临界值 时 在测试阶段 想直接通过修改接口响应来验证前端页面是否正常
  • 新手也能看懂的【前端自动化测试入门】

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读2 3k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自

随机推荐

  • Redis4.0从库复制报错"master_link_status:down"处理一例

    环境描述 Redis版本 4 0 2 主库 192 168 0 190 从库 192 168 0 191 今天Zabbix告警一直出现redis sync error的信息 于是登陆redis发现从库复制状态一直是master link s
  • 若依前端框架登录执行过程

    一 登录页面 登录页面是Views文件夹下的login vue文件 二 点击登录调用的方法为 handleLogin handleLogin this refs loginForm validate valid gt if valid th
  • Bugku题目MISC部分(持续更新)

    目录 telnet 1和0的故事 这是一张单纯的图片 隐写 社工 进阶收集 来自论坛提问 gQiRf的附件 zip 简单取证1 mimikatz PasswareKitForensic工具 眼见非实 啊哒 ping FileStoraged
  • spring中使用ThreadPoolTaskExecutor配置线程池

    背景 spring中经常使用ThreadPoolTaskExecutor来调用JDK的ThreadPoolExecutor初始化线程池 尤其在有异步执行的任务时 由于spring 异步任务默认使用的executor不会reuse线程 因此需
  • 寄存器优化补充

    一 简要概述 结构体在寄存器中应用可以简化繁琐的寄存器 这段代码在每个结构体成员前增加了一个 IO 前缀 它的原型在这段代码的第一行 代表了C 语言中的关键字 volatile 在 C 语言中该关键字用于表示变量是易变的 要求编译器不要优化
  • Pytorch cpu版安装及卸载详细教程(以及安装成功后无法在ide中使用问题解决方法)

    Pytorch cpu版安装及卸载详细教程 以及安装成功后无法在ide中使用问题解决方法 一 准备 1 首先确定python的版本 且python是64位 win R 输入cmd 确定 在页面中输入python回车 可查看python版本既
  • React---使用react脚手架搭建项目

    一 使用create react app创建react应用 1 1 react脚手架 xxx脚手架 用来帮助程序员快速创建一个基于xxx库的模板项目 包含了所有需要的配置 语法检查 jsx编译 devServer 下载好了所有相关的依赖 可
  • eclipse改变html字体大小,细说eclipse设置字体以及字体大小?

    电脑现已成为我们工作 生活和娱乐必不可少的工具了 在使用电脑的过程中 可能会遇到eclipse设置字体以及字体大小 的问题 如果我们遇到了eclipse设置字体以及字体大小 的情况 该怎么处理怎么才能解决eclipse设置字体以及字体大小
  • 初步使用基于RESTful的前后端交互方式,包括前后端增、删、改流程

    目录 一 问题背景 二 解决方法 1 POST增 2 DELETE删 3 PUT改 4 参考源代码 一 问题背景 最开始接触web后端开发时 使用的请求大多是通过自定义的一些请求名称 比如update do delete do等 现在为了规
  • python实现卷积神经网络实现手写数字识别

    工具 tensorflow opencv import cv2 import numpy as np import tensorflow as tf np set printoptions suppress True def get tra
  • 重新定义分析 - EventBridge 实时事件分析平台发布

    对于日志分析大家可能并不陌生 在分布式计算 大数据处理和 Spark 等开源分析框架的支持下 每天可以对潜在的数百万日志进行分析 事件分析则和日志分析是两个完全不同的领域 事件分析对实时性的要求更高 需要磨平事件领域中从半结构化到结构化的消
  • ListView的视图属性View的Details的时候每一项的旁边老有个小方框,如何去掉?

    ListView有一个CheckBox属性 将其修改为false即可
  • 共享存储映射

    存储映射I O 存储映射I O memory mapped I O 使一个磁盘文件与存储空间中的一个缓冲区相映射 于是当从缓冲区中获取数据 就相当于读文件中的相应字节 与此类似 将数据存入缓冲区 则相应的字节就自动写入文件 这样 就可在不适
  • 算法序列--树

    树结构是一种非线性存储结构 存储的是具有 一对多 关系的数据元素的集合 树的结点 结点 使用树结构存储的每一个数据元素都被称为 结点 例如图中 数据元素 A 就是一个结点 父结点 双亲结点 子结点和兄弟结点 对于图中的结点 A B C D
  • laravel进阶学习之laravel生命周期

    Laravel 生命周期概括起来大致可分为三步 1 加载依赖 2 创建 Laravel 应用实例 3 接收请求并响应 加载依赖及生成实例部分 一个请求进来之后由public index php开始也由public index php结束 直
  • windows下python开发环境搭建

    写这篇文章主要目的是指导XXX如何在windows学习并使用python 内容主要包括 sdk安装 IDE的安装 IDE环境的配置 编写一个简单测试示例进行验证 这里我们选择的python开发工具为 python3 vscode pytho
  • 超融合服务器连接虚拟机,H3C UIS 6.5超融合产品新增虚拟机配置指导-5W100

    虚拟机是由一组文件构成的一个完整系统 它具有CPU 内存 网络设备 存储设备和BIOS 虚拟机中操作系统和应用程序的运行方式与在普通物理机上的运行方式没有任何区别 创建虚拟机是服务器虚拟化管理软件最基础的功能 虚拟机的创建过程不仅包括虚拟机
  • Numpy章节 2 数学函数

    章节 2 数学函数 1 通用函数 ufuncs 的使用 NumPy提供了各种通用函数 ufuncs 用于对数组进行逐元素的数学运算 这些函数可以对数组进行向量化操作 提高运算效率 示例代码 arr np array 1 2 3 4 5 平方
  • MySQL学习_01

    SQL通用语法 1 SQL 语句可以单行或多行书写 以分号结尾 2 MySQL 数据库的 SQL 语句不区分大小写 关键字建议使用大写 3 注释 单行注释 注释内容 或 注内容 MySQL 特有 多行注释 注释 SQL分类 DDL Data
  • Springmvc之JSR303和拦截器

    JSR303 拦截器 1 JSR303 什么是JSR303 JSR是Java Specification Requests的缩写 意思是Java 规范提案 是指向JCP Java Community Process 提出新增一个标准化技术规