spring mvc

简介

springmvc是一个web层mvc框架
model           模型
view            视图
controller      控制器

这是一种设计模式,将责任进行拆分,不同的组件负责不同的事情

优点:
    结构清晰
    更好维护(大量使用JSP的年代)

缺点:
    更加复杂了

入门体验

1.创建wen项目
2.编写web.xml,在其中注册一个特殊的servlet,前端控制器
3.编写一个springmvc的配置文件
    注册一个视图解析器
4.编写一个控制器
5.编写一个结果页面

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.dumbo</groupId>
    <artifactId>SpringMVCDemo3</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>

</project>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <servlet>
        <servlet-name>springmvcdemo3</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>springmvcdemo3</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

视图解析器

<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 class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--前缀-->
        <property name="prefix" value="/jsp/"/>

        <!--后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>

    <bean class="com.dumbo.controller.HelloController" name="/helloController">

    </bean>
</beans>

控制器

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

//实现一个接口的方式
public class HelloController implements Controller {
    public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("girl","菲菲");
        modelAndView.setViewName("girl");
        return modelAndView;
    }
}

组件分析

# web.xml
    注册前端控制器,目的在于,我们希望让springmvc去处理所有的请求
    通过
    <!--servlet映射配置-->
    <servlet-mapping>
        <servlet-name>springmvcdemo3</servlet-name>
        <!--这里统一用斜扛-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    处理绝大部分的请求
    
    urlPattern的写法
        1.  /
        2.  /*      (永远不要这样写)
        3.  *.do    ()
    
    /*不能写的原因
        请求/helloController过去的时候,他的仕途名称是girl,girl.jsp页面,
        它将其当做一个叫girl.jsp的请求,尝试去匹配对应的controller,所以无法匹配,导致404.
    *.do
        这种方式,是有些团队此故事呢将请求的行为加个小尾巴进行区分,do *.action
    /
        处理所有的请求,但是和/.*不一样,他处理完之后要出去的时候不会将这个girl.jsp当做一个新的请求,将这个渲染的结构直接返回浏览器

关于前端控制器的解释

springmvc设计的里面是希望开发者尽量远离原生的servlet API,API不是很好用,比较繁琐,讲操作进一步简化,它将很多东西责任进行了拆分,不希望我们将一些技术点绑定死,可以做到随意的切换,本身还是基于servlet设计的,分发的servlet

springmvc配置文件,名字的问题

默认情况下是用dispatcherServlet的名字当做命名空间,
[servletName]-servlet.xml(WEB-INF下寻找)
[servletName]-servlet = namespace

<!--可以重新声明配置文件的名字-->
<init-param>
    <param-name>namespace</param-name>
    <param-value>mvc</param-value>
</init-param>

重新指定上下文配置文件的位置即可
<init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springmvcdemo3-servlet.xml</param-value>
</init-param>
在类路径下寻找springmvcdemo3-servlet.xml这个配置文件

视图解析器

spring支持多种视图技术
    jsp
    Freemaker
内部的资源解析器
    视图前缀
        /jsp/它是我们的请求响应的资源的路径的配置 viewName:girl     /jsp/girl
    后缀
        .jsp    此时我们的前缀+视图名称+后缀=/jsp/girl.jsp
        
request.getDS.forward(request,response)的作用是一样一样的
    
物理视图由逻辑视图转换而来
物理视图是 webapp/jsp/girl.jsp

逻辑视图
    prefix
    logicViewName
    suffix
    p View = prefix + logicViewName + suffix

控制器

如果要给接口只有一个方法,这种接口叫做函数式接口

@Nullable
    ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;

它像谁?
    servlet里面由doGet,doPost里面入参就是请求与响应
    
    viod    无返回值
    
    springmvc 设计为 ModelAndView 为返回值
    在model当中填充数据,然后在具体的视图进行展示
    还需要在配置文件当中配置一下这个bean,要取个名字,就用来充当了这个请求的uri
    <bean class="com.dumbo.controller.HelloController" name="/helloController"></bean>
    name是必须的
    它就处理一个请求,跟servlet差别不是很大

注解开发模式

基本注解
    @Controller
    @RequestMapping
    
开发步骤
    配置一下基础扫描包:
        <!--配置一个注解扫描包-->
        <context:component-scan base-package="com.dumbo.controller"></context:component-scan>
    在指定的类上面添加@Controller注解
    添加@RequestMapping 类似于前面的Controller的那个name
    
当我们写上Controller之后,就标记了它为spring的一个组件,并且是控制器的组件,此时我们的handlermapping会去扫描寻找这个Controller是否与之匹配,如果发现匹配就把这里处理的工作交给它

匹配的规则
    具体的匹配就是通过请求的路径
    @RequestMapping(URI)
    此时就是通过URI进行匹配
    @RequestMapping可以写在方法上也可以写在类上
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

//不要继承任何类和实现任何接口
@Controller
public class ByeController {
//    ModelAndView Model + View
    @RequestMapping("/bye")
    public  String bye(Model model){
        model.addAttribute("model","modeller");
//        这里return的就是我们的那么viewName
//        此时去的是/jsp/bye.jsp页面
        return "bye";
    }
}

转发与重定向

转发到页面              默认到选项
重定向到页面            redirect:path
转发到另一个控制器      forward:path
package com.dumbo.controller;

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

@Controller
@RequestMapping("/hello")
public class HelloController {
//    转发
    @RequestMapping("/forward")
    public String forward(Model model){
//    springmvc model 默认上在请求域当中存储值
        model.addAttribute("skill","睡觉");
        System.out.println("shuijiao");
        return "forward";
    }

    @RequestMapping("/redirect")
    public String redirect(Model model){
        model.addAttribute("skill","煮饭");
        System.out.println("zhufan");
//        不带斜扛的写法上一个相对路径,根据当期上下文决定
//        如果上重定向,就跟视图解析的规则无关
        return "redirect:/jsp/redirect.jsp";
    }

    @RequestMapping("/forword2")
    public String forwardAnotherController(){
        return "forward:/baby/hello";
    }
}

关于springmvc 访问web元素

request
session
application

请求路径永远写 /

可以通过模拟的对象完成操作,也可以使用原生的Servlet API完成,直接在方法中入参即可
package com.dumbo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.WebRequest;

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

@Controller
@RequestMapping("/web")
public class WebElementController {
    @RequestMapping("/request")
    public String request(WebRequest request){
        System.out.println(request.getParameter("girl"));
        return "forward";
    }
    @RequestMapping("/request2")
    public String request2(HttpServletRequest request){
        System.out.println(request.getParameter("boy"));
        return "forward";
    }

    @RequestMapping("/request3")
    public String request3(HttpSession session){
        session.setAttribute("g","神庙逃亡");
        session.getServletContext().setAttribute("ctx","宇宙我最帅");

        return "redirect:/jsp/redirect.jsp";
    }
}

注解详解

@RequestMapping
    value:写的是路径,是一个数组的形式,可以匹配多个路径
    path:是value的别名,二选一,作用一致
    method:是可以指定可以访问的请求的类型,比如,GET,POST
    params:可以指定参数,你还可以去限定这个参数的特征,必须等于某个值
    header:能够影响浏览器的行为
    consumers:媒体类型,可以限定必须为application/json;charset="UTF-8"
//    请求映射路径path可以写多个值
//    如果没有限定,啥请求都可以
    @RequestMapping(value = {"/m1","/m2"},method = RequestMethod.GET)
    public String m1(){
        System.out.println("m1..");
        return "forward";
    }
//      params描述了,我需要两个参数,一个是boy,一个是girl
    @RequestMapping(value = {"/m2"},params = {"boy=wangfei","girl=guandong"})
    public String m2(){
        System.out.println("m2..");
        return "forward";
    }

修改路径

@WebServlet(urlPatterns = {},loadOnStartup = 2)
public class WebPathInitServlet extends HttpServlet {
    @Override
    public void init(ServletConfig config) throws ServletException {
//        在整体应用上下文当中存储了一个ctx 的值,用它来引用上下文路径
        config.getServletContext().setAttribute("ctx",config.getServletContext().getContextPath());
        super.init(config);
    }
}

${ctx} 调用

关于请求路径的问题

springmvc支持ant风格
    ?       任意的一个字符,斜杠例外
    *       0到任意个字符都可以 不能含斜杠
    **      支持任意层路径  m3/**
//    @RequestMapping(path = {"/m3?"})
//    @RequestMapping(path = {"/m3*"})
    @RequestMapping(path = {"/m3/**"})
    public String m3(){
        System.out.println("m3...");
        return "forward";
    }
@GetMapping @PostMapping
getMapping:限定了只支持get请求
postMapping:限定了只支持post请求
对于非get,post请求的支持
对于非get,post请求的支持,需要有额外的内容添加,要添加一个过滤器额外处理
    <!--注册一个支持所有http请求类型的过滤器-->
    <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>

    表单提交里面还要添加一个隐藏的参数
    
    <form action="${ctx}/web/m5" method="post">
        <input type="hidden" name="_method" value="DELETE">
        <input type="submit" value="提交">
    </form>
关于静态资源访问的问题
由于我们的servlet设置了URL匹配方式为/ 所以,它将静态资源也当做一个后台的请求
他尝试去匹配一个static/css/index.css的Controller里面的RequestMapping的组合,因为没有,所以404
最简单的是 是让springmvc单独处理,将这些交给容器的默认的servlet处理,就不让 DispatcherServlet来处理了

    <!--默认的Servlet处理者-->
    <mvc:default-servlet-handler/>
    <mvc:annotation-driven/>
@PathVariable
restful风格

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

@Controller
@RequestMapping("/product")
public class ProductController {
    @RequestMapping("/add/{id}/{name}/{price}")
    public String addProduct(@PathVariable("id")Integer id,@PathVariable("name")String name,@PathVariable("price")Double price){
        System.out.println(id+name+price);
        return "forward";
    }
}

{id}与@PathVariable("id")Integer id进行绑定
@Responsebody
返回数据的,一般情况下返回json  格式
@SessionAttributes
这个用在类上面,它会将模型自动填充到会话里面去

@SessionAttribute
要求当前这次访问当中的会话里面必须要有某个对象
关于post请求中文乱码问题解决
<filter>
    <filter-name>characterEncodingFilter</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>
    <init-param>
        <param-name>foeceRequestEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
关于form表单提交数据的方式
方式一:
    通过属性名称进行绑定,可以完成数据传递
    页面当中表单元素的那么值要和后台的形参的名字一致
    前台:
        <form action="${context}/user/put" method="post">
            <input type="hidden" name="_method" value="put">
            <input type="text" name="name">
            <input type="password" name="password">
            <input type="submit" value="提交">
        </form>
    后台:
        @PutMapping("/put")
        @ResponseBody
        public String put(String name,String password){
            System.out.println(name + password);
            return "ok";
        }
    
方式二:
    利用@RequestParam
    后台:
        @PutMapping("/put")
        @ResponseBody
        public String put(@RequestParam("name") String name,@RequestParam("password") String password){
            System.out.println(name + password);
            return "ok";
        }

方法三:
    直接利用POJO形式传递
    后台:
        @PutMapping("/put")
        @ResponseBody
        public String put(User user){
            System.out.println(user.getName() + user.getPassword());
            return "ok";
        }
@ModelAttribute
方式一:
    package com.dumbo.controller;

    import com.dumbo.pojo.User;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    @RequestMapping("/user2")
    public class User2Controller {
    //    就是在Controller里面的任意一个处理具体的方法之前都会执行
        @ModelAttribute
        public User init(){
            System.out.println("init....");
            User u = new User();
            u.setName("王菲");
            u.setPassword("123");
            return u;
        }
        @RequestMapping("/login")
        public String login(Model model){
            System.out.println(model.containsAttribute("u"));
            System.out.println(model.containsAttribute("user"));
            return "msg";
        }
    }
    

    如果某些对象从头到尾每次请求当中都要存在,不消失,就适合用

方法二:
    @ModelAttribute("user")
    public void init(Model model){
        System.out.println("init....");
        User user = new User();
        user.setName("王菲");
        user.setPassword("123");
        model.addAttribute("user",user);
    }
    
    如果没有传递这个模型过来,那么方法上加了@ModelAttribute的为你提供,如果传了就用你的
关于form提交日期的解决问题
前台:
    <form action="${context}/user/put" method="post">
        <input type="hidden" name="_method" value="put">
        <input type="text" name="name">
        <input type="password" name="password">
        <input type="date" name="date">
        <input type="submit" value="提交">
    </form>

后台:
    @Controller
    @RequestMapping("/user")
    public class UserController {
    
        @InitBinder("user")
        public void init(WebDataBinder dataBinder){
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            simpleDateFormat.setLenient(true);
            dataBinder.registerCustomEditor(Date.class,new CustomDateEditor(simpleDateFormat,false));
        }
    
        @PutMapping("/put")
        @ResponseBody
        public String put(@ModelAttribute User user){
            System.out.println(user.getName() + user.getPassword());
            System.out.println(user.getDate());
            return "ok";
        }
    }

注解:
    @DateTimeFormat(pattern = "yyyy/MM/dd")
    private Date date;
    
通过initBinder指定了user名字和modelAttribute 里面user绑定

不指定名字,根据数据类型一样可以分析解析转换成功

时间+日期:
    @InitBinder("user")
    public void init(WebDataBinder dataBinder){
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        simpleDateFormat.setLenient(true);
        dataBinder.registerCustomEditor(Date.class,new CustomDateEditor(simpleDateFormat,false));
    }
posted @ 2019-04-03 14:19  漠宇尘  阅读(132)  评论(0编辑  收藏  举报