SpringBootWeb学习笔记——12万字详细总结!

news2024/12/24 21:57:26

0. 写在前面

注:这套笔记是根据黑马程序员B站2023-3-21的视频学习的成果,其中省略了前端基础部分、Maven部分和数据库基础部分,详情可见目录。
注注:目前文章内结尾处多幅图片加载不出来,因为图片还存在本地没被传上来,过段时间再改~

所有的Spring项目都基于Spring Framework
Spring Framework配置繁琐,入门难度大

文章目录

  • 0. 写在前面
  • 1. SpringBootWeb入门
    • 简单的构成分析
    • 工程启动后
  • 2. HTTP协议
    • 特点
    • 请求协议
    • 相应协议
    • 协议解析
  • 3. Tomcat
    • 基本使用
    • 部署项目
  • 4. SpringBootWeb入门程序解析
    • 起步依赖
  • 5. 请求响应
    • 前端控制器
    • 请求对象:获取请求数据
    • 响应对象:设置响应数据
    • B/S架构:浏览器/服务器架构模式。客户端只需要浏览器,应用程序和数据都存放在服务端。
    • C/S架构:客户端/服务器架构模式。
    • 工具
    • 简单参数
    • 实体参数
    • 响应数据
    • 案例
  • 6. 分层解耦
    • 三层架构
    • IOC&DI引入
    • IoC-DI入门
    • IoC详解
    • 组件扫描
    • DI详解
  • 7. SQL
    • 语句分类
    • DDL
    • 事务
    • 索引
    • 索引结构
    • 索引操作语法
  • 8. Mybatis
    • 快速入门
    • 单元测试
    • 配置注解SQL提示(23版IDEA自带,无需配置)
    • JDBC介绍
    • 数据库连接池
    • Lombok工具包
    • MyBatis基本操作案例,准备工作
    • 删除
    • 删除(预编译SQL)
    • 新增
    • 新增(主键返回)
    • 更新
    • 查询(根据id查询)
    • 查询(条件查询)
    • XML映射文件(也叫配置文件)
  • 9. MyBatis动态SQL
    • \<if\>
    • \<where\>
    • if案例
    • \<set\>
    • \<foreach\>
    • \<sql\>和\<include\>
  • 10. 部门管理员工管理案例
    • 环境搭建
    • 开发规范restful
    • 开发规范统一响应结果
    • 开发流程
    • 部门管理部分
    • 员工管理部分
    • 插播:文件上传
    • 本地存储
    • 插播结束:阿里云OSS
    • 修改员工
    • 参数配置化
    • yml配置文件
    • @ConfigurationProperties
    • 登录功能
    • 登录校验
  • 11. 会话技术
    • 客户端会话跟踪技术:Cookie
    • 服务器会话跟踪技术:Session
    • 令牌技术
    • JWT令牌
    • JWT生成和校验
    • 登录后下发令牌(注意:令牌存储由前端负责,案例中存放在本地存储空间)
  • 12. 统一拦截,过滤器Filter
    • 过滤器Filter快速入门
    • Filter执行流程
    • Filter拦截路径
    • 过滤器链
    • 案例实现登录校验过滤器
  • 13. 拦截器Interceptor快速入门
    • 拦截器拦截路径
    • 拦截器执行流程
    • 案例实现登录校验拦截器
  • 14. 异常处理
    • 异常处理方案
  • 15. 事务管理
    • Spring事务管理
  • 16. 事务进阶
    • rollbackFor属性
    • propagation属性
  • 17. AOP
    • AOP快速入门
    • AOP核心概念
    • AOP执行流程
    • AOP通知类型
    • AOP通知顺序
    • 切入点表达式
    • execution
  • annotation
    • 连接点
    • AOP案例
  • 终章:原理篇
    • 配置优先级
    • 获取bean
    • bean作用域
    • 第三方bean
  • SpringBoot原理
    • Spring家族
    • 起步依赖原理
    • 自动配置概述
    • 自动配置原理
    • 源码跟踪分析
    • @Conditional
    • 自定义starter
  • Web总结
    • 三层架构

1. SpringBootWeb入门

一个简单的应用:浏览器发起/hello请求后,给浏览器返回一个字符串Hello World~
在这里插入图片描述
步骤:

  1. 创建springboot工程,并勾选web开发相关依赖。
  2. 定义HelloController类,添加方法hello,并添加注解。
  3. 运行测试

简单的构成分析

  • 一个自动生成的类,和模块名一致:启动类,用来启动springboot工程,想启动工程只需要运行这个main函数
    package com.itheima;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    // 启动类 --- 启动springboot工程
    @SpringBootApplication
    public class SpringBootQuickStartApplication {
         
    
        public static void main(String[] args) {
         
            SpringApplication.run(SpringBootQuickStartApplication.class, args);
        }
    }
    
  • resources/static/templates暂时用不上
  • .properties是默认配置文件,也支持一些其他的后缀格式
  • 创建的HelloController类是一个普通的Java类,需要添加注解@RestController把他变成请求处理类。还需要指定当前方法处理的是那个请求,使用注解@RequestMapping('/hello')指定处理的请求路径,浏览器将来请求/hello这个地址时,最终就会调用这个方法
    package com.itheima.controller;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    // 请求处理类,加上注解后,这就是一个请求处理类,不加注解就是普通类
    @RestController
    public class HelloController {
         
        // 指定当前方法处理的是哪个请求,/hello请求。浏览器请求/hello地址时,最终就会调用这个hello方法
        @RequestMapping("/hello")
        public String hello() {
         
            System.out.println("Hello World~");
            return "Hello World~";
        }
    }
    

