zoukankan      html  css  js  c++  java
  • SpringMVC

    SpringMVC

    一、简介

    1.1 引言

    java开源框架,Spring Framework的一个独立模块。

    MVC框架,在项目中开辟MVC层次架构

    对控制器中的功能包装简化扩展践行工厂模式,功能架构在工厂之上

    1.2 MVC架构

    1.2.1 概念

    名称 职责
    Model 模型:即业务模型,负责完成业务中的数据通信处理,对应项目中的service和dao
    View 视图:渲染数据,生成页面。对应项目中的Jsp
    Controller 控制器:直接对接请求,控制MVC流程,调度模型,选择视图。对应项目中的Servlet

    1.2.2 好处

    • MVC是现下软件开发中的最流行的代码结构形态;
    • 人们根据负责的不同逻辑,将项目中的代码分成MVC3个层次;
    • 层次内部职责单一,层次之间藕合度低;
    • 符合低精合高内聚的设计理念。也实际有利于项目的长期维护。

    二、开发流程

    2.1 导入依赖

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.1.6.RELEASE</version>
    </dependency>
    

    2.2 配置核心(前端)控制器:DispatcherServlet

    作为一个MVC框架,首先要解决的是:如何能够收到请求!

    所以MVC框架大都会设计一款前端控制器,选型在Servlet或Filter两者之一,在框架最前沿率先工作,接收所有请求。

    此控制器在接收到请求后,还会负责springMVC的核心的调度管理,所以既是前端又是核心。

    web.xml

    <!--SpringMVC前端(核心)控制器
            1.前端,按收所有请求
            2.启动SpringMVC工厂mvc.xml
            3.springMVC流程调度
        -->
    <!-- SpringMVC前端控制器 -->
    <servlet>
        <servlet-name>mvc_shine</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc.xml</param-value>
        </init-param>
        <!--懒汉式 饿汉式加载 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>mvc_shine</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

    2.3 后端控制器

    等价于之前定义的Servlet

    @Controller     //声明控制器
    @RequestMapping("/hello")	//访问路径
    public class HelloController {
    
        @RequestMapping("/test1")	//访问路径
        public String hello1() { //service doGet doPost
            System.out.println("hello1");
            return "hello";     //跳转:hello.jsp
        }
        @RequestMapping("/test2")	//访问路径
        public String hello2() { //service doGet doPost
            System.out.println("hello2");
            return null;
        }
    }
    

    2.4 配置文件

    默认名称:核心控制器名-servet.xml默认位置:WEB-INF

    随意名称:mvc.xml随意位置:resources但需要配置在核心控制器中

    <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/beans
                               http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/context
                               http://www.springframework.org/schema/context/spring-context.xsd
                               http://www.springframework.org/schema/mvc
                               http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        <!-- 注解扫描 告诉SpringMVC 哪些包中含有注解 -->
        <context:component-scan base-package="com.qf.web"/>
    
        <!-- 注解驱动 -->
        <mvc:annotation-driven></mvc:annotation-driven>
    
        <!-- 视图解析器
            作用:1.辅获后端控制器的返回值="hello"
                 2.解析:在返回值的前后拼接==>"/hello.jsp"
        -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!-- 前缀 -->
            <property name="prefix" value="/"/>
            <!-- 后缀 -->
            <property name="suffix" value=".jsp"/>
        </bean>
    </beans>
    

    三、接受请求参数

    3.1 基本类型参数

    请求参数和方法的形参 同名即可

    SpringMVC默认可以识别的日期字符串格式为:YYYY/MM/dd HH:mm:ss

    通过@DateTimeFormat可以修改默认日志格式

    // http://xxxx/param/test1?id=1&name=shine&gender=true&birth=2020-12-12 12:13:20
    @RequestMapping("/test1")
    public String test1(Integer id, 
                        String name, 
                        Boolean gender,
                        @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") Date birth) {
        System.out.println("test1");
        System.out.println("id:" + id + "   name:" + name + "  gender:" + gender + "  birthday:" + birth);
        return "hello";
    }
    

    3.2 实体收参(重点)

    请求参数和实体的属性 同名即可

    //实体类
    public class User {
        private Integer id;
        private String name;
        private Boolean gender;
        @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
        private Date birth;
    }
    
    // http://xxxx/param/test2?id=1&name=shine&gender=true&birth=2020-12-12 12:13:20
    @RequestMapping("/test2")
    public String test2(User user) {
        System.out.println("test2");
        System.out.println(user);
        return "hello";
    }
    

    3.3 数组收参

    简单类型的 数组

    <form action="${pageContext.request.contextPath}/param/test3">
        <input type="checkbox" name="hobby" value="football">足球
        <input type="checkbox" name="hobby" value="basketball">篮球
        <input type="checkbox" name="hobby" value="volleyball">排球
        <input type="submit" value="提交">
    </form>
    
    @RequestMapping("/test3")
    public String test3(String[] hobby) {
        System.out.println("test3");
        System.out.println(hobby);
        return "hello";
    }
    

    3.4 集合收参

    //实体类
    public class UserList {
        private List<User> users;
    }
    
    //http://xxx/param/test4?users[0].id=1&users[0].name=shine&...
    @RequestMapping("/test4")
    public String test4(UserList userList) {
        System.out.println("test4");
        for (User u :
             userList.getUsers()) {
            System.out.println(u);
        }
        return "hello";
    }
    

    3.5 路径参数

    // {id} 命名规则
    // {id} 等价于 /test5/1 test5/2  test5/xxx
    @RequestMapping("/test5/{id}")
    public String test5(@PathVariable("id") Integer id) {
        System.out.println("test5");
        System.out.println("id:" + id);
        return "hello";
    }
    

    3.6 中文乱码

    tomcat中字符集设置,对get请求中,中文参数乱码有效

    Tomcat配置:URIEncoding=utf-8
    

    web.xml中加入编码过滤器,对post请求中,中文参数乱码有效

    <!-- 编码过滤器 -->
    <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>
    

    四、跳转

    4.1 转发

    @RequestMapping("/test1")
    public String test1() {
        System.out.println("test1");
        //        return "hello";     //转发
        return "forward:/hello.jsp";
    }
    
    @RequestMapping("/test2")
    public String test2() {
        System.out.println("test2");
        //        return "forward:/jump/test1";       //转发
        return "forward:test1";       //相对路径转发
    
    }
    

    4.2 重定向

    @RequestMapping("/test3")
    public String test3() {
        System.out.println("test3");
        return "redirect:/hello.jsp";       //重定向
    }
    
    @RequestMapping("/test4")
    public String test4() {
        System.out.println("test4");
        //        return "redirect:test3";       //重定向
        return "redirect:/jump/test3";       //重定向
    }
    

    4.3 跳转细节

    在增删改之后,为了防止请求重复提交,重定向跳转

    在查询之后,可以做转发跳转

    五、传值

    c得到数据后,跳转到V,并向V传递数据。进而V中可以渲染数据,让用户看到含有数据的页面

    转发跳转:Request作用域

    重定向跳转:Session作用域

    5.1 Request和Session

    //在形参中可以获得Request和Session
    @RequestMapping("/test1")
    public String test1(HttpServletRequest request, HttpSession session) {
        System.out.println("test1");
        request.setAttribute("name", "张三");
        session.setAttribute("age", 10);
        return "data";
    }
    

    5.2 JSP中取值

    name:${requestScope.name}<br>
    age:${sessionScope.age}<br>
    

    5.3 Model

    //model中的数据,会在V渲染之前,将数据复制一份给request
    @RequestMapping("/test2")
    public String test2(Model model) {
        model.addAttribute("city", "北京");
        model.addAttribute("street", "长安街");
        return "data2";
    }
    
    //JSP 通过EL表达式取值
    city:${sessionScope.city}<br>
    street:${sessionScope.street}
    

    5.4 ModelAndView

    //ModelAndView 可以集中管理跳转和数据
    @RequestMapping("/test4")
    public ModelAndView test4() {
        //新建ModelAndView对象
        ModelAndView modelAndView = new ModelAndView();
        //设置视图名即如何跳转
        modelAndView.setViewName("forward:/hello.jsp");
        //增加数据
        modelAndView.addObject("claz", "001");
        return modelAndView;
    }
    
    //JSP 通过EL表达式取值
    ${sessionScope.claz}
    

    5.5 @SessionAttributes

    • @SessionAttributes(name={"city","street}) Model中的name和gender会存入session中

    • SessionStatus 移除Session

    @Controller
    @RequestMapping("/data")
    @SessionAttributes(names = {"city", "street"})
    public class DataController {
    
        @RequestMapping("/test2")
        public String test2(Model model) {
            model.addAttribute("city", "北京");
            model.addAttribute("street", "长安街");
            return "data2";
        }
    
        @RequestMapping("/test3")
        public String test3(SessionStatus status) {
            //清空所有通过model存入的session
            status.setComplete();
            return "data2";
        }
    }
    

    六、静态资源

    6.1 静态资源问题

    静态资源:html,js文件,css文件,图片文件

    • 静态文件没有url-pattern,所以默认是访问不到的

    • 之所以可以访问,是因为tomcat中有一个全局的servlet:
      org.apache.catalina.servlets.DefaultServlet,它的url-pattern是"/"

    • 它是全局默认的Servlet.所以每个项目中不用匹配的静态资源的请求,由这个Servlet来处理即可。

    • 但在SpringMVC中DispatcherServlet也采用了“/"作为url-pattern,所以项目中不会再使用全局的Serlvet,则静态资源不能完成访问,

    6.2 解决方案1

    DispathcerServlet采用其他的url-pattern

    此时,所有访问handler的路径都要以action结尾!!

    web.xml文件中:

    <servlet>
        <servlet-name>mvc_shine</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc_shine</servlet-name>
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>
    

    6.3 解决方案2

    DispathcerServlet的url-pattern依然采用"/",但追加配置

    mvc.xml文件中:

    <!--
       额外的增加一个handler,且其requestMapping:  "/**" 可以匹配所有请求,但是优先级最低
      所以如果其他所有的handler都匹配不上,请求会转向 "/**" ,恰好,这个handler就是处理静态资源的
      处理方式:将请求转会到tomcat中名为default的Servlet
    
      RequestMapping  /*   /a   /b  /c   /dxxxx    /a/b
                      /**
     -->
    <mvc:default-servlet-handler/>
    

    解决方案3

    mapping是访问路径,location是静态资源存放的路径

    将/html/** 中 /**匹配到的内容,拼接到/hhh/ 后 http://.../html/a.html 访问 /hhh/a.html

    mvc.xml文件中

    <mvc:resources mapping="/html/**" location="/hhh/"/>
    

    七、Json处理

    7.1 导入依赖

    <!-- SpringMVC默认的Json解决方案是Jackson,所以只需要导入jackson的jar,即可使用。-->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.12.0</version>
    </dependency>
    

    7.2 发送Json数据

    7.2.1 使用@ResponseBody

    @Controller
    @RequestMapping("/json")
    public class JsonController {
    
        @RequestMapping("/test1")
        @ResponseBody	//handler的返回值,转换成json,并将json响应给客户端。
        public User test1() {
            System.out.println("test1");
            User user = new User(1, "张三",new Date());
            return user;
        }
    
        @RequestMapping("/test2")
        //@ResponseBody还可以用在handler的返回值上
        public @ResponseBody List<User> test2() {
            System.out.println("test2");
            User user = new User(1, "张三");
            User user1 = new User(1, "李四");
            List<User> users = Arrays.asList(user, user1);
            return users;
    	}
    
        //如果返回值已经是字符串,则不需要转json,直接将字符串响应给客户端
        @RequestMapping(value="/test3",produces ="text/html;charset=utf-8")//produces防止中文乱码
        @ResponseBody
        public String test3() {
            System.out.println("test3");
            //        return "ok";
            return "你好";
        }
    }
    

    7.2.2 使用@RestController

    Controller类上加了@RestController注解,等价于在类中的每个方法上都加了@ResponseBody

    7.3 接收Json数据

    7.3.1 使用@RequestBody

    • 接收数据

      public class User {
          private Integer id;
          private String username;
          private String password;
          private Boolean gender;
      }
      
      @RequestMapping("/test4")
      public String test4(@RequestBody User user){//@RequestBody将请求体中的json数据转换为java对象
          System.out.println(user);
          return "ok";
      }
      
    • Ajax发送数据

      function send_json() {
          var user = {id: 1, name: "shine"};
          var json = JSON.stringify(user);
          $.ajax({
              url: "${pageContext.request.contextPath}/json/test4",
              type: "post",
              data: json,
              contentType: "application/json",
              success: function (ret) {
                  alert(ret);
              }
          })
      }
      

    7.4 Jackson常用注解

    7.4.1 日期格式化

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")

    public class User {
        private Integer id;
        private String name;
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        private Date birth;
    }
    

    7.4.2 属性名修改

    //不再使用原属性名 而是“new_id”
    @JsonProperty("new_id")
    private Integer id;
    

    7.4.3 属性忽略

    @JsonIgnore

    //生成Json时,忽略此属性
    @JsonIgnore
    private String name;
    

    7.4.4 null和empty属性排除

    Jackson默认会输出null值的属性,如果不需要,可以排除。

    @JsonInclude(JsonInclude.Include.NON_NULL) 属性不输出null值

    @JsonInclude(JsonInclude.Include.NON_EMPTY) 不输出empty属性(空串,长度为0的集合,null值)

    7.4.5 自动化序列

    @JsonSerialize(using = MySerializer.class)
    private Double salary = 10000.126;      //在输出此属性时,使用MySerializer输出
    
    public class MySerializer extends JsonSerializer<Double> {
    
        @Override
        public void serialize(Double aDouble, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            //将Double salary 的值 四舍五入
            String number = BigDecimal.valueOf(aDouble).setScale(2, BigDecimal.ROUND_HALF_UP).toString();
            //输出 四舍五入之后的值
            jsonGenerator.writeNumber(number);
    
        }
    }
    

    7.5 FastJson

    7.5.1 导入依赖

    <!-- FastJson -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.54</version>
    </dependency>
    

    7.5.2 安装FastJson

    web.xml中安装FastJson

    <!-- 注解驱动 -->
    <mvc:annotation-driven>
        <!-- 安装FastJson转换器 -->
        <mvc:message-converters>
            <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <!-- 声明转换类型 -->
                <property name="supportedMediaTypes">
                    <list>
                        <value>application/json</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    

    7.5.3 使用FastJson

    @ResponseBody @RequestBody @RestController 使用方法不变

    7.5.4 常用注解

    • 日期格式化:@JSONField(format="yyyy/MM/dd")
    • 属性名修改:@JSONField(name="birth")
    • 忽略属性:@JSONField(serialize=false)
    • 包含null值:@JSONField(serialzeFeatures=SerializerFeature.WriteMapNullValue)默认会忽略所有null值,有此注解会输出null
      • @JSONField(serialzeFeatures=SerializerFeature.WriteNullStringAsEmpty)null的String输出为""
    • 自定义序列化:@JSONField(serializeUsing=MySerializer2.class)
    public class User2 {
        @JSONField(serialize = false)
        private Integer id;
        @JSONField(name = "NAME", serialzeFeatures = SerializerFeature.WriteNullStringAsEmpty)
        private String name;// ""
        @JSONField(serialzeFeatures = SerializerFeature.WriteMapNullValue)
        private String city;// null
        @JSONField(format = "yyyy/MM/dd")
        private Date birth;
        @JSONField(serializeUsing = MySerializer2.class)
        private Double salary; // 元
    }
    
    public class MySerializer2 implements ObjectSerializer {
    
        @Override
        public void write(JSONSerializer jsonSerializer, Object o, Object o1, Type type, int i) throws IOException {
            Double value = (Double) o;
            String text = value + "元";      //拼接“元”
            jsonSerializer.write(text);
    
        }
    }
    

    八、异常解析器

    8.1 现有方案,分散处理

    Controller中的每个Handler自己处理异常

    此种处理方案,异常处理逻辑,分散在各个handler中,不利于集中管理

    8.2 异常解析器,统一处理

    Controller中的每个Handler不再自己处理异常,而是直接throws所有异常。

    定义一个“异常解析器”集中捕获处理所有异常

    此种方案,在集中管理异常方面,更有优势!

    // 异常解析器
    //任何一个Handler中抛出异常
    public class MyExceptionResolver implements HandlerExceptionResolver {
    
        @Override
        public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
            ModelAndView modelAndView = new ModelAndView();
            if (e instanceof MyException1) {
                //errorl.jsp
                modelAndView.setViewName("redirect:/error1.jsp");
            } else if (e instanceof MyException2) {
                //error2.jsp
                modelAndView.setViewName("redirect:/error2.jsp");
            } else if (e instanceof MyException3) {
                //error3.jsp
                modelAndView.setViewName("redirect:/error3.jsp");
            } else if (e instanceof MaxUploadSizeExceededException){
                modelAndView.setViewName("redirect:/uploadError.jsp");
            }
                return modelAndView;
        }
    }
    

    mvc.xml中声明异常解析器

    <!-- 异常解析器 -->
    <bean class="异常解析器类名"/>
    

    九、拦截器

    9.1 作用

    作用:抽取handler中的冗余功能

    9.2 定义拦截器

    执行顺序:preHandle-->postHandle-->afterCompletion

    public class MyInterceptor implements HandlerInterceptor {
    
        //在handler之前执行
        //再次定义 handler中冗余的功能
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            //判断登录状态
            HttpSession session = request.getSession();
            if (session.getAttribute("state") != null) {
                return true;    //放行 执行后续的handler
            }
            //中断之前响应请求
            response.sendRedirect("/login.jsp");
            return false;   //中断请求,不再执行后续的handler
    
        }
    
        //在handler之后执行 响应之前执行
        // 改动请求中的数据
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("post Handle");
        }
    
        //在视图渲染完毕后
        //资源回收
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("after completion");
        }
    }
    

    9.3 配置拦截路径

    mvc.xml中添加:

    <!-- 拦截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--            <mvc:mapping path="/inter/test1"/>-->
            <!--            <mvc:mapping path="/inter/test2"/>-->
            <mvc:mapping path="/inter/*"/>
            <mvc:exclude-mapping path="/inter/login"/>
            <bean class="拦截器类名"/>
        </mvc:interceptor>
    </mvc:interceptors>
    

    十、上传

    10.1 导入依赖

    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.4</version>
    </dependency>
    
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.3</version>
        <exclusions>
            <exclusion>
                <groupId>javax.servlet</groupId>
                <artifactId>servlet-api</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    

    10.2 上传表单

    <form action="${pageContext.request.contextPath}/upload/test1" method="post" enctype="multipart/form-data">
        file: <input type="file" name="source"><br>
        <input type="submit" value="上传">
    </form>
    

    10.3 上传解析器

    <!-- 
    上传解析器 id必须是"multipartResolver"
    -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 最大可上传的文件大小 单位byte 超出后会抛出异常 -->
        <property name="maxUploadSize" value="1048576"/>
    </bean>
    

    10.4 Handler

    @Controller
    @RequestMapping("/upload")
    public class UploadController {
    
        @RequestMapping("/test1")
        public String test1(MultipartFile source, HttpSession session) throws IOException {
            System.out.println("test1");
            //获取上传文件的名称
            String filename = source.getOriginalFilename();
            //获取上传文件的类型
            String contentType = source.getContentType();
    
            //生成一个唯一的文件名
            String uniqueFilename = UUID.randomUUID().toString();
            //获取文件后缀
            String extension = FilenameUtils.getExtension(filename);
            //拼接唯一文件名
            String uniqueFilename2 = uniqueFilename + "." + extension;
    
            System.out.println(filename);
            System.out.println(contentType);
    
            //保存文件
    //        source.transferTo(new File("d:/abd.img"));
            String realPath = session.getServletContext().getRealPath("/upload");
            System.out.println("realpath" + realPath);
            source.transferTo(new File(realPath + "\" + uniqueFilename2));
            return "index";
        }
    }
    

    十一、下载

    11.1 超链

    <a href="${pageContext.request.contextPath}/download/test1?name=c93d8b03ecf53.jpg">下载</a>
    

    11.2 Handler

    @Controller
    @RequestMapping("/download")
    public class DownloadController {
    
        @RequestMapping("/test1")
        public void test1(String name, HttpSession session, HttpServletResponse response) throws IOException {
            String realPath = session.getServletContext().getRealPath("/upload");
            String filePath = realPath + "\" + name;
    
            //设置响应头告知浏览器,要以附件的形式保存内容 filename=浏览器显示的下载文件名
            response.setHeader("content-disposition", "attachment;filename=" + name);
    
            //响应过程
            IOUtils.copy(new FileInputStream(filePath), response.getOutputStream());
    
        }
    }
    

    十二、验证码

    12.1 导入依赖

    <!-- Kaptcha -->
    <dependency>
        <groupId>com.github.penggle</groupId>
        <artifactId>kaptcha</artifactId>
        <version>2.3.2</version>
        <exclusions>
            <exclusion>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    

    12.2 声明验证码组件

    web.xml中添加:

    <!-- 验证码组件 -->
    <servlet>
        <servlet-name>cap</servlet-name>
        <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
    
        <!-- 验证码的图片是否有边框 -->
        <init-param>
            <param-name>kaptcha.border</param-name>
            <param-value>no</param-value>
        </init-param>
    
        <!-- 验证码字符长度 -->
        <init-param>
            <param-name>kaptcha.textproducer.char.length</param-name>
            <param-value>4</param-value>
        </init-param>
    
        <!-- 验证码字符集 -->
        <init-param>
            <param-name>kaptcha.textproducer.char.string</param-name>
            <param-value>abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</param-value>
        </init-param>
    
        <!-- 图片底色 -->
        <init-param>
            <param-name>kaptcha.backgroud.clear.to</param-name>
            <param-value>211,229,237</param-value>
        </init-param>
    
        <!-- 将验证码存入session -->
        <init-param>
            <!--session.setAttribute("captchd","验证码")-->
            <param-name>kaptcha.session.key</param-name>
            <param-value>captcha</param-value>
        </init-param>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>cap</servlet-name>
        <!-- 生成验证码的路径 -->
        <url-pattern>/captcha</url-pattern>	
    </servlet-mapping>
    

    十三、REST

    13.1 开发风格

    是一种开发风格,遵从此风格开发软件,符合REST风格,则RESTFUL。

    两个核心要求:

    • 每个资源都有唯一的标识(URL)
    • 不同的行为,使用对应的http-method
    访问标识 资源
    http://localhost:8989/xxx/users 所有用户
    http://localhost:8989/xxx/users/1 用户1
    http://localhost:8989/xxx/users/1/orders 用户1的所有订单
    请求方式 标识 意图
    GET http://localhost:8989/xxx/users 查询所有用户
    POST http://localhost:8989/xxx/users 在所有用户中增加一个
    PUT http://localhost:8989/xx/users 在所有用户中修改一个
    DELETE http://localhost:8989/xxx/users/1 删除用户1
    GET http://localhost:8989/xxx/users/1 查询用户1
    GET http://localhost:8989/xxx/users/1/orders 查询用户1的所有订单
    POST http://localhost:8989/xxx/users/1/orders 在用户1的所有订单中增加一个

    13.2 使用

    13.2.1定义Rest风格的Controller

    @RequestMapping(value="/users",method=RequestMethod.GET)

    等价于

    @GetMapping("/users")

    /**
     * 查询:所有用户 id=xx 某一个用户
     * 删除:id=xx 某一个用户
     * 增加:在所有用户中 增加一个
     * 修改:在所有用户中 修改一个
     * <p>
     * 资源:所有用户 /users
     * id=xx 某一个用户 /users/{id}
     */
    @RestController()
    public class MyRestController {
    
        @GetMapping("/users")
        public List<User> queryUsers() {
            System.out.println("query users with get");
            User user = new User(1, "张三");
            User user1 = new User(2, "李四");
            return Arrays.asList(user, user1);
        }
    
        @GetMapping("/users/{id}")
        public User queryOne(@PathVariable Integer id) {
            System.out.println("query one user with get:" + id);
            return new User(1, "张三");
        }
    
        @DeleteMapping("/users/{id}")
        public String deleteOne(@PathVariable Integer id) {
            System.out.println("delete one user with delete:" + id);
            return "ok";
        }
    
        @PostMapping("/users")
        public String saveUser(@RequestBody User user) {
            System.out.println("save user with post:" + user);
            return "ok";
        }
    
        @PutMapping("/users")
        public String updateUser(@RequestBody User user) {
            System.out.println("update user with put:" + user);
            return "ok";
        }
    }
    

    13.2.2 Ajax

    <input type="button" value="queryALL" onclick="queryAll();">
    <input type="button" value="queryOne" onclick="queryOne();">
    <input type="button" value="saveUser" onclick="saveUser();">
    <input type="button" value="updateUser" onclick="updateUser();">
    <input type="button" value="deleteUser" onclick="deleteUser();">
    <script>
        function queryAll() {
            $.ajax({
                type: "get",
                url: "${pageContext.request.contextPath}/users",
                success: function (ret) {
                    console.log("查询所有:");
                    console.log(ret);
                }
            });
        }
    
        function queryOne() {
            $.ajax({
                type: "get",
                url: "${pageContext.request.contextPath}/users/100",
                success: function (ret) {
                    console.log("查询单个用户");
                    console.log(ret);
                }
            });
        }
    
        function saveUser() {
            var user = {name: "shine", birth: "2020-12-12 12:12:20"}
            $.ajax({
                type: "post",
                url: "${pageContext.request.contextPath}/users",
                data: JSON.stringify(user),
                contentType: "application/json",
                success: function (ret) {
                    console.log("增加用户");
                    console.log(ret);
                }
            });
        }
    
        function updateUser() {
            var user = {id: 1, name: "shine2", birth: "2020-12-13 12:12:20"}
            $.ajax({
                type: "put",
                url: "${pageContext.request.contextPath}/users",
                data: JSON.stringify(user),
                contentType: "application/json",
                success: function (ret) {
                    console.log("更新用户");
                    console.log(ret);
                }
            });
        }
    
        function deleteUser() {
            $.ajax({
                type: "delete",
                url: "${pageContext.request.contextPath}/users/200",
                success: function (ret) {
                    console.log("删除用户");
                    console.log(ret);
                }
            });
        }
    </script>
    

    十四、跨域请求

    14.1 域

    域:协议+IP+端口

    14.2 Ajax跨域问题

    Ajax发送请求时,不允许跨域,以防用户信息泄露。

    当Ajax跨域请求时,响应会被浏览器拦截(同源策略),并报错。即浏览器默认不允许ajax跨域得到响应内容。

    互相信任的域之间如果需要ajax访问,(比如前后端分离项目中,前端项目和后端项目之间),则需要额外的设置才可正常请求。

    14.3 解决方案

    允许其他域访问

    在被访问方的Controller类上,添加注解

    @Crossorigin("http://localhost:8080")//允许此域发请求访问
    
    • 携带对方cookie,使得session可用
    • 在访问方,ajax中添加属性:withCredentials:true

    十五、SpringMVC执行流程

    十六、Spring整合

    16.1 整合思路

    此时项目中有两个工厂

    • DispatcherServlet启动的springMVC工厂=负责生产C及springMVC自己的系统组件
    • ContextLoaderListener启动的spring工厂==负责生产其他所有组件
    • springMVC的工厂会被设置为spring工厂的子工厂,可以随意获取spring工厂中的组件
    • 整合过程,就是累加:代码+依赖+配置。然后将service注入给controller即可

    16.2 整合技巧

    两个工厂不能有彼此侵入,即,生产的组件不能有重合。

    mvc.xml中添加:

    <!--
    告知SpringMVC哪些包中存在被注解的类
    use-default-filters=true 凡是被 @Controller @Service @Repository 注解的类,都会被扫描
    use-default-filters=false 默认不扫描包内的任何类,只扫描include-filter中指定的类
    只扫描被@Controller注解的类
    -->
    <context:component-scan base-package="com.techoc" use-default-filters="false">
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        </context:component-scan>
    

    applicationContext.xml中添加:

    <!--
    告知Spring
    唯独不扫描eController注解的类
    -->
    <context:component-scan base-package="com.techoc" use-default-filters="true">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    
  • 相关阅读:
    iOS之POST与GET的优缺点
    iOS之设置头像(访问系统相册、本地上传)
    iOS之清除缓存
    iOS之自动调节输入文本框的高度
    iOS之隐藏键盘的方式
    iOS之关于 srand() 和rand()
    Android Studio移除模块
    Android 弹出输入框
    webApi添加视图出现/Index.cshtml”处的视图必须派生自 WebViewPage 或 WebViewPage<TModel>。
    JSON Web Tokens简单学习
  • 原文地址:https://www.cnblogs.com/techoc/p/14213311.html
Copyright © 2011-2022 走看看