Spring MVC(一)

发布于:2022-11-09 ⋅ 阅读:(12) ⋅ 点赞:(0) ⋅ 评论:(0)

一、SpringMVC

1、MVC

M:Model,模型层,指工程中的JavaBean,作用是处理数据

JavaBean分为两类:

  • 一类称为实体类Bean:专门存储业务数据的,如 Student、User 等

  • 一类称为业务处理 Bean:指 Service 或 Dao 对象,专门用于处理业务逻辑和数据访问。

V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据

C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器

MVC的工作流程:  用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器

2、SpringMVC概念

SpringMVC是spring框架的一个模块,springmvc和spring无需通过中间整合层进行整合。是一个WEB框架,基于MVC的理念而设计,是目前最主流的MVC框架之一。MVC的三个核心部件分别是:Model(模型)、View(视图)、Controller(控制器)。SpringMVC是Spring框架的一个模块,SpringMVC和Spring无需通过中间整合层进行整合。

  • SpringMVC3.0之后的版本超越了Struts2,成为最优秀的web框架。

  • SpringMVC通过MVC注解,让POJO成为处理请求的控制器,而且它不需要实现任何额外的接口。

  • SpringMVC支持REST风格的URL请求。

  • SpringMVC和其他MVC框架相比,具有更高的灵活性和可扩展性。

3、原理

客户端(浏览器)发送请求,直接请求到DispatcherServlet。

  1. DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler。

  2. 解析到对应的Handler后,开始由HandlerAdapter适配器处理。

  3. HandlerAdapter会根据Handler来调用真正的处理器开处理请求,并处理相应的业务逻辑。

  4. 处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是个逻辑上的View。

  5. ViewResolver会根据逻辑View查找实际的View。

  6. DispaterServlet把返回的Model传给View。

  7. 通过View返回给请求者(浏览器)

4、优点

  • 利用一个Controller类, 完成某项业务的全部调用

  • 简化了配置

  • 参数获取非常灵活

5、组件

前端控制器

DispatcherServlet:Spring提供的前端控制器,所有的请求都有经过它来统一分发。在DispatcherServlet将请求分发给Spring Controller之前,需要借助于Spring提供的HandlerMapping定位到具体的Controller。

处理器映射器

HandlerMapping:它的作用是请求派发,负责请求和控制器建立对应的关系。它是由 DispatcherServlet 调用,DispatcherServlet 会从容器中取出所有 HandlerMapping 实例并遍历,让 HandlerMapping 实例根据自己实现类的方式去尝试查找 Handler。也就是说,DispatcherServlet要将一个请求交给哪个特定的Controller,它需要咨询一个Bean,这个Bean的名字为“HandlerMapping”。

控制器

Controller:负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model 返回给对应的View 进行展示。

封装数据信息和视图信息的模型

ModelAndView:使用ModelAndView类用来存储处理完后的结果数据,以及显示该数据的视图。从名字上看ModelAndView中的Model代表模型,View代表视图,这个名字就很好地解释了该类的作用。业务处理器调用模型层处理完用户请求后,把结果数据存储在该类的model属性中,把要返回的视图信息存储在该类的view属性中,然后让该ModelAndView返回该Spring MVC框架。框架通过调用配置文件中定义的视图解析器,对该对象进行解析,最后把结果数据显示在指定的页面上。      

视图解析器

ViewResolver 的主要作用是把一个逻辑上的视图名称解析为一个真正的视图。

6、三层架构

dao->数据层

service -> 业务实现

web  ->  web 接口

或者

common—>公共方法,公共类,工具类,只有关键地方加日志

dao->数据层,不加日志

domain->实体类,不加日志

rpc->调用接口,出入参加日志

sdk->  给外部提供sdk,例如对外接口,只是提供接口定义,该层没有业务逻辑,真实的sdk逻辑实现是在service层,不加日志

service -> 业务实现逻辑,单元测试进行层,日志接入:关键地方可以加, 实现sdk层的接口方法出入参加日志

web  ->  web 接口,对外提供http 接口,一般都有逻辑,http 是直接对 c的,看到的ip 是c端用户的,出入参加日志

(1)、service层

业务层,用来实现业务逻辑。能调用dao层或者service层,返回数据对象DO或者业务对象BO,BO通常由DO转化、整合而来,可以包含多个DO的属性,也可以是只包含一个DO的部分属性。通常为了简便,如果无需转化,service也可以直接返回DO。外部调用(HTTP、RPC)方法也在这一层,对于外部调用来说,service一般会将外部调用返回的DTO转化为BO。是专注业务逻辑,对于其中需要的数据库操作,都通过Dao层去实现。主要去负责一些业务处理,比如取得连接、关闭数据库连接、事务回滚,一些复杂的逻辑业务处理就放到service层。

(2)、DAO层

负责访问数据库进行数据的操作,取得结果集,之后将结果集中的数据取出封装到VO类对象之后返回给service层。数据层,直接进行数据库的读写操作,返回数据对象DO,DO与数据库表一一对应。Dao的作用是封装对数据库的访问:增删改查,不涉及业务逻辑,只是达到按某个条件获得指定数据的要求。