工程启动后

工程如果成功启动,在浏览器输入localhost:8080就可以看到函数return的Hello World,也可以在控制台看到输出的Hello World,说明入门程序没有问题。

2. HTTP协议

超文本传输协议,规定了浏览器与服务器间数据传输的规则,实际上就是规定了数据格式
F12查看网络里的hello请求
request header是请求的数据
response header是响应的数据

特点

  1. 基于TCP协议:面向连接,安全
  2. 基于请求-响应模型:一次请求对应一次响应
  3. HTTP协议是无状态协议:对事物没有记忆力,每次请求-响应都是独立的
    • 缺点:多次请求间不能共享数据
    • 优点:速度快

请求协议

就是请求数据的格式,分为三个部分

  1. 请求行,数据第一行,请求方式+资源路径+协议
  2. 请求头,第二行开始,是key: value的格式
  3. 请求体,POST请求,存放请求参数
    • GET:请求参数在请求行中,没有请求体,请求大小有限制
    • POST:请求参数在请求体中,请求大小没有限制

相应协议

同样分为三个部分

  1. 响应行,数据第一行,协议+状态码+描述
  2. 响应头,第二行开始,是key: value的格式
  3. 响应体,最后一部分,存放响应数据
    注意状态码200、404、500

协议解析

Tomcat

3. Tomcat

最流行的HTTP协议解析程序,使程序员不必直接对协议进行操作。主要功能是“提供网上信息浏览服务”,又称Servlet容器
安装:解压安装包
卸载:删除文件夹

基本使用

bin/startup.bat运行
bin/shutdown.bat关闭
常见问题:

  • startup.bat闪退,检查环境变量是否有JAVA_HOME(不带bin)
  • 端口号冲突,找到对应程序将其关闭(任务管理器、详细信息、找对应端口号的程序)或修改Tomcat使用的端口号conf/server.xml
    注意:HTTP协议默认端口号为80,若将Tomcat端口号改为80,则访问Tomcat时,不用输入端口号

部署项目

将项目放置到webapps下就部署完成了

4. SpringBootWeb入门程序解析

起步依赖

  • spring-boot-starter-web:包含了web应用开发所需要的常见依赖
  • spring-boot-starter-test:包含了单元测试所需要的常见依赖

借助依赖传递特性将某个功能所需的常见依赖聚合到一起。
pom中会看到起步依赖没有写<version>,这是因为SpringBoot项目都有一个父工程,在pom中表现为<parent>标签里的内容,所有起步依赖的版本都在父工程中进行了统一管理,会自动引入和SpringBoot版本对应的起步依赖版本

可以看到spring-boot-starter-web中有一个tomcat相关的依赖,所以启动项目时会将内置的Tomcat启动起来并占用Tomcat默认端口号8080,这个Tomcat和外部安装的Tomcat不是同一个,外部的Tomcat会很少使用,多数都是使用内置的Tomcat

5. 请求响应

Tomcat不能识别我们写的如HelloControllercontroller程序,但Tomcat可以识别Servlet规范。SpringBoot底层提供了一个非常核心的Servlet程序DispatcherServlet,它实现了Servlet规范中的Servlet接口(可看继承体系)。
浏览器发起的请求都会先经过DispatcherServlet,由DispatcherServlet将请求转给controller程序进行处理,处理完的结果返回给DispatcherServletDispatcherServlet再给浏览器响应数据

前端控制器

`DispatcherServlet`

如何在Servlet中获取请求数据?浏览器发请求会携带HTTP请求数据,web服务器负责对请求协议的解析。Tomcat就会对数据进行解析,并将解析后所有请求信息封装到一个对象HttpServletRequest中,也叫请求对象

请求对象:获取请求数据

应用程序就可以从HttpServletRequest对象中获取请求数据了,然后对请求进行处理。然后Tomcat服务器需要根据响应数据的格式给浏览器响应数据。借助另一个对象设置响应数据,HttpServletResponse,Tomcat会根据在这个对象中设置的响应信息来响应数据给浏览器。

响应对象:设置响应数据

HttpServletResponse

B/S架构:浏览器/服务器架构模式。客户端只需要浏览器,应用程序和数据都存放在服务端。

C/S架构:客户端/服务器架构模式。

主要关注controller程序,最重要的是获取请求参数和设置响应数据

工具

postmanapifox

简单参数

