5、JavaWeb-请求响应-分层解耦

P67 请求响应-概述

DispatcherServlet是一个较为核心的类,在springboot当中称为核心控制器或者前端控制器,

请求发送到后端,web服务器Tomcat接受到这个请求数据,然后进行解析,然后将解析的封装到一个对象当中:HttpServletRequest请求对象。

借助HttpServletResponse来设置响应数据

这样的架构称为BS架构,浏览器/服务器架构模式,

C/S这种就是客户端/服务器架构模式,

P68 请求响应-postman工具

P69 请求响应-简单参数-实体参数

简单参数

原始web程序,需要通过HttpServletRequest对象手动获取

例如:

@RestController
public class RequestController {

    @RequestMapping("/simpleParams")
    public String simpleParams(HttpServletRequest request){
//        参数中创建request对象用于获取请求参数
        String name = request.getParameter("name");
        String age = request.getParameter("age");

        System.out.println(name + " and " + age);
        return "OK";
    }
}

使用postman发送请求时,可以看到记录:tomcat服务器接收请求解析请求,使用到了dispatcherServlet,然后获取到请求内容,
在这里插入图片描述

上述操作繁琐,简化方式:

在controller中声明需要的形参即可接收到,还会自动进行类型转换,如下所示:

//    简化方式
    @RequestMapping("/simpleParams")
    public String simpleParams(String name, int age){
        System.out.println(name + ": " + age);

        return "OK";
    }

如果是post请求,则是在请求体中,所以在postman中需在body中编写,这里知识点在http协议中讲解过。

简单参数中,如果方法形参名与请求参数名不匹配,可以使用@ requestParam完成映射

例如:

//    映射参数名
    @RequestMapping("/simpleParams")
    public String simpleParams(@RequestParam(name = "name")String name, @RequestParam(name = "age")Integer age){
        System.out.println(name + ": " + age);
//
        return "OK";
    }

注意如果使用requestParam,其中的required默认为true,代表该请求参数必须传递,不传递则会报错,

如果该参数可选,要将required设置为false.

实体参数

简单参数要求参数名与后端中形参名相同,但如果过多则不方便,

简单实体参数封装

可以考虑将所有请求参数封装到一个实体类中,同样也应保证请求参数名和形参名对应

创建实体对象后,可用alt+insert或者右键,生成。来快速生成get、set方法以及tostring方法。

例如:创建一个User实体

public class User {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

参数名为实体对象

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

        return "OK";
    }

复杂实体对象

请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套pojo属性参数,这里的嵌套是指一个一个的封装

例如:

 // User中嵌套Address属性
    private Address address;

P70 请求响应-数组集合参数

假设表单中有一个复选框,则提交是吧这个属性的多个值进行提交,则也是一个一个提交即可。

例如:hobby=game&hobby=javas

如果使用数组接收,则数组参数:请求参数与形参数组名相同且请求参数为多个,定义数组类型形参即可接收参数,例如:

// 数组参数
    @RequestMapping("/arrayParam")
    public String arrayParam(String[] hobby){
        System.out.println(hobby);
        System.out.println(Arrays.toString(hobby));

        return "OK";
    }

如果使用集合参数:请求参数名与形参集合名相同且请求参数为多个,默认情况下多个值封装到数组中,要封装到集合当中则要使用注解@ RequestParam绑定参数关系

例如:

   // 集合参数
    @RequestMapping("/listParam")
    public String listParam(@RequestParam List<String> hobby){
        System.out.println(hobby);

        return "OK";
    }

P71 请求响应-日期参数-json参数

日期参数形式多种多样,在后端接收时需要使用注解@ DateTimeFormat完成日期参数格式转换,例如:

   // 日期参数
    @RequestMapping("/dateParam")
    public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")LocalDateTime updateTime){
        // 使用日期类型创建一个对象接收参数
        System.out.println(updateTime);

        return "OK";
    }

json参数在前后端异步交互较多,

接收json参数:json数据键名需要与形参对象属性名相同,定义实体类型形参即可接收参数,需要使用@ RequestBody进行标识(将json数据封装到实体中)。一般用实体对象接收json格式数据

json的请求格式数据需要放在请求体当中,所以请求方式因该为post,

例如:

   // json参数
    @RequestMapping("/jsonParam")
    public String jsonParam(@RequestBody User user){
        System.out.println(user);

        return "OK";
    }

P72 请求响应-路径参数

是请求路径的一部分,同时也是传递的参数,例如path/1,这样通过URL直接传递参数,使用{…}来标识该路径参数,需要使用@PathVariable获取路径参数

例如:

    // 路径参数
    @RequestMapping("/path/{id}")
    public String pathParam(@PathVariable  Integer id){
        // 使用@PathVariable注解将路径参数绑定到id上
        System.out.println(id);

        return "OK";
    }

