• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
tyrantblue
博客园    首页    新随笔    联系   管理    订阅  订阅
Java学习笔记 (十五) Spring入门

Spring Boot入门

介绍

官网: spring.io

Spring发展到今天已经形成了一种开发生态圈, Spring提供了若干个子项目, 每个项目用于完成特定的功能

快速入门

需求: 使用SpringBoot开发一个web应用, 浏览器发起请求 /hello后, 给浏览器返回字符串"Hello World"

步骤:

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

ppf0m3d.png

ppf0V4e.png

ppf0e9H.png

在com.tyrant.hello包下创建HelloControl请求处理类

package com.tyrant.hello;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

//请求处理类
@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String test1(){

        System.out.println("Hello Spring");
        return "Hello Spring";
    }
}

创建时遇到的一些问题:

  1. 创建时版本选择了3.x版本, 导致默认的jdk版本为17, 运行时报错, 应该选择2.x版本
  2. 删除时直接删除了本地文件, 没有删除项目中的模块, 然后创建之前同名的模块时发现报模块已存在, 无法创建, 解决方法是在项目结构中将模块删除
  3. 引入@RestControll注解时放的位置不对,,,,是我太粗心
  4. 我将创建spring脚手架模块时Server URL改为了阿里云的镜像源: https://start.aliyun.com/

HTTP协议

介绍

概念: Hyper Text Transfer Protocol, 超文本传输协议, 规定了浏览器和服务器之间数据传输的规则

特点:

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

请求协议

HTTP请求数据格式

  1. 请求行: 请求数据第一行(请求方式, 资源路径, 协议)

  2. 请求头: 第二行开始, 格式key: val

    key value
    Host 请求的主机名
    User-Agent 浏览器版本, 例如Chrome浏览器的标识类似Mozilla/5.0 ... Chrome/79, IE浏览器的标识类似Mozilla/5.0(Windows NT ...) like Gecko
    Accept 表示浏览器能接收的资源类型, 如text/, image/或者/表示所有
    Accept-Language 表示浏览器偏好的语言, 服务器可以据此返回不同语言的网页
    Accept-Encoding 表示浏览器可以支持的压缩类型, 如gzip, deflate等
    Content-Type 请求主题的数据类型
    Content-Length 请求主体的大小
  3. 请求体: POST请求独有, 存放请求参数

请求方式-GET: 请求参数在请求行中, 没有请求体, 如/brand/findAll?name=OPPO&status=1 GET请求大小是有限制的

请求方式-POST: 请求参数在请求体中, POST请求大小是没有限制的

响应协议

HTTP相应格式

  1. 响应行: 响应数据第一行(协议, 状态码, 描述)
  2. 响应头: 第二行开始, 格式key: value
  3. 响应体: 最后一部分,存放响应数据
状态码分类 描述
1xx 响应中-临时状态码, 表示请求已接收, 告诉客户端应该继续请求或者如果它已完成则忽略它
2xx 成功-表示请求已经被成功接收, 处理已完成
3xx 重定向-重定向到其他地方, 让客户端再发起一次请求以完成整个处理
4xx 客户端错误-处理发生错误, 责任在客户端, 如: 请求了不存在的资源, 客户端未被授权, 禁止访问等
5xx 服务器错误-处理发生错误, 责任在服务端, 如: 程序抛出异常等

状态码大全: https://cloud.tencent.com/developer/chapter/13553

key value
Content-Type 表示该响应内容的类型, 例如text/html, application/json
Content-Length 表示该相应内容的长度(字节数)
Content-Encoding 表示该相应压缩算法, 例如gzip
Cache-Control 指示客户端应如何缓存, 例如max-age=300表示可以最多缓存300秒
Set-Cookie 告诉浏览器为当前页面所在的域设置cookie

Tomcat

介绍

web服务器

  • 对HTTP协议操作进行封装, 简化web程序开发
  • 部署web项目, 对外提供网上信息浏览服务

概念:

  • Tomcat时Apache软件基金会的一个核心项目, 是一个开源免费的轻量级Web服务器, 支持Servlet/JSP少量JavaEE规范

JavaEE:

  • Java Enterprise Edition, Java企业版, 指Java企业级开发的技术规范总和, 包含13项技术规范: JDBC, JNDI, EJB, RMI, JSP, Servlet, XML, JMS, Java IDL, JTS, JTA, JavaMail, JAF

