day61(Spring MVC框架基础1:接受客户端请求,@RequestMapping注解,响应正文,接受请求参数,POJO)

day61(Spring MVC框架基础:接受客户端请求,@RequestMapping注解,响应正文,接受请求参数,POJO)

1. 接收客户端的请求

1.创建Spring MVC工程

  1. 请参考 http://doc.canglaoshi.org/doc/idea_tomcat/index.html创建项目,首次练习的项目名称请使用springmvc01。

  2. 【案例目标】开发使用Spring MVC框架的项目,将其部署到Tomcat,最终,部署此项目启动Tomcat,用户在浏览器中输入指定的URL提交请求后,项目可以对此进行简单的响应

  3. 在pom.xml中添加spring-webmvc依赖项:

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc--><dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.14</version>
    </dependency>
    
    1. 提示:如果后续运行时提示不可识别Servlet相关类,则补充添加以下依赖项:

      <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
      </dependency>
      
    2. 以上代码中的provided表示此依赖不会参与测试或部署,因为当Web项目部署到Tomcat中后,Tomcat环境会包含此依赖项

  4. 准备2个配置类

    1. 一个是Spring框架的配置类:

      package cn.tedu.springmvc.config;
      import org.springframework.context.annotation.Configuration;
      @Configuration // 此注解不是必须的
      public class SpringConfig {
      }
      
    2. 另一个是Spring框架的配置类:

      package cn.tedu.springmvc.config;
      import org.springframework.context.annotation.ComponentScan;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration // 此注解不是必须的
      @ComponentScan("cn.tedu.springmvc") // 必须配置在当前配置类,不可配置在Spring的配置类public class SpringMvcConfig implements WebMvcConfigurer {
      }
      
  5. 需要创建项目的初始化类,此类必须继承自AbstractAnnotationConfigDispatcherServletInitializer,并在此类中重写父类的3个抽象方法,返回正确的值(各方法的意义请参见以下代码中的注释):

    package cn.tedu.springmvc;
    import cn.tedu.springmvc.config.SpringConfig;
    import cn.tedu.springmvc.config.SpringMvcConfig;
    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;/**
    * Spring MVC项目的初始化类
    */
    public class SpringMvcInitializer extends
    AbstractAnnotationConfigDispatcherServletInitializer {@Override
    protected Class<?>[] getRootConfigClasses() {
    // 返回自行配置的Spring相关内容的类
    return new Class[] { SpringConfig.class };
    }
    @Override
    protected Class<?>[] getServletConfigClasses() {
    // 返回自行配置的Spring MVC相关内容的类
    return new Class[] { SpringMvcConfig.class };
    }
    @Override
    protected String[] getServletMappings() {
    // 返回哪些路径是由Spring MVC框架处理的
    return new String[] { "*.do" };
    }
    }
    
  6. 最后,创建控制器类,用于接收客户端的某个请求,并简单的响应结果:

    package cn.tedu.springmvc.controller;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    @Controller // 必须是@Controller,不可以是此前在Spring框架中学习到的其它组件注解
    public class UserController {
    public UserController() {
    System.out.println("UserController.UserController()");
    }
    // http://localhost:8080/springmvc01_war_exploded/login.do
    @RequestMapping("/login.do")
    @ResponseBody
    public String login() {
    return "UserController.login()";
    }
    }
    
  7. 全部完成后,启动项目,会自动打开浏览器并显示主页,在主页的地址栏URL上补充/login.do即可实现访问,并看到结果

  8. 在启动过程中,你还可以在IntelliJ IDEA的控制台中看到控制器类的构造方法中输出的内容

  9. 关于以上案例:

    1. 当启动Tomcat时,会自动将项目打包并部署到Tomcat,通过自动打开的浏览器中的URL即可访问主页,在URL中有很长一段是例如 springmvc01_war_explored 这一段是不可以删除的,其它的各路径必须补充在其之后,例如 /login.do 就必须在此之后
    2. 当启动Tomcat时,项目一旦部署成功,就会自动创建并加载AbstractAnnotationConfigDispatcherServletInitializer的子类,即当前项目中自定义的SpringMvcInitialier,无论这个类放在哪个包中,都会自动创建并加载
    3. 由于会自动调用这个类中所有方法,所以会将Spring MVC框架处理的请求路径设置为*.do,并执行对 cn.tedu.springmvc 的组件扫描,进而会创建 UserController 的对象
    4. 由于在 UserController 中配置的方法使用了 @RequestMapping("/login.do"),则此时还将此方法与/login.do进行了绑定,以至于后续随时访问/login.do时都会执行此方法
  10. 关于以上案例的注意事项:

    1. 注意:组件扫描必须配置在Spring MVC的配置类中
    2. 注意:控制器类上的注解必须是@Controller,不可以是@Component、@Service、@Repository
      1. 各注解的作用并不是注解自身决定的,而是运行环境或框架决定的,在Spring框架中,这4个注解是完全等效的,但是,在部分版本的Spring MVC框架中并不相同
    3. 注意:方法返回的值应该是ASCII码字符,不推荐使用中文、中文标点符号等非ASCII码字符,否则可能显示为乱码
      1. 某些版本的Spring MVC默认的字符编码是ISO-8859-1,只支持ASCII字符
      2. 乱码的问题暂不解决