get post方式的处理方法相同
发送的是普通数据如name=zhangsan,创建controller/RequestController类进行操作

  • 原始方式获取请求参数:通过HttpServletRequest对象手动获取
    请求地址:http://localhost:8080/simpleParam?name=tom&age=10
    实现请求处理方法,处理对/simpleParam的请求

     package com.itheima.controller;
    
     import jakarta.servlet.http.HttpServletRequest;
     import org.springframework.web.bind.annotation.RequestMapping;
     import org.springframework.web.bind.annotation.RestController;
    
     @RestController
     public class RequestController {
         
         // 原始方式获取简单参数
         @RequestMapping("/simpleparam")
         public String simpleParam(HttpServletRequest request) {
         
             // 获取请求参数
             String name = request.getParameter("name");
             String ageStr = request.getParameter("age");
    
             int age = Integer.parseInt(ageStr);
             System.out.println(name + ":" + age);
             return "OK";
         }
     }
    

    缺点:繁琐,需要手动类型转换

  • SpringBoot方式
    简单参数:参数名与形参变量名相同,即可自动接收参数
    请求地址:http://localhost:8080/simpleParam,参数为name=Tomage=20
    示例如下:
    修改RequestController 中的方法

    package com.itheima.controller;
    
    import jakarta.servlet.http.HttpServletRequest;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class RequestController {
         	
        // SpringBoot方式获取参数
        @RequestMapping("/simpleParam")
        public String simpleParam(String name, Integer age) {
         
            System.out.println(name + ":" + age);
            return "OK";
        }
    }
    

    如果参数名和形参名不同,默认情况下会接收不到,变量存储值为null。可以通过注解@RequestParam完成映射

    @RequestMapping("/simpleParam")
    public String simpleParam(@RequestParam(name= "name") String username, Integer age) {
         
        System.out.println(username + ":" + age);
        return "OK";
    }
    

    注意:@RequestParam中的required默认为true,表示该参数必须传递,如果不传递则报错,若参数是可选的,可修改requiredfalse

实体参数

  • 简单实体对象:请求参数名与形参对象属性名相同,定义一个POJO类接收即可。
    注意:定义的类中属性名必须和请求参数名相同才能成功封装
    示例如下:
    请求地址:http://localhost:8080/simplePojo?name=ITCAST&age=16
    定义一个User类,其中的属性名必须和请求参数名相同,这里为name、age

    package com.itheima.pojo;
    
    public class User {
         
        public String name;
        public 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 +
                    '}';
        }
    }
    

    修改RequestController中的方法,处理对/simple/Pojo的请求

    package com.itheima.controller;
    
    import com.itheima.pojo.User;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class RequestController {
         
        @RequestMapping("/simplePojo")
        public String simplePojo(User user) {
         
            System.out.println(user);
            return "OK";
        }
    }
    
  • 复杂实体对象:按照对象层次关系接收嵌套POJO属性参数
    示例如下:
    请求地址:http://localhost:8080/complexPojo?name=ITCAST&age=16&address.province=北京&address.city=北京
    写一个Address

    package com.itheima.pojo;
    
    /**
     * Created with IntelliJ IDEA.
     *
     * @author : wu_qing
     * @version : 1.0
     * @Project : LearnSpringBoot
     * @Package : com.itheima.pojo
     * @ClassName : .java
     * @createTime : 2024/2/10 21:58
     * @Email : 1553232108@qq.com
     * @Description :
     */
    public class Address {
         
        public String province;
        public String city;
    
        public String getProvince() {
         
            return province;
        }
    
        public void setProvince(String province) {
         
            this.province = province;
        }
    
        public String getCity() {
         
            return city;
        }
    
        public void setCity(String city) {
         
            this.city = city;
        }
    
        @Override
        public String toString() {
         
            return "Address{" +
                    "province='" + province + '\'' +
                    ", city='" + city + '\'' +
                    '}';
        }
    }
    

    Address添加到User类的属性中
    修改请求处理方法

    package com.itheima.controller;
    
    import com.itheima.pojo.User;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class RequestController {
         
        @RequestMapping("/complexPojo")
        public String complexPojo(User user) {
         
            System.out.println(user);
            return "OK";
        }
    }
    
  • 数组集合参数/多同名参数:
    数组方法:请求参数名与数组名相同且请求参数为多个,定义数组类型形参即可
    集合方法:使用@RequestParam绑定参数关系,将多个请求参数的值封装到集合
    示例如下:
    请求地址:http://localhost:8080/arrayParam?hobby=game&hobby=java&hobby=sing
    http://localhost:8080/listParam?hobby=game&hobby=java&hobby=sing
    修改请求处理方法

    package com.itheima.controller;
    
    import com.itheima.pojo.User;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class RequestController {
         
        // 数组方法
        @RequestMapping("/arrayParam")
        public String arrayParam(String[] hobby) {
         
            System.out.println(Arrays.toString(hobby));
            return "OK";
        }
        
        // 集合方法获取同名参数
        @RequestMapping("/listParam")
        public String listParam(@RequestParam List<String> hobby) {
         
            System.out.println(hobby);
            return "OK";
        }
    }
    
  • 日期参数:使用@DateTimeFormat注解完成日期参数格式转换
    需要指定传来的日期参数格式,如@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss)"
    注意:这里写的是yyyy-MM-dd HH:mm:ss的格式,那么请求参数里也必须是4位-2位-2位 2位:2位:2位
    请求地址:http://localhost:8080/dateParam?updateTime=2024-12-10 22:17:39,如这里的月份如果为1月必须写为01补全位数

    package com.itheima.controller;
    
    import com.itheima.pojo.User;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class RequestController {
         
        // 日期参数
        @RequestMapping("/dateParam")
        public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime) {
         
            System.out.println(updateTime);
            return "OK";
        }
    }
    
  • JSON参数:需要POST方式发送,要求JSON数据键名对象属性名相同,定义POJO类型即可接收参数,需要使用@RequestBody标识,将JSON数据封装到实体对象中
    示例如下:
    请求地址:http://localhost:8080/jsonParam
    数据:{ "name": "zhangsan", "age": 16, "address": { "province": "北京", "city": "北京" } }
    实现请求处理方法

    package com.itheima.controller;
    
    import com.itheima.pojo.User;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class RequestController {
         
        // JSON参数
        @RequestMapping("/jsonParam")
        public String jsonParam(@RequestBody User user) {
         
            System.out.println(user);
            return "OK";
        }
    }
    
  • 路径参数
    通过请求URL直接传递参数,如http://localhost:8080/path/1,如果注解写@RequestMapping("/path/1"),将来/1变成/2、/3、/100那就不能再处理这个请求,所以这个参数应该是动态的。使用{...}来标识该路径参数,就可以写@RequestMapping("/path/{id})代表这地方不是固定值而是路径参数,参数名叫id,这样就可以在controller中声明一个形参叫id,使用@pathVariable来指定获取路径参数并将路径参数的id绑定给方法参数的id路径参数参数名需要与方法形参参数名保持一致
    示例如下:
    请求地址:http://localhost:8080/path/1
    实现请求处理方法

    package com.itheima.controller;
    
    import com.itheima.pojo.User;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class RequestController {
         
        // 路径参数
        @RequestMapping("/path/{id}")
        public String pathParam(@PathVariable Integer id) {
         
            System.out.println(id);
            return "OK";
        }
    }
    

    多路径参数:在请求路径中使用/分隔,再写其他的参数,请求路径也要相应改变@RequestMapping("/path/{id}/{name}")
    注意:每个参数都需要使用@pathVariable来绑定
    示例如下:
    请求地址:http://localhost:8080/path/1/Tom
    实现请求处理方法

    package com.itheima.controller;
    
    	@RestController
    	public class RequestController {
         
    // 多路径参数
        @RequestMapping("/path/{id}/{name}")
        public String pathParam2(@PathVariable Integer id, @PathVariable String name) {
         
            System.out.println(id);
            System.out.println(name);
            return "OK";
        }
    }
    

响应数据

controller程序的return值就是响应(返回给浏览器)的值。所有的响应数据都需要依赖核心的@ResponseBody注解,需要写在controller方法上或类上。他的作用就是将方法返回值直接作为响应数据给客户端浏览器,如果返回值类型是实体对象/集合,会转为JSON格式再响应,**但是似乎从来没见过?**事实上是因为@RestController = @Controller + @ResponseBody,已经包含了@ResponseBody注解。写@RestController等价于写@Controller + @ResponseBody
在类上加了@ResponseBody注解,代表当前类下所有返回值都会作为响应数据,如果是对象或集合会先转JSON再来响应
示例如下:
请求地址分别为:

http://localhost:8080/hello
http://localhost:8080/getAddr
http://localhost:8080/listAddr

新建一个ResponseController

package com.itheima.controller;

import com.itheima.pojo.Address;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class ResponseController {
   

    @RequestMapping("/hello")
    public String hello() {
   
        System.out.println("Hello World!");
        return "Hello World~";
    }

    @RequestMapping("/getAddr")
    public Address getAddr() {
   
        Address addr = new Address();
        addr.setProvince("广东");
        addr.setCity("深圳");
        return addr;
    }

    @RequestMapping("/listAddr")
    public List<Address> listAddr() {
   
        List<Address> list = new ArrayList<>();

        Address addr = new Address();
        addr.setProvince("北京");
        addr.setCity("北京");

        Address addr2 = new Address();
        addr2.setProvince("广东");
        addr2.setCity("深圳");

        list.add(addr);
        list.add(addr2);
        return list;
    }
}

对这三个请求处理方法分别发出请求,可以看到第一个返回的就是一个字符串,第二个返回的是JSON格式的数据,第三个返回的是JSON数组的数据
说明:每个对外暴露的方法都称为功能接口,注解中写的路径为接口的访问路径。开发文档就是描述功能接口的请求路径,请求参数以及响应数据的。
可以发现每个接口响应的数据很随意,没有任何规范,前端很难解析响应回去的数据,开发成本会增加,项目不便管理且很难维护。
一般会给所有的功能接口设置统一的响应结果,可以考虑使用一个对象result来接收,主要有3个属性:

  1. int code,响应码,可以和前端约定:1表示成功,0表示失败
  2. string msg,提示信息
  3. object data,返回的数据
    返回的result对象经过ResponseBody的处理后,就会响应一个JSON格式的数据,前端只会收到一种格式的数据,只需要根据这一种格式来解析。项目管理和维护就会更加方便。

示例如下:
实现一个Result

package com.itheima.pojo;

public class Result {
   