Tomcat也被称为Web容器, Servlet容器, Servlet程序需要依赖于Tomcat才能运行

官网:

  • https://tomcat.apache.org/

基本使用

ppfHQAO.png

ppfHfET.png

配置Tomcat端口号(conf/server.xml)

<!-- 69行, 配置port -->
<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

部署

将项目放置到webapps目录下, 即部署完成

入门程序解析(内嵌tomcat)

起步依赖:

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

内嵌Tomcat服务器

  • 基于Springboot开发的web应用程序, 内置了tomcat服务器, 当启动类运行时, 会自动启动内嵌的tomcat服务器

请求响应

请求

Postman

介绍: Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件

作用: 常用于进行接口测试

安装: 搜索postman官网下载安装即可

简单参数

原始方式: 在原始的web程序中, 获取请求参数, 需要通过HttpServletRequest对象手动获取

SpringBoot方式:

  • 简单参数只要参数名与形参变量名相同, 定义形参即可接收参数
  • 如果方法形参名称与请求参数名称不匹配, 可以使用@RequestParam完成映射

注意: @PequestParam中的required属性默认为true, 代表该请求参数必须传递, 如果不传递将报错, 如果该参数是可选的, 可以将required属性设置为false

/*测试请求参数接收*/
@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方式
/*    @RequestMapping("/simpleParam")
    public String simpleParam(String name , Integer age){

        System.out.println(name + ": " + age);
        return "OK";
    }*/

    @RequestMapping("/simpleParam")
    public String simpleParam(@RequestParam(name="name") String username , Integer age){

        System.out.println(username + ": " + age);
        return "OK";
    }
}

实体参数

简单实体对象: 请求参数名与形参对象名相同, 定义POJO接收即可

POJO: Plain Old Java Object

复杂实体对象: 请求参数名与形参对象属性名相同, 按照对象层次结构即可接收嵌套POJO属性参数

    //简单实体参数
    @RequestMapping("/simplePojo")
    public String simplePojo(User user){
        System.out.println(user);

        return "OK";
    }

    //复杂实体参数
    @RequestMapping("/complexPojo")
    public String complexPojo(User user){
        System.out.println(user);

        return "OK";
    }

数组集合参数

数组参数: 请求参数名与型参数组名称相同且请求参数为多个, 定义数组类型形参即可接收参数

集合参数: 请求参数名与形参集合名称相同且请求参数为多个, @RequestParam 绑定参数关系

    //数组参数
    @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";
    }

日期参数

日期参数: 使用@DataTimeFormat 注解完成日期参数格式转换

    //时间参数
    @RequestMapping("/dataParam")
	//注意时间的格式
    public String dataParam(@DateTimeFormat (pattern = "yyyy-MM-dd HH:mm:ss")LocalDateTime updateTime){
        System.out.println(updateTime);
        return "OK";
    }

JSON参数

JSON参数: JSON数据键名与形参对象属性名相同, 定义POJO类型形参即可接收参数, 需要使用@RequsetBody标识符

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

路径参数

路径参数: 通过请求URL直接传递参数, 使用{...}来标识该路径参数, 需要使用@PathVariable 获取路径参数

    //路径参数
    @RequestMapping("/path/{id}")
    public String pathParam(@PathVariable Integer id){

        System.out.println(id);
        return "OK";
    }

    @RequestMapping("/path/{id}/{name}")
    public String pathParam(@PathVariable Integer id, @PathVariable String name){

        System.out.println(id+"/"+name);
        return "OK";
    }

响应

响应数据的核心注解@ResponseBody

类型: 方法注解, 类注解

位置: Controller方法上/类上

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

说明: @RestController = @Controller + @ResponseBody

@RestController
public class ResponseController {
    @RequestMapping("/hello")
    public String hello(){
        System.out.println("hello spring");
        return "hello spring";
    }


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

    @RequestMapping("/listAddr")
    public List<Address> listAddr(){

        List<Address> list = new ArrayList<>();

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

        Address addr2 = new Address();
        addr2.setCity("深圳");
        addr2.setProvince("广东");
        list.add(addr);
        list.add(addr2);
        System.out.println(list);

        return list;
    }
}

统一响应结果: Result(code, msg, data)