2. @RequestMapping注解

  1. @RequestMapping注解的主要作用是配置请求路径与处理请求的方法的映射关系,例如将此注解添加在控制器中某个方法之前:

    // http://localhost:8080/springmvc01_war_exploded/login.do
    @RequestMapping("/login.do")
    @ResponseBody
    public String login() {
    return "UserController.login()";
    }
    
    1. 注意:配置的请求路径值必须是初始化类getServletMapping()方法返回值可以匹配的,例如方法返回值是 *.do,则配置的请求路径必须以.do作为后缀
  2. 除了方法之前,此注解还可以添加在控制器类之前,例如:

    @Controller
    @RequestMapping("/user")
    public class UserController {
    }
    
  3. 在类上添加了此注解并配置路径后,每个方法实际映射到的请求路径都是“类上的配置的路径 + 方法上的配置的路径”,例如/user/login.do

  4. 通常,在开发实践中,推荐为每一个控制器类都配置此注解,以指定某个URL前缀

    1. 在使用@RequestMapping配置路径时,无意义的/ 会被忽略,例如在类上配置为 /user/,在方法上配置为 /login.do,则拼接的结果会是/user//login.do,实际有效值为 /user/login.do
    2. 必要的 / 会被补充,例如在类上配置为user,在方法上配置为login.do,实际有效值仍是 /user/login.do
    3. 在开发实践中,应该保持统一风格,例如:无论在类上,还是在方法上,配置的请求路径均以 / 作为第1个字符(某些特殊的URL配置例外)
  5. 在@RequestMapping还可以配置:

    1. method:请求方式
    2. headers:请求头
    3. params:请求参数
    4. consumes:请求文档类型
    5. produces:响应文档类型
  6. 例如,在@RequestMapping注解中,增加配置method属性,可以限制客户端的请求方式:

    @RequestMapping(value = "/login.do"
    , method = RequestMethod.POST)@ResponseBody
    public String login() {
    return "UserController.login()";
    }
    
    
    1. 如果按照以上代码,则/login.do路径只能通过POST方式发起请求才可以被正确的处理,如果使用其它请求方式(例如GET),则会导致HTTP的405错误。
  7. 在部分版本的Spring MVC框架中,响应结果不会自动添加响应头的文档类型,可通过配置@RequestMapping的produces属性显式的添加,以解决中文乱码问题

    @RequestMapping(value = "/login.do"
    ,
    produces="text/html; charset=utf-8")
    @ResponseBody
    public String login() {
    return "UserController.login()";
    }
    
    
    1. 可以在类上进行此配置,则类中每个处理请求的方法均应用此配置
  8. 如果没有配置method属性,则表示可以使用任何请求方式,包括:GET,HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRAC

  9. 另外,Spring MVC框架还提供了@RequestMapping的相关注解,例如:

    1. @GetMapping –
    2. @PostMapping –
    3. @PutMapping –
    4. @DeleteMapping 等等
  10. @GetMapping等注解是已经限制了请求方式的注解

    1. 以@GetMapping为例,限制了请求方式必须是GET,除此以外,使用方式与@RequestMapping完全相同
  11. 在开发实践中,在的上方肯定使用@RequestMapping(其它的@XxxMapping不可以加在类上),方法上一般都使用@GetMapping、@PostMapping等注解,除非在极特殊的情况下,某些请求同时允许多种请求方式,才会在方法上使用@RequestMapping

2.小结

  • 使用@RequestMapping的主要作用是配置请求路径
  • @RequestMapping可以添加在类上,也可以添加在方法上,同时配置时,最终的URL是由类上的与方法上的配置值组合的
  • 当需要限制请求方式为某1种时,应该在处理请求的方法上使用@GetMapping、@PostMapping等注解
  • 在开发实践中,通常:
    • 在类上使用@RequestMapping,配置请求路径,并配置produces以指定响应的文档类型
    • 在方法上使用@GetMapping、@PostMapping等限制了请求类型的注解,配置请求路径

3.响应正文

1.概念

  1. @ResponseBody注解表示:响应正文
  2. 一旦配置为“响应正文” ,则处理请求的方法的返回值就会直接响应到客户端去
  3. 如果没有配置为“响应正文” ,则处理请求的方法的返回值表示“视图组件的名称” ,当方法返回后,服务器端并不会直接响应,而是根据“视图组件的名称”在服务器端找到对应的视图组件,并处理,最后,将处理后的视图响应到客户端去,这不是前后端分离的做法

2.用法

  1. @ResponseBody注解可以添加在方法上,则仅作用于当前方法,也可以添加在类上,则作用于当前类的所有方法
  2. 在Spring MVC框架中,还提供了@RestController注解,它同时具有@Controller和@ResponseBody注解的效果,所以,在响应正文的控制器上,只需要使用@RestController即可,不必再添加@Controller和@ResponseBody注解。
  3. Spring MVC内置了一系列的转换器(Converter),用于将方法的返回值转换为响应到客户端的数据(并补充其它必要数据),并且,SpringMVC会根据方法的返回值不同,自动选取某个转换器,例如,当方法的返回值是String时,会自动使用StringHttpMessageConverter,这个转换器的特点就是直接将方法返回的字符串作为响应的正文,并且,在许多版本的Spring MVC框架中,其默认的响应文档的字符集是ISO-8859-1,所以在在许多版本的Spring MVC中响应String正文时默认不支持非ASCII字符(例如中文)

3.响应JSON格式的正文

  1. 在开发实践中,不会使用String作为处理请求的方法的返回值类型,主要是因为普通的字符串不足以清楚的表现多项数据,通常建议使用XML或JSON语法来组织数据

  2. 主流的做法是向客户端响应JSON格式的字符串,需要在项目中添加jackson-databind的依赖项:

    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.3</version>
    </dependency>
    
    
  3. 以上jackson-databind依赖项中也有一个转换器,当SpringMVC调用的处理请求的方法的返回值是Spring MVC没有匹配的默认转换器时,会自动使用jackson-databind的转换器,而jackson-databind转换器就会解析方法的返回值,并将其处理为JSON格式的字符串,在响应头中将Content-Type设置为application/json

  4. 注意:在Spring MVC项目中,还需要在Spring MVC的配置类上添加@EnableWebMvc注解,否则响应时将导致出现HTTP的406错误。

  5. 示例代码(返回值类型):

    public class UserVO {
    private String username;
    private String password;
    private String email;
    // 请自行补充以上3个属性的Setter & Getter
    }
    
    
  6. 注意:jackson-databind会自动调用属性的Setter / Getter方法

    // http://localhost:8080/springmvc01_war_exploded/user/info.do@GetMapping("/info.do")
    public UserVO info() {
    UserVO userVO = new UserVO();
    userVO.setUsername("chengheng");
    userVO.setPassword("1234567890");
    userVO.setEmail("chengheng@qq.com");
    return userVO;
    }
    
    
  7. 示例代码(Spring MVC配置类):

    @Configuration
    @EnableWebMvc
    @ComponentScan("cn.tedu.springmvc")Spring的配置类public class SpringMvcConfig implements WebMvcConfigurer {
    }
    
    