在实际开发中dao层要先定义出自己的操作标准即标准接口,就是为了解耦合。

(3)、Cotroller层

控制层,主要的功能是处理用户发送的请求。主要处理外部请求。调用service层,将service层返回的BO/DO转化为DTO/VO并封装成统一返回对象返回给调用方。如果返回数据用于前端模版渲染则返回VO,否则一般返回DTO。不论是DTO还是VO,一般都会对BO/DO中的数据进行一些转化和整合,比如将gender属性中的0转化“男”,1转化为“女”等。controller的功能主要有5点:参数校验、调用service层接口实现业务逻辑、转换业务/数据对象、组装返回对象、异常处理。

(4)、View层

又叫显示层,主要是负责前台jsp页面的表示

二、HelloSpringMVC

使用注解开发SpringMVC程序

导入依赖

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

<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/jsp-api -->
<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2.1-b03</version>
    <scope>provided</scope>
</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>

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

编写Web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <display-name>Archetype Created Web Application</display-name>
    <!--配置DispachServlet-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/hellomvc</url-pattern>
    </servlet-mapping>
</web-app>

编写springmvc-servlet.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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">

    <!-- 添加处理映射器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <!-- 添加处理器适配器 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    <!--视图解析器:DispatcherServlet给他的ModeLAndView-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver"/>
    <!--前缀-->
    <!--前缀-->
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <!--后缀-->
    <property name="suffix" value=".jsp"/>

        <!-- handler -->
    <bean name="/hello" class="com.biibili.spring.HelloController" />
</beans>

编写HelloController

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//注意: 这里我们先导入Controller接口
public class HelloController implements Controller {
    @Override
    public ModelAndView handleRequest (HttpServletRequest request, HttpServletResponse response) throws Exception {
        //ModeLAndView模型和视图
        ModelAndView mv = new Mode1AndView();
        //封装对象,放在ModeLAndView中
        mv.addobject("msg","HelloSpringMVC!");
        //封装要跳转的视图,放在ModelAndView中
        mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp
        return mv;
    }
}

编写hello.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>springmvc</title>
  </head>
  <body>
  $END$
  </body>
</html>

三、RestFul

1、概念

一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

2、URL定义

资源:互联网所有的事物都可以被抽象为资源   资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。   分别对应 添加、 删除、修改、查询。

3、传统方式操作资源 

http://127.0.0.1/item/queryUser.action?id=1   查询,GET

http://127.0.0.1/item/saveUser.action             新增,POST

http://127.0.0.1/item/updateUser.action          更新,POST

http://127.0.0.1/item/deleteUser.action?id=1  删除,GET或POST

4、使用RestFul操作资源 

【GET】 /users # 查询用户信息列表

【GET】 /users/1001 # 查看某个用户信息

【POST】 /users # 新建用户信息

【PUT】 /users/1001 # 更新用户信息(全部字段)

【PATCH】 /users/1001 # 更新用户信息(部分字段)

【DELETE】 /users/1001 # 删除用户信息

四、重定向和转发

1、重定向

客户发送一个请求到服务器,服务器匹配servlet,这都和请求转发一样,servlet处理完之后调用了sendRedirect()这个方法,这个方法是response的方法,所以,当这个servlet处理完之后,看到response.senRedirect()方法,立即向客户端返回这个响应,响应行告诉客户端你必须要再发送一个请求,去访问test.jsp,紧接着客户端受到这个请求后,立刻发出一个新的请求,去请求test.jsp,这里两个请求互不干扰,相互独立,在前面request里面setAttribute()的任何东西,在后面的request里面都获得不了。可见,在sendRedirect()里面是两个请求,两个响应。

redirect重定向的三种方式:

(1)、response.sendRedirect重定向跳转

@RequestMapping(value="/testredirect",method = { RequestMethod.POST, RequestMethod.GET })  
public ModelAndView testredirect(HttpServletResponse response){      
response.sendRedirect("/index");     return null; }

(2)、ViewResolver直接跳转

不带参数

@RequestMapping(value="/testredirect",method = { RequestMethod.POST, RequestMethod.GET })  
public  String testredirect(HttpServletResponse response){  
    return "redirect:/index";  
} 

带参数

@RequestMapping("/testredirect")
public String testredirect(Model model, RedirectAttributes attr) {
	attr.addAttribute("test", "51gjie");//跳转地址带上test参数
    attr.addFlashAttribute("u2", "51gjie");//跳转地址不带上u2参数
	return "redirect:/user/users";
}
  • 使用RedirectAttributes的addAttribute方法传递参数会跟随在URL后面,如上代码即为http:/index.action?test=51gjie

  • 使用addFlashAttribute不会跟随在URL后面,会把该参数值暂时保存于session,待重定向url获取该参数后从session中移除,这里的redirect必须是方法映射路径,jsp无效。你会发现redirect后的jsp页面中b只会出现一次,刷新后b再也不会出现了,这验证了上面说的,b被访问后就会从session中移除。对于重复提交可以使用此来完成.

  • spring mvc设置下RequestMappingHandlerAdapter 的ignoreDefaultModelOnRedirect=true,这样可以提高效率,避免不必要的检索。