public class Result {
    private Integer code;//1: 成功, 0: 失败
    private String msg;//提示信息
    private Object data;//数据 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 +
                '}';
    }
}
@RestController
public class ResponseController {
    @RequestMapping("/hello")
    public Result hello(){
        System.out.println("hello spring");
        //return new Result(1, "success", "hello spring");
        return Result.success("hello spring");
    }


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

    @RequestMapping("/listAddr")
    public Result listAddr(){

        List<Address> list = new ArrayList<>();

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

        Address addr2 = new Address();
        addr2.setCity("深圳");
        addr2.setProvince("广东");
        list.add(addr);
        list.add(addr2);
        System.out.println(list);

        return Result.success(list);
    }
}

案例

需求: 获取员工数据, 返回同意响应数据, 在页面渲染展示

步骤:

  1. 在pom.xml文件中引入dom4j的依赖, 用于解析XML文件
  2. 引入资料中提供的解析XML的工具类XMLParserUtils, 对应的实体类Emp, XML文件emp.xml
  3. 引入资料中提供的静态页面文件, 放在resources下的static目录下
  4. 编写Controler程序, 处理请求, 响应数据

分层解耦

三层架构

controller: 控制层, 接受前端发送的请求, 对请求进行处理, 并响应数据

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

dao: 数据访问层(Data Access Object) (持久层), 负责数据访问操作, 包括数据的增删改查

分层解耦

内聚: 软件中各个功能模块内部的功能联系

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

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

控制反转: Inversion Of Control, 简称IOC, 对象的创建控制权由程序自身转移到外部(容器), 这种思想称为控制反转

依赖注入: Denpendency Injection, 简称DI, 容器为应用程序提供运行时, 所依赖的资源, 称之为依赖注入

Bean对象: IOC容器中创建, 管理的对象, 称之为bean

IOC & DI 入门

步骤:

  1. Service层及Dao层的实现类, 交给IOC层容器处理
  2. 为Controller及Service注入运行时, 依赖的对象
  3. 运行测试

IOC详解

注解 说明 位置
@Component 声明bean的基础注解 不属于以下三类时, 用此注解
@Controller @Component的衍生注解 标注在控制器上
@Service @Component的衍生注解 标注在业务层上
@Repository @Component的衍生注解 标注在数据访问类上(由于与mybatis整合, 用的少)

注意事项:

  • 声明bean的时候, 可以通过value属性指定bean的名字, 如果没有指定, 默认为类名首字母小写
  • 使用以上四个注解都可以声明bean, 但是springboot集成web开发中, 声明控制器bean只能用@Controller

Bean组件扫描

  • 前面声明bean的四大注解, 想要生效, 还需要被组件扫描注解@ComponentScan扫描
  • @ComponentScan注解虽然没有显式配置, 但是实际上已经包含在了启动类声明注解@SpringBootApplication中, 默认扫描的范围是启动类所在包及其子包

解决方案一: 手动设置扫描包

解决方案二: 按照规范放置包的位置

DI详解

@Autowired注解, 默认是按照类型进行, 如果存在多个相同类型的bean, 将会报出如下错误

Field empService in com.tyrant.controller.EmpController required a single bean, but 2 were found:
	- empServiceA: defined in file [D:\Code\java\webProject01\springboot-quickstart\target\classes\com\tyrant\service\impl\EmpServiceA.class]
	- empServiceB: defined in file [D:\Code\java\webProject01\springboot-quickstart\target\classes\com\tyrant\service\impl\EmpServiceB.class]


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

解决方案:

  1. @Primary指定想要生效的类

    @Primary
    @Service
    Public class EmpServiceA implement EmpService{}
    
  2. @Autowired + @Qualifier指定想要装载的bean对象

    @RestController
    public class EmpController{
        @Autowired
        @Qualifier("empServiceA")
        private EmpService empService;
    }
    
  3. @Resource(name = "bean的名称")

    @RestController
    public class EmpController{
        @Resource(name = "empServiceB")
        private EmpService empService;
    }
    

@Resource 与Autowired区别

  1. @Autowired是spring框架提供的注解, 而@Resource是JDK提供的注解
  2. @Autowired默认是按照类型注入, 而@Resource默认是按照名称注入
posted on 2023-04-15 22:24  雪化凛然  阅读(142)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3