假设存在多个路径参数也是同样类似的方法

以上部分小结:

在这里插入图片描述

P73 请求响应-@ ResponseBody-统一响应结果

controller中返回给浏览器,依赖于注解@responseBody

该注解:

  • 类型:方法注解、类注解

  • 位置:Controller方法上/类上,也就是说在某个类上添加这个注解,当前类中所有返回的值会作为响应数据

  • 作用:将方法返回值直接响应,如果返回值类型是实体对象/集合,将会转换为json格式响应

  • 说明:@RestController = @Controller + @ResponseBody

为了统一响应结果可以使用一个实体封装,例如:
在这里插入图片描述

下面是一个示例:

package com.example.entity;

/**
 * @author by hongdou
 * @date 2024/2/29.
 * @DESC: 统一响应结果封装类
 */
public class Result {
    private Integer code; // 1成功 0失败
    private String msg; // 提示信息
    private Object data; // 数据date

    // 无参构造
    public Result(){

    }

    // 有参构造
    public Result(Integer code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    // 下面是一些静态方法
    public static Result success(Object data){
        return new Result(1, "success", data);
    }

    public static Result success(){
        return new Result(1, "success", null);
    }

    public static Result error(String msg){
        return new Result(0, msg, null);
    }

    @Override
    public String toString() {
        return "Result{" +
                "code=" + code +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }
}

然后在controller中可以这样使用:

@RestController
public class ResponseController {
    @RequestMapping("/hello")
    public Result hello(){
        System.out.println("Hello World");
        // 由于要返回一个result对象,所以新建一个,并返回我们需要返回的值
//        return new Result(1, "success", "Hello World");

        // 同样result中封装了静态方法,也可以直接调用静态方法
        return Result.success("Hello World");
    }
}

P74 请求响应-案例

案例:获取员工数据,返回统一响应结果,在页面渲染

步骤:

  • 引入dom4j用于解析xml文件,

  • 导入解析xml的工具类

  • 创建员工的实体类Emp

  • 引入员工数据的xml文件

  • 导入前端页面

注意:springboot项目下前端静态页面默认存放目录为:

  • classpath:/static

  • classpath:/public

  • classpath:/resources

案例:

@RestController
public class EmpController {

    @RequestMapping("/listEmp")
    public Result list(){
        // 加载解析xml文件
        // 加载绝对路径不太方便
        // 动态加载路径,可以基于类加载器
        String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
        System.out.println("文件路径:" + file);
        List<Emp> empList = XmlParserUtils.parse(file, Emp.class);

        // 对数据转换处理
        // 有多个数据需要遍历,基于stream流进行遍历
        empList.stream().forEach(emp -> {
            // 处理gender以及job
            String gender = emp.getGender();
            if("1".equals(gender)){
                emp.setGender("男");
            }else if("2".equals(gender)){
                emp.setGender("女");
            }

            String job = emp.getJob();
            if("1".equals(job)){
                emp.setJob("讲师");
            }else if("2".equals(job)){
                emp.setJob("班主任");
            }else if("3".equals(job)){
                emp.setJob("就业指导");
            }

        });
        // 响应数据
        return Result.success(empList);

    }
}

springboot项目的中访问静态页面:直接加上静态页面名称

P75 分层解耦-三层架构

了解单一职责原则,因此上述的controller部分还可以修改,

了解三层架构:

  • controller:控制层,接收请求、响应数据

  • Service:业务逻辑层,处理具体的业务逻辑

  • dao:数据访问层,data access object持久层,负责数据访问操作,包括数据增删改查

在这里插入图片描述

如果按照三层架构,去修改某个接口的业务逻辑,只需要修改Service层就好了,

dao层面向接口的方式实现,

对于案例实现建议理解代码及步骤,注意理解面向接口编程,

小结:

所有数据处理和业务逻辑都写在controller的方法中,造成代码复用性差、难以维护管理

三层架构复用性强,职责单一便于维护和利用拓展

P76 分层解耦-IOC-DI引入

内聚:软件中各个功能模块内部的功能联系,例如EmpService中的方法都是与Emp相关的,高内聚

耦合:衡量软件中各个层/模块之间的依赖,关联的程度

软件设计原则:高内聚低耦合

例如:controller中new 一个service的实现类,这里与service层实现耦合,如果service改变,这里也要改变,可以想到将实现类实现到容器当中进行存储,然后容器为controller中提供需要的资源,这就涉及到:控制反转、依赖注入

  • 控制反转,Inversion of control,简称iOC,对象的创建控制由程序自身转移到外部容器,这种思想称为控制反转。spring框架的第一大核心,理解解释:例如初始版本的controller中,我要创建一个对象,就直接new一个,现在是把所有的对象交给容器管理,反转之前由应用程序自身控制对象的创建的,反转后由容器管理这些对象的,又称为IOC容器,或者叫spring容器。