(3)、ModelAndView重定向

不带参数

@RequestMapping(value="/restredirect",method = { RequestMethod.POST, RequestMethod.GET })  
public  ModelAndView restredirect(String userName){  
    ModelAndView  model = new ModelAndView("redirect:/main/index");    
    return model;  
}

带参数

@RequestMapping(value="/toredirect",method = { RequestMethod.POST, RequestMethod.GET })  
public  ModelAndView toredirect(String userName){  
    ModelAndView  model = new ModelAndView("/main/index");   
    model.addObject("userName", userName);  //把userName参数带入到controller的RedirectAttributes
    return model;  
}

2、转发

(1)、ModelAndView

设置ModelAndView对象 , 根据view的名称 , 和视图解析器跳到指定的页面 .

页面 : {视图解析器前缀} + viewName +{视图解析器后缀}

<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
      id="internalResourceViewResolver">
    <!-- 前缀 -->
    <property name="prefix" value="/WEB-INF/jsp/" />
    <!-- 后缀 -->
    <property name="suffix" value=".jsp" />
</bean>

对应的controller类

public class ControllerTest1 implements Controller {
 
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        //返回一个模型视图对象
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","ControllerTest1");
        mv.setViewName("test");
        return mv;
    }
}

(2)、ServletAPI

通过设置ServletAPI , 不需要视图解析器 .

1、通过HttpServletResponse进行输出

2、通过HttpServletResponse实现重定向

3、通过HttpServletResponse实现转发

@Controller
public class ResultGo {
 
    @RequestMapping("/result/t1")
    public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
        rsp.getWriter().println("Hello,Spring BY servlet API");
    }
 
    @RequestMapping("/result/t2")
    public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
        rsp.sendRedirect("/index.jsp");
    }
 
    @RequestMapping("/result/t3")
    public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
        //转发
        req.setAttribute("msg","/result/t3");
        req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
    }
 
}

(3)、SpringMVC

通过SpringMVC来实现转发和重定向 - 无需视图解析器;

测试前,需要将视图解析器注释掉

@Controller
public class ResultSpringMVC {
    @RequestMapping("/rsm/t1")
    public String test1(){
        //转发
        return "/index.jsp";
    }
 
    @RequestMapping("/rsm/t2")
    public String test2(){
        //转发二
        return "forward:/index.jsp";
    }
 
    @RequestMapping("/rsm/t3")
    public String test3(){
        //重定向
        return "redirect:/index.jsp";
    }
}

通过SpringMVC来实现转发和重定向 - 有视图解析器;

重定向 , 不需要视图解析器 , 本质就是重新请求一个新地方嘛 , 所以注意路径问题.

可以重定向到另外一个请求实现

@Controller
public class ResultSpringMVC2 {
    @RequestMapping("/rsm2/t1")
    public String test1(){
        //转发
        return "test";
    }
 
    @RequestMapping("/rsm2/t2")
    public String test2(){
        //重定向
        return "redirect:/index.jsp";
        //return "redirect:hello.do"; //hello.do为另一个请求/
    }
}

五、数据处理

1、处理提交数据

(1)、提交的域名称和Controller处理方法的参数名一致

提交数据 : http://localhost:8080/SpringMVC/test/t?name=张三

处理方法 :

@Controller
@RequestMapping("test")
public class RedirectController {

    @RequestMapping("t")
    public String Test3(String name){

        return "/test";
    }
}

(2)、提交的域名称和Controller处理方法的参数名不一致

通过@RequestParam映射,提交数据 : http://localhost:8080/SpringMVC/test/t?username=张三

处理方法 :

@Controller
@RequestMapping("test")
public class RedirectController {

    @RequestMapping("t")
    public String Test3(@RequestParam("username")String name){

        return "/test";
    }
}

(3)、提交的是一个对象

要求提交的表单域和对象的属性名一致  , 参数使用对象即可

1、实体类

public class User {
    private int id;
    private String name;
    private int age;
    //构造
    //get/set
    //tostring()
}

2、提交数据  : http://localhost:8080/SpringMVC/test/t?name=张三&sex=男&age=18

3、处理方法 :

@RequestMapping("test")
public String user(User user){
    System.out.println(user);
    return "/test";
}

2、数据显示到前端

ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转;

ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;

Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;

(1)、通过ModelAndView

@RequestMapping("/hello")
public String hello(@RequestParam("username") String name, ModelMap model){
   //封装要显示到视图中的数据
   //相当于req.setAttribute("name",name);
   model.addAttribute("name",name);
   System.out.println(name);
   return "hello";
}

(2)、ModelMap

@RequestMapping("/hello")
public String hello(@RequestParam("username") String name, ModelMap model){
    //封装要显示到视图中的数据
    //相当于req.setAttribute("name",name);
    model.addAttribute("name",name);
    System.out.println(name);
    return "hello";
}

(3)、Model

@RequestMapping("/ct2/hello")
public String hello(@RequestParam("username") String name, Model model){
   //封装要显示到视图中的数据
   //相当于req.setAttribute("name",name);
   model.addAttribute("msg",name);
   System.out.println(name);
   return "test";
}