4.小结

  1. 响应正文的定义:将处理请求的方法的返回值作为响应到客户端的正文数据
  2. 响应正文的做法:
    1. 在方法上添加@ResponseBody
    2. 或,在类上添加@ResponseBody
    3. 或,在类上使用@RestController取代@Controller
  3. 响应正文的格式:响应的结果通常需要包含多项数据,响应1个字符串并不便于表示这些数据,通常响应JSON格式的字符串
  4. 响应正文的原理:Spring MVC内置了一系列的转换器(Converter),根据处理请求的方法的返回值不同,自动选取某个转换器,将方法的返回值转换为响应到客户端的数据,当方法的返回值没有匹配的默认转换器时,会自动使用jackson-databind的转换器
  5. 当需要响应JSON格式的正文时,你需要:–
    1. 添加jackson-databind依赖 –
    2. 在Spring MVC配置类上添加@EnableWebMvc注解–
    3. 自定义类,作为处理请求的方法的返回值类型 –
      1. 类的属性必须添加Setter & Getter –
    4. 使得处理请求的方法是响应正文的

4. 接收请求参数

  1. 在Spring MVC中,当需要接收客户端的请求参数时,只需要将各参数直接声明为处理请求的方法的参数即可,例如:

    // /user/reg.do?username=root&password=123456&age=25
    @RequestMapping("/reg.do")
    public String reg(String username, String password, Integer age){System.out.println("username = " + username
    + "
    , password = " + password
    + "
    , age = " + age);
    return "OK";
    }
    
    
  2. 在声明参数时,你可以将参数声明为你期望的数据类型,Spring会自动的执行类型转换

    1. 经过网络传输得到的数据,最原始的类型都是String
    2. Spring会自动尝试转换类型,通常,类型必须是基础数据类型及其包装类、常用类型(例如String等)、Spring定义的类型等
    3. 如果自动转换类型失败,会抛出相应的异常
  3. 如果客户端提交的请求中根本没有匹配名称的参数,则以上获取到的值将是null

  4. 如果客户端仅提交了参数名称,却没有值,则以上获取到的值将是""(长度为0的字符串)

  5. 如果客户端提交了匹配名称的参数,并且值是有效的,则可以获取到值

  6. 以上名称应该是由服务器端决定的,客户端需要根据以上名称来提交请求参数

  7. 当有必要的情况下,可以在以上各参数的声明之前添加@RequestParam注解,其作用主要有:

    1. 配置name属性:客户端将按照此配置的值提交请求参数,而不再是根据方法的参数名称来提交请求参数
    2. 配置required属性:是否要求客户端必须提交此请求参数,默认为true,如果不提交,则出现400错误,当设置为false时,如果不提交,则服务器端将此参数值视为null
    3. 配置defaultValue属性:配置此请求参数的默认值,当客户端没有提交此请求参数时,视为此值
  8. 如果需要客户端提交的请求参数较多,可以将这些参数封装为自定义的数据类型,并将自定义的数据类型作为处理方法的参数即可,例如:

    public class UserRegDTO {
    private String username;
    private String password;
    private Integer age;
    // 生成Setters & Getters
    // 生成toString(),为了方便查看数据
    }
    
    
  9. 在控制器中:

    // /user/reg.do?username=root&password=123456&age=25
    @RequestMapping("/reg.do")
    public String reg(UserRegDTO userRegDTO) {
    System.out.println(userRegDTO);
    return "OK";
    }
    
    
  10. 你也可以将多个请求参数区分开来,一部分直接声明为处理请求的方法的参数,另一部分封装起来。

  11. 理论上来说,由于一个个的声明请求参数更加简单并且直观,所以,当请求参数数量非常少时,应该使用这种做法,当参数较多,或参数数量可能调整(例如需求变化引起的调整),则应该使用封装的数据类型

  12. 在开发实践中,考虑到需要使用到的其它框架的特性,使用封装的做法更为常见

2.小结

  • 你可以将请求参数一个个的声明为处理请求方法的参数,也可以将多个参数封装到一个自定义类中,使用自定义类作为处理请求的方法的参数,Spring MVC框架会自动接收客户端提交的请求参数,并用于调用你编写的处理请求的方法
  • 在大部分情况下,推荐使用将参数封装到自定义类的做法
  • 你需要保证非String类型的参数是Spring框架可以成功自动转换类型的,或者,对转换失败有进一步的处理,或可以接受转换失败带来的后果

5. 课后阅读

1. 关于POJO

  1. POJO:Plain Ordinary Java Object,即:普通Java对象
  2. 所有用于封装属性的类型都可以统称为POJO
  3. 常见的POJO的类名后缀有:BO、DO、VO、DTO等,不同的后缀表示不同的意义,例如:
    1. VO = Value Object / View Object 通常用于业务层之间的数据传递,和PO一样也是仅仅包含数据而已。但应是抽象出的业务对象,可以和表对应,也可以不,这根据业务的需要.
    2. DTO = Data Transfer Object
  4. 所有POJO类都应该遵循以下规范:
    1. 实现Serializable接口 –
    2. 所有属性都是私有的 –
    3. 所有属性都有规范的Setter & Getter
      • 由开发工具生成的,即是规范的
    4. 规范的重写了hashCode()和equals()
      • 2个类型相同、所有属性值都相同的对象,equals()应该返回true,否则,返回false
      • 2个类型相同、所有属性值都相同的对象,hashCode()返回值应该相同,否则,不同
      • 由开发工具生成的,即是规范的,不同开发工具生成的方法源码可能不同,这不重要
    5. 通常,应该重写toString()输出每个属性的值
  5. 所有POJO类的类名后缀:
    1. 在一个项目中,哪些情景下使用哪种后缀并没有统一的规定,通常是各项目内部决定
    2. 注意:在为封装属性的类进行命名时,以上BO、DO、VO、DTO等这些后缀的每一个字母都应该是大写的!
    3. 参考资料:在《阿里巴巴Java开发手册》中要求POJO类名后缀的每个字母都是大写的
      • 【强制】类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:DO / BO / DTO / VO / AO
        • 正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
        • 反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion
    4. 参考资料:在《阿里巴巴Java开发手册》中要求不允许使用POJO作为类名后缀
      • 领域模型命名规约
      • 1) 数据对象:xxxDO,xxx 即为数据表名。
      • 2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
      • 3) 展示对象:xxxVO,xxx 一般为网页名称。
      • 4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。
    5. 参考资料:在《阿里巴巴Java开发手册》中要求所有POJO类的属性都不要设置默认值
      • 定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值。
        • 反例:POJO 类的 gmtCreate 默认值为 new Date();但是这个属性在数据提取时并没有置入具 体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。
    6. 参考资料:在《阿里巴巴Java开发手册》中提供了使用类名后缀的参考,但不是强制约定
      • 分层领域模型规约:
        • DO(Data Object):与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
        • DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。
        • BO(Business Object):业务对象。由 Service 层输出的封装业务逻辑的对象。
        • AO(Application Object):应用对象。在 Web 层与 Service 层之间抽象的复用对象模型, 极为贴近展示层,复用度不高。
        • VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。
        • Query:数据查询对象,各层接收上层的查询请求。注意超过 2 个参数的查询封装,禁止 使用 Map 类来传输。

2.VO、DTO、Entity的区别

1.含义

1、entity 里的每一个字段,与数据库相对应,

2、vo 里的每一个字段,是和你前台 html 页面相对应,

3、dto 这是用来转换从 entity 到 vo,或者从 vo 到 entity 的中间的东西 。(DTO中拥有的字段应该是entity中或者是vo中的一个子集

2.举个例子:

你的html页面上有三个字段,name,pass,age

你的数据库表里,有两个字段,name,pass , 注意没有 age。

而你的 vo 里,就应该有下面三个成员变量 ,因为对应 html 页面上三个字段 。

3.举个例子:

业务经理让你做这样一个业务“年龄大于 20 的才能存入数据库,这个时候,你就要用到 dto 了

1)你要先从页面上拿到 vo,然后判断 vo 中的 age 是不是大于 20。

2)如果大于 20,就把 vo 中的 name 和 pass 拿出来,放到 dto 中。

3)然后在把 dto 中的 name 和 pass 原封不动的给 entity,然后根据 entity 的值,在传入数据库。

posted @ 2022-05-26 13:41  约拿小叶  阅读(105)  评论(0编辑  收藏  举报