    private Integer code; // 验证码,1成功,0失败
    private String msg; // 提示信息
    private Object data; // 数据

    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 +
                '}';
    }
}

类中写了三个静态方法,分别是成功且返回数据,成功且不返回数据,失败
再修改ResponseController类中的方法,使三个功能接口具有相同的响应数据格式

package com.itheima.controller;

import com.itheima.pojo.Address;
import com.itheima.pojo.Result;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class ResponseController {
   

    /*@RequestMapping("/hello")
    public String hello() {
        System.out.println("Hello World!");
        return "Hello World~";
    }

    @RequestMapping("/getAddr")
    public Address getAddr() {
        Address addr = new Address();
        addr.setProvince("广东");
        addr.setCity("深圳");
        return addr;
    }

    @RequestMapping("/listAddr")
    public List<Address> listAddr() {
        List<Address> list = new ArrayList<>();

        Address addr = new Address();
        addr.setProvince("北京");
        addr.setCity("北京");

        Address addr2 = new Address();
        addr2.setProvince("广东");
        addr2.setCity("深圳");

        list.add(addr);
        list.add(addr2);
        return list;
    }*/

    @RequestMapping("/hello")
    public Result hello() {
   
        System.out.println("Hello World!");
        // return new Result(1, "success", "Hello World!");
        return Result.success("Hello World!"); // 和上边的效果一样,一个用构造,一个用静态方法,都是返回一个result对象
    }

    @RequestMapping("/getAddr")
    public Result getAddr() {
   
        Address addr = new Address();
        addr.setProvince("广东");
        addr.setCity("深圳");
        return Result.success(addr);
    }

    @RequestMapping("/listAddr")
    public Result listAddr() {
   
        List<Address> list = new ArrayList<>();

        Address addr = new Address();
        addr.setProvince("北京");
        addr.setCity("北京");

        Address addr2 = new Address();
        addr2.setProvince("广东");
        addr2.setCity("深圳");

        list.add(addr);
        list.add(addr2);
        return Result.success(list);
    }
}

案例

  • 在POM中引入dom4j,用来解析XML文件
<!-- 解析XML -->
<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.3</version>
</dependency>
  • 引入工具类XMLParserUtils,实体类Emp,XML文件emp.xml
  • 引入静态页面,放在resources/static目录下
  • 写controller程序
package com.itheima.controller;

import com.itheima.pojo.Emp;
import com.itheima.pojo.Result;
import com.itheima.utils.XmlParserUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class EmpController {
   

    @RequestMapping("/listEmp") // 和前端页面发送请求的路径相同
    public Result list() {
   
        // 1. 加载解析XML文件
        String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
        System.out.println(file);
        List<Emp> empList = XmlParserUtils.parse(file, Emp.class);

        // 2. 对数据进行转换处理 gender和job
        empList.stream().forEach(emp -> {
   
            // 处理gender 1男 2女
            String gender = emp.getGender();
            if ("1".equals(gender)) {
   
                emp.setGender("男");
            } else if ("2".equals(gender)) {
   
                emp.setGender("女");
            }

            // 处理job 1: 讲师, 2: 班主任 , 3: 就业指导
            String job = emp.getJob();
            if ("1".equals(job)) {
   
                emp.setJob("讲师");
            } else if ("2".equals(job)) {
   
                emp.setJob("班主任");
            } else if ("3".equals(job)) {
   
                emp.setJob("就业指导");
            }
        });
        
        // 3. 响应数据
        return Result.success(empList);
    }
}

说明:SpringBoot项目的静态资源默认存放目录为:classpath:/static、classpath:/public、classpath:/resources,classpath是类路径,对Maven项目来说resources目录就是类路径,一般就使用static目录

6. 分层解耦

EmpController中的代码包含数据访问,逻辑处理,接受请求和响应数据。需要尽量让每个类 接口 方法的职责更加单一,这是单一职责原则。能够使类 接口 方法的可读性 可维护性 拓展性更好

三层架构

  • Controller:表示层/控制层,就是编写的Controller程序,负责接收请求、进行处理、响应数据。
  • Service:逻辑层,处理具体的业务逻辑。
  • Dao:数据访问层(Data Access Object)/持久层,负责数据访问操作,包括增删改查。

前端发起请求先到达Controller,调用Service进行逻辑处理,处理的前提是拿到数据,所以再调用Dao层去操作文件中的数据,拿到数据再返回给Service,处理之后的结果返回给Controller,再响应数据给前端。

数据访问Dao层的实现方式可能有很多,如访问文件的数据、数据库的数据、别人提供接口的数据,要灵活的切换各种实现,可以通过面向接口的方式进行编程

需要先定一个Dao的接口来增强灵活性和拓展性

  • 先来一个dao包下的EmpDao接口
package com.itheima.dao;


import com.itheima.pojo.Emp;

import java.util.List;

public interface EmpDao {
   
    // 获取员工列表
    public List<Emp> listEmp();
}

  • 再来一个impl/EmpDaoA(方式很多所以叫A来区分)实现类来实现接口的方法,逻辑就是Controller中解析XML文件获取数据部分
package com.itheima.dao.impl;


import com.itheima.dao.EmpDao;
import com.itheima.pojo.Emp;
import com.itheima.utils.XmlParserUtils;

import java.util.List;

public class EmpDaoA implements EmpDao {
   
    @Override
    public List<Emp> listEmp() {
   
        // 1. 加载解析XML文件
        String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
        System.out.println(file);
        List<Emp> empList = XmlParserUtils.parse(file, Emp.class);

        return empList;
    }
}

  • 然后是Service层,先来一个接口增加灵活性EmpService
package com.itheima.service;


import com.itheima.pojo.Emp;

import java.util.List;

public interface EmpService {
   
    // 获取员工列表
    public List<Emp> listEmp();
}

  • 再来impl/EmpServiceA实现类来实现接口方法,逻辑就是Controller中数据处理部分,但是要从Dao中获取数据
package com.itheima.service.impl;

import com.itheima.dao.EmpDao;
import com.itheima.dao.impl.EmpDaoA;
import com.itheima.pojo.Emp;
import com.itheima.service.EmpService;

import java.util.List;

public class EmpServiceA implements EmpService {
   

    private EmpDao empDao = new EmpDaoA();

    @Override
    public List<Emp> listEmp() {
   
        // 1. 调用Dao获取数据
        List<Emp> empList = empDao.listEmp();

        // 2. 对数据进行转换处理 gender和job
        empList.stream().forEach(emp -> {
   
            // 处理gender 1男 2女
            String gender = emp.getGender();
            if ("1".equals(gender)) {
   
                emp.setGender("男");
            } else if ("2".equals(gender)) {
   
                emp.setGender("女");
            }

            // 处理job 1: 讲师, 2: 班主任 , 3: 就业指导
            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 empList;
    }
}
  • 最后修改Controller程序,从Service中接收处理完的数据并响应给前端
package com.itheima.controller;

import com.itheima.pojo.Emp;
import com.itheima.pojo.Result;
import com.itheima.service.EmpService;
import com.itheima.service.impl.EmpServiceA;
import com.itheima.utils.XmlParserUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class EmpController {
   
    private EmpService service = new EmpServiceA();

    @RequestMapping("/listEmp") // 和前端页面发送请求的路径相同
    public Result list() {
   
        // 调用Service获取数据,然后响应
        List<Emp> empList = service.listEmp();
        // 3. 响应数据
        return Result.success(empList);
    }

//    @RequestMapping("/listEmp") // 和前端页面发送请求的路径相同
//    public Result list() {
   
//        // 1. 加载解析XML文件
//        String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
//        System.out.println(file);
//        List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
//
//        // 2. 对数据进行转换处理 gender和job
//        empList.stream().forEach(emp -> {
   
//            // 处理gender 1男 2女
//            String gender = emp.getGender();
//            if ("1".equals(gender)) {
   
//                emp.setGender("男");
//            } else if ("2".equals(gender)) {
   
//                emp.setGender("女");
//            }
//
//            // 处理job 1: 讲师, 2: 班主任 , 3: 就业指导
//            String job = emp.getJob();
//            if ("1".equals(job)) {
   
//                emp.setJob("讲师");
//            } else if ("2".equals(job)) {
   
//                emp.setJob("班主任");
//            } else if ("3".equals(job)) {
   
//                emp.setJob("就业指导");
//            }
//        });
//
//        // 3. 响应数据
//        return Result.success(empList);
//    }
}

整体过程:前端发起请求之后,先到达Controller程序,他只负责接受请求响应数据,所以直接调用Service层中的方法,Service层只负责逻辑处理,所以直接调用Dao中的方法获取数据,由Dao层来负责数据访问操作,将查询的数据返回给Service,Service处理完后返回给Controller,Controller拿到结果再响应给前端页面。

IOC&DI引入

  • 内聚:各个功能模块内部的功能联系

    员工管理的Service中只会编写与员工相关的逻辑处理,与员工无关的逻辑处理不会放在这个类中。

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

    Controller中new了一个Service的实现类,如果Service层类名发生变化,Controller的代码也需要修改。Service与Dao也有这样的耦合关系。

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

解耦:不能直接new Service对象

提供一个容器来存储一些对象,如果想用EmpServiceA,只需要将其创建的对象A放在容器当中。Controller在运行时需要依赖于EmpService,就可以去容器中查找EmpService这个类型的对象,看到A就是这个类型,就可以从容器中找到对象然后将其赋值给Controller中的empService。

如果要切换实现类,从A切换为B,只需要将B创建的对象放到容器中,Controller运行时也只需要在容器中查找对象,找到对象后赋值给empService。这样即使Service发生变化,Controller代码也不需要修改。

两个问题:

  • 对象怎么交给容器管理
  • 容器怎么为程序提供它所依赖的资源

涉及到Spring中两个概念:

  • 控制反转IoC(Inversion Of Control),Spring框架第一大核心,对象创建的控制权由应用程序转移到了外部容器。反转前由程序自身控制对象创建,反转后由容器控制。容器也叫IoC容器或Spring容器
  • 依赖注入DI(Dependency Injection),容器为程序提供运行时依赖的资源,如Controller运行时依赖EmpService,就可以让容器为它提供。
  • bean对象,IoC容器中创建管理的对象,称之为bean对象

IoC-DI入门

解耦Controller与Service,Service与Dao

  • 将Service层及Dao层的实现类,交给IoC容器管理,为类加注解@Component

  • 为Controller和Service注入运行时依赖的对象,为属性加注解@Autowired

    package com.itheima.dao.impl;
    
    
    import com.itheima.dao.EmpDao;
    import com.itheima.pojo.Emp;
    import com.itheima.utils.XmlParserUtils;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    @Component // 将当前类交给IoC容器管理,成为IoC容器中的bean
    public class EmpDaoA implements EmpDao {
         
        @Override
        public List<Emp> listEmp() {
         
            // 1. 加载解析XML文件
            String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
            System.out.println(file);
            List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
    
            return empList;
        }
    }
    
    

package com.itheima.service.impl;

import com.itheima.dao.EmpDao;
import com.itheima.dao.impl.EmpDaoA;
import com.itheima.pojo.Emp;
import com.itheima.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class EmpServiceA implements EmpService {
@Autowired
private EmpDao empDao;

  @Override
  public List<Emp> listEmp() {
      // 1. 调用Dao获取数据
      List<Emp> empList = empDao.listEmp();

      // 2. 对数据进行转换处理 gender和job
      empList.stream().forEach(emp -> {
          // 处理gender 1男 2女
          String gender = emp.getGender();
          if ("1".equals(gender)) {
              emp.setGender("男");
          } else if ("2".equals(gender)) {
              emp.setGender("女");
          }

          // 处理job 1: 讲师, 2: 班主任 , 3: 就业指导
          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 empList;
  }

}


```java
package com.itheima.controller;

import com.itheima.pojo.Emp;
import com.itheima.pojo.Result;
import com.itheima.service.EmpService;
import com.itheima.service.impl.EmpServiceA;
import com.itheima.utils.XmlParserUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;


@RestController
public class EmpController {

    @Autowired // 运行时,IoC容器会提供该类型的bean对象,并赋值给该变量
    private EmpService service;

    @RequestMapping("/listEmp") // 和前端页面发送请求的路径相同
    public Result list() {
        // 调用Service获取数据,然后响应
        List<Emp> empList = service.listEmp();
        // 3. 响应数据
        return Result.success(empList);
    }
}

这样就完成了控制反转和依赖注入,也完成了层与层之间的解耦,若要切换到EmpServiceB,只需要将A的@Component注释掉,给B添加上@Component注解和自动注入,Dao层和Controller层的代码都不需要改动。

IoC详解

除了@Component外,还提供了三个衍生注解@Controller @Service @Repository来表示bean对象到底归属于哪一层。实际上由于与Mybatis整合``@Repository`用的较少。

某一个类不能归到这三层,又想交给IoC容器管理,就可以使用@Component注解,典型的就是一些工具类。

另外,Controller程序也不需要使用@Controller注解,因为@RestController注解已经包括了@Controller注解。

bean对象的默认名字为类名首字母小写,可以手动指定名字@Repository(value = "daoA&

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1460590.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux 修改开发板网卡eth0的ip地址

win10如何新增电脑ip地址&#xff1a; https://blog.csdn.net/linxinfa/article/details/105817473 ifconfig # 可设置网络设备的状态&#xff0c;或是显示目前的设置。 命令详解&#xff1a;https://www.runoob.com/linux/linux-comm-ifconfig.html 一、临时修改 ifconfig e…

「Qt Widget中文示例指南」如何实现文档查看器?(一)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 文档查看器是一个显…

基于springboot+vue的洗衣店订单管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

【水处理】水环境治理综合服务,污水一体化管控平台

水是人类生活中不可或缺的资源之一。然而&#xff0c;随着人口的增长和工业化的进程&#xff0c;水资源的压力日益增大。由于污染和过度开采&#xff0c;全球许多地区面临水资源短缺的问题。因此&#xff0c;水处理成为了一个至关重要的问题&#xff0c;旨在净化和保护重要的水…

【Python笔记-设计模式】适配器模式

一、说明 适配器模式是一种结构型模式&#xff0c;它使接口不兼容的对象能够相互合作 (一) 解决问题 主要解决接口不兼容问题 (二) 使用场景 当系统需要使用现有的类&#xff0c;但类的接口不符合需求时当需要一个统一的输出接口&#xff0c;但输入类型不可预知时当需要创…

Nginx 反向代理配置

Nginx就不废话了&#xff0c;web服务器。 最近在备案一个域名&#xff0c;想要备案&#xff0c;部署一个服务器&#xff0c;平常很少自己配置Nginx&#xff0c;今天记录下。 1、反向代理 正向代理 指 客户端通过代理访问后端服务 反向代理 指 服务器推出一个客户&#xff0…

最优二叉搜索树 C#实现

最优二叉搜索树 C#实现 介绍一下 上一篇博文搞半天挺烧脑&#xff0c;没搞清楚继续… 主要是练习动态规划算法。最关键的一个是这个最优二叉搜索树能干啥。我认为如果数据稳定&#xff0c;统计出概率来&#xff0c;用最优二叉树保存&#xff0c;以后搜索应该是效率比较高的。…

五种多目标优化算法(MOJS、MOGWO、NSWOA、MOPSO、NSGA2)性能对比,包含6种评价指标,9个测试函数(提供MATLAB代码)

一、5种多目标优化算法简介 1.1MOJS 1.2MOGWO 1.3NSWOA 1.4MOPSO 1.5NSGA2 二、5种多目标优化算法性能对比 为了测试5种算法的性能将其求解9个多目标测试函数&#xff08;zdt1、zdt2 、zdt3、 zdt4、 zdt6 、Schaffer、 Kursawe 、Viennet2、 Viennet3&#xff09;&#xff0…

计算机设计大赛 深度学习人脸表情识别算法 - opencv python 机器视觉

文章目录 0 前言1 技术介绍1.1 技术概括1.2 目前表情识别实现技术 2 实现效果3 深度学习表情识别实现过程3.1 网络架构3.2 数据3.3 实现流程3.4 部分实现代码 4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习人脸表情识别系…

隐匿的炸弹 — C语言的隐式声明

前言 &#xff08;1&#xff09;如果有嵌入式企业需要招聘湖南区域日常实习生&#xff0c;任何区域的暑假Linux驱动实习岗位&#xff0c;可C站直接私聊&#xff0c;或者邮件&#xff1a;zhangyixu02gmail.com&#xff0c;此消息至2025年1月1日前均有效 &#xff08;2&#xff0…

comsol燃料电池+锂离子电池

电化学仿真技术通过对电池微观行为进行研究&#xff0c;明晰电池内部多现象机理&#xff0c;并将其数值化&#xff0c;通过数值法实现对物理特征联合计算&#xff0c;建立完整的电池模型。COMSOL Multiphysics 具有强大的多物理场全耦合仿真分析功能、高效的计算性能&#xff0…

WebRTC最新版报错解决:FileNotFoundError: LASTCHANGE.committime (二十五)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

IP地址定位可以精确到哪里

IP地址定位能够精确到的位置级别取决于多种因素&#xff0c;包括IP地址的分配方式、数据库的质量和更新频率、用户的移动性等。一般而言&#xff0c;IP地址定位可以精确到市级&#xff0c;甚至可以达到街道级别 https://www.ip66.net/?utm-sourceLJ&utm-keyword?1146 但需…

【算法2-1】前缀和、差分与离散化

一、【P3406】海底高铁&#xff08;差分贪心&#xff09;​​​​​​ 由于本题涉及到线路问题&#xff0c;需要统计Uim途径每条线路的次数&#xff0c;而且Uim每次的轨迹都是很长一段路径&#xff0c;所以需要使用一个合理的数据结构来维护区间的变化&#xff0c;首先想到线段…

ncnn之三(补充):window环境下vs2022安装ncnn+protobuf

启动VS2022 下面的 x64 Native Tools Command Prompt for VS2022 protobuf git clone gitgithub.com:protocolbuffers/protobuf.git# 或者 下载 https://github.com/google/protobuf/archive/v3.11.2.zip cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPERelease -D…

ThreadLocal内存泄漏?

ThreadLocal内存泄漏&#xff1f; 在分析ThreadLocal导致的内存泄露前&#xff0c;需要普及了解一下内存泄露、强引用与弱引用以及GC回收机制&#xff0c;这样才能更好的分析为什么ThreadLocal会导致内存泄露呢&#xff1f;更重要的是知道该如何避免这样情况发生&#xff0c;增…

【二十五】【C++】二叉搜索树及其简单实现

二叉搜索树的性质 二叉搜索树&#xff08;BST&#xff09;是一种特殊的二叉树&#xff0c;它具有以下性质&#xff1a; 每个节点都有一个键&#xff08;或值&#xff09;&#xff0c;并且每个节点最多有两个子节点。 左子树上所有节点的键都小于其根节点的键。 右子树上所有…

bugku3

xxx二手交易市场 进去是这样讴歌乱进的页面 查看了一下源代码也没什么 先随便注册一个账号 然后登录 随便看了看&#xff0c;发现可以修改头像 上传文件 随便上传了一个图片 发现他对图片进行了base64加密 解密后得到是 data:image/jpeg;base64 这里重新修改类型为php&a…

栈的实现及其括号匹配问题

栈的实现及其括号匹配问题 一、栈的基本概念及其实现二、栈的数据结构实现三、oj题括号匹配问题解答 一、栈的基本概念及其实现 1.什么是栈&#xff1f;   栈是一种特殊的线性表&#xff0c;只允许栈顶一段插入和删除数据&#xff0c;栈底就是一个容器的底子&#xff0c;栈中…

ubuntu22.04@Jetson Orin Nano之OpenCV安装

ubuntu22.04Jetson Orin Nano之OpenCV安装 1. 源由2. 分析3. 证实3.1 jtop安装3.2 jtop指令3.3 GPU支持情况 4. 安装OpenCV4.1 修改内容4.2 Python2环境【不需要】4.3 ubuntu22.04环境4.4 国内/本地环境问题4.5 cudnn版本问题 5. 总结6. 参考资料 1. 源由 昨天用Jetson跑demo程…