  • 依赖注入,Dependency injection,简称DI,容器为应用程序提供运行时,所依赖的资源,称为依赖注入。例如controller中需要EmpService的资源,就有容器提供,

  • Bean对象,IOC容器中创建、管理的对象,称为bean。

P77 IOC/DI入门

主要完成的工作就是按照三层架构进行解耦,注意理解逻辑关系

  • Service层和Dao层的实现类,交给IOC容器管理,如何交给容器管理,只需要加上注解@component,注意是在实现类中添加,

  • 为Controller及Service注入运行时依赖的对象,加上注意@Autowired实现依赖注入

  • 运行测试

这样做,举个例子,当EmpService的实现类有两个EmpServiceA和EmpServiceB的时候,我们如果需要将实现类从A换成B只需要注释掉A的注解,在B添加注解即可,其余Controller和Dao层的就可以不用改变。

P78 IOC详解

IOC容器当中的对象也称为bean对象

在spring框架当中,除了component注解以外,spring框架为了更好标识bean对象到底归属于哪一层,又提供了component的三个衍生注解。

在这里插入图片描述

推荐使用衍生的三个注解,如果某个类不在上述三层中,但是也要加入IOC容器管理,则可以使用component注解,例如一些工具类。

注意如果在controller层上实现component的衍生注解,需要加上controller,但是我们已经引入了restcontroller = responsebody + controller,所以不需要单独再加controller了。

在IOC容器中,每一个bean都是有名字的,可通过value指定,默认是类名首字母小写。例如EmpController,则实现类的bean名字默认为empController。

如果通过value指定名字,则可以Repository(value="daoA")

注意使用上述四个注解都可以声明bean,但是在springboot集成web开发中,声明控制器bean只能用@Controller。

Bean组件扫描:

注意声明bean的四大注解,要想生效,还需要被组件扫描注解@ComponentScan扫描。虽然没有显式配置,但实际上包含在启动类声明注解@SpringBootApplication中,默认扫描的范围是启动类所在包及其子包。例如启动类所在包是com.itheima,则该包和子包都能扫描到。

如果需要注入bean的类不在该包中,则需要在启动类上添加,例如@ComponentScan({ Dao", "com.itheima"})

注意@Repository注解很少用,学习mybatis框架后会用另外一个注解进行替代。

P79 DI详解

autowired,按照类型查找对象,完成注入操作。举例,如果EmpService的bean注入有两个,即两个实现类都有,则在controller中会出现下述提示,

在这里插入图片描述

因此解释上述问题,因为autowired注解默认是按照类型进行,如果存在多个相同类型的bean,则会报错。

提示信息中有以下几种方案解决:

  • @Primary,设置优先级,再在具体的实现类中加上这个注解

  • @Qualifier,在autowired上加上qualifier,指定bean的名字

  • @Resource,不同于autowired按照类型注入,resource是按照名子进行注入的,例如:

 @Resource(name = "empServiceA")
 private EmpService empService;

resource是jdk提供的,其余两个是spring提供的。

面试问题:resource与autowired的区别

  • 前者是JDK提供的注解,后者是spring框架提供的注解

  • autowired默认按照类型注入,resource默认按照名称注入

  • 39
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java EE 项目的目录结构可以根据具体的需求进行灵活设计,但一般情况下,推荐使用以下的标准目录结构: ``` project ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ └── example │ │ │ ├── controller │ │ │ ├── dao │ │ │ ├── entity │ │ │ ├── service │ │ │ └── util │ │ ├── resources │ │ │ ├── mapper │ │ │ └── db.properties │ │ └── webapp │ │ ├── WEB-INF │ │ │ ├── classes │ │ │ ├── lib │ │ │ └── web.xml │ │ ├── css │ │ ├── js │ │ ├── images │ │ └── index.jsp │ └── test │ ├── java │ └── resources ├── target ├── pom.xml └── README.md ``` 其中,各个目录的作用如下: - `src/main/java`:存放项目的 Java 源代码,按照包名分层,一般包括 `controller`、`dao`、`entity`、`service` 和 `util` 等包; - `src/main/resources`:存放项目的配置文件和资源文件,一般包括数据库连接配置文件 `db.properties`、MyBatis 的 mapper 文件等; - `src/main/webapp`:存放 Web 应用的 Web 资源,包括 JSP 页面、CSS 样式表、JavaScript 脚本等; - `src/test/java`:存放项目的测试代码; - `src/test/resources`:存放测试代码所需要的资源文件; - `target`:存放编译后的 .class 文件、打包后的 .war 文件等; - `pom.xml`:Maven 项目管理工具的配置文件; - `README.md`:项目说明文件。 以上是一种常见的 Java EE 项目目录结构,但并不是唯一的标准。在实际开发中,可以根据项目的具体需求进行合理的调整和修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值