SpringBoot之旅

从零开始的SpringBoot

首先,我们知道从前端发出的请求要传递到Tomcat服务器,然后服务器响应处理前端发送的请求,再把处理后的结果返回到前端,再通过前端渲染到页面展示给用户,那么这个过程中服务器怎么知道数据应该交给谁来完成逻辑处理,又怎么知道处理后的数据该返回给谁呢?

问题一:服务器硬件可以处理这些数据吗?

答案:不能,服务器硬件不具备这样的功能,那么就需要软件来实现这样的功能,Tomcat就是一个服务器的软件.

问题二:那么Tomcat从哪里来的呢?

答案:开发者可以下载并完成Tomcat的配置来使用,但因为有Spring,它可以给我们提供一个内嵌的Tomcat.

问题三:Tomcat认识开发者所写的Controller吗?

答案:开发者编写的Controller默认Tomcat是不认识的.因为Tomcat是Servlet容器(只有实现了Servlet接口的类才可以被Tomcat管理)

问题四:既然Tomcat只是一个容器,那么前端发来的请求是谁来分配?

答案:Spring提供了一个DispatcherServlet,当Tomcat接收到请求会默认交给DispatcherServlet,再由DispatcherServlet转发给编写的Controller.

一.Tomcat的核心功能有3种

  • 接收本次的请求交给DispatcherServlet负责继续分发
  • 解析本次请求数据中的内容封装HttpServletRequest请求对象
  • 解析响应对象HttpServletResponse对象的内容创建满足要求的响应内容

那么了解了数据从前端页面到后端服务器,再到前端页面展示的过程,接下来就是SpringBoot的使用

在类声明前,加上一个@RestController注解,这样才能在程序运行时让Spring扫描到该类

@RestController
public class RController {
}

二.SpringBoot接收请求

(1).普通参数(也称之为简单参数,是指get请求时,地址栏中?后面携带的参数或者是post请求时,请求体中的KEY=VALUE的参数)原始接收请求的方式,这里@RequestMapping()括号中的内容也就是在访问页面时需要在地址后面增加的内容.

@RequestMapping("/p1")
public String parameter1(HttpServletRequest httpServletRequest) {
    //基于HttpServletRequest调用方法getParameter传递要获取的参数名称
    String username = httpServletRequest.getParameter("username");
    String age = httpServletRequest.getParameter("age");
    System.out.println("username : " + username + " age : " + age);
    return "Ok!";
}

这种写法有明显的缺陷,代码的编写繁琐且获取到的参数一律看作String对象,还需要转换成其他类型!

(2).SpringBoot提供了更加方便的写法,普通参数声明形参接收请求的方式,方法的形参则是要接收的预期接收类型的参数.

@RequestMapping("/p1")
public String parameter2(String username, Integer age) {
    System.out.println("username : " + username + " age : " + age);
    return "Ok!";
}

在这里可能出现一种情况,就是在实际开发中可能前端传递的参数的参数名与后端要接收的参数名不一致,那么也可以用映射的方式修改代码中的参数,在形参的前面添加注解@RequestParam(),括号里面可以声明将前端传递的那个参数名映射到我们编写代码的参数名,这样后面的内容就不用修改了,required需要给一个boolean值,表示前端的数据是否必须传递.按照接口文档开发正常情况下不会出现此问题.

@RequestMapping("/p1")
public String parameter3(@RequestParam(name = "name", required = true) String username, Integer age) {
    System.out.println("username : " + username + " age : " + age);
    return "Ok!";
}

(3).SpringBoot接收pojo参数请求的方式

pojo本质上也是普通参数,当多个参数可以被封装为一个对象,直接将对象作为方法的参数声明即可,想要让SpringBoot自动将参数封装到对象的成员变量中,需要保证参数的名称与成员变量的名称一致。

@RequestMapping("/p2")
public String parameter4(Student student) {
    System.out.println("student : " + student);
    return "Ok!";
}

SpringBoot封装对象的原则是尽最大努力封装(能封装的属性就封装,封装不了找不到映射关系的就是默认值)

(4).SpringBoot接收数组/集合参数请求的方式

在方法的参数声明指定类型的数组并且数组的名称与参数名称相同.

@RequestMapping("/p3")
public String parameter5(String[] hobby) {
    System.out.println("hobby : " + Arrays.toString(hobby));
    return "Ok!";
}

除了使用数组也可以使用集合接收多个同类型的参数。但是必须在声明集合参数的时候添加@RequestParam注解。

@RequestMapping("/p4")
public String parameter6(@RequestParam ArrayList<String> hobby) {
    System.out.println("hobby : " + hobby);
    return "Ok!";
}

@RequestParam的核心功能就是告诉SpringBoot将多个参数作为元素添加到集合中。

(5).SpringBoot接收日期参数请求的方式

当参数表示一个时间,基于SpringBoot也可以直接将时间参数转换为一个具体的时间对象进行接收。

首先要明确将时间参数接收为什么样类型的对象

年月日:LocalDate / 时分秒:LocalTime / 年月日时分秒:LocalDateTime

将预期的类型作为方法的参数保证形参名称与参数名称一致 参数前添加@DateTimeFormat注解

并且给pattern属性赋值(解析模板字符串)

@RequestMapping("/p5")
public String parameter7(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime date1,
                         @DateTimeFormat(pattern = "yyyy年MM月dd日") LocalDate date2,
                         @DateTimeFormat(pattern = "HH:mm:ss") LocalTime date3) {
    System.out.println("date1 : " + date1);
    System.out.println("date2 : " + date2);
    System.out.println("date3 : " + date3);
    return "Ok!";
}

@DateTimeFormat注解除了可以标记方法的形式参数也可以标记类的成员变量。

public class Student {
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime time;
}

(6).SpringBoot接收JSON参数请求的方式

JSON数据就是一个指定格式的字符串!可以按照层级的方式表示一个对象或者表示一个数组!

前台与后台之后通用的交互方式基于JSON交互 前台给后台发送JSON数据 后台给前台响应JSON数据

JSON数据的接收必须依赖于对象,且必须是JSON对应的对象

当添加了@RequestBody注解SpringBoot读取到之后就会将本次请求的JSON数据转换为对应的对象,否则就相当于接收pojo参数请求的方式了,易混淆

@RequestMapping("/p6")
public String parameter8(@RequestBody Student student) {
    System.out.println("student : " + student);
    return "Ok!";
}

如果想要将本次请求中的JSON数据转换成对应的一个数组/集合,需要将数组/集合声明为方法的形式参数,形参前面也要加上@RequestBody注解

@RequestMapping("/p7")
public String parameter9(@RequestBody ArrayList<String> arr) {
    System.out.println("arr : " + arr);
    return "Ok!";
}

(7).SpringBoot接收路径参数请求的方式

路径参数就是将该参数包含在实际访问的路径中后的路径

1.明确应该在@RequestMapping中声明什么样的映射路径不变的直接写,变的占位符占位。
2.在方法的形参中声明变量接收路径参数名称一致 类型一致 添加@PathVariable注解
@RequestMapping("/p8/{number}")
public String parameter10(@PathVariable Integer number) {

}

@RequestMapping("/p9/{name}/{age}")
public String parameter11(@PathVariable String name, @PathVariable Integer age) {

}

路径参数之后一般会和RESTful风格搭配使用。

之后请求到底携带什么参数一定要仔细去分析,一般在接口文档中都会主动声明本次请求携带的参数类型,明确了之后再分析应该属于哪种情况。

三.SpringBoot响应数据

通过@ResponseBody注解来进行响应数据,可以用于注解Controller类或者方法上,可以将方法的返回值直接响应,如果返回值类型是 实体对象/集合, 将会转换为JSON格式响应,@RestController = @Controller+@ResponseBody

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
    @AliasFor(
        annotation = Controller.class
    )
    String value() default "";
}

(1).@Reponsebody标记方法,方法返回值是字符串 会直接将字符串作为响应体返回(不会转换为JSON)

//1.@Reponsebody标记方法,方法返回值是字符串 会直接将字符串作为响应体返回(不会转换为JSON)
@RequestMapping("/r1")
public String response1() {
    return "Hello World";
}

(2).@Reponsebody标记方法,方法返回值是自定义对象 会将对象转换为JSON格式后返回 {}

//2.@Reponsebody标记方法,方法返回值是自定义对象 会将对象转换为JSON格式后返回 {}
@RequestMapping("/r2")
public Student response2() {
    Student stu = new Student("张三", 23, "三年级二班", new Phone("小米", 5999.0));
    return stu;
}

(3).@Reponsebody标记方法,方法返回值是数组/集合 会将集合/数组转换为JSON格式后返回 []

//3.@Reponsebody标记方法,方法返回值是数组/集合 会将集合/数组转换为JSON格式后返回 []
@RequestMapping("/r3")
public List<String> response3() {
    ArrayList<String> strList = new ArrayList<>();
    Collections.addAll(strList, "张二狗", "李狗蛋", "刘铁柱");
    return strList;
}

@RequestMapping("/r4")
public Integer[] response4() {
    return new Integer[]{11, 22, 33, 44};
}

(4).SpringBoot全局统一响应结果

基于统一的响应结果可以让前台开发者更方便的处理每次请求的响应数据。【基于统一格式

public class R {
    private Integer code; //响应码
    private String message; //响应信息
    private Object data; //响应数据
    
    public static R error(String message) {
        return new R(2, message, null);
    }
    
    public R() {
    }

    public R(Integer code, String message, Object data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }
}

如果每一个方法的返回值都是R,那么前端每次获取到的结果都是一样的,更方便处理。

核心:在R对象中封装了一个data,里面用于保存真正响应给前台的数据。

四.SpringBoot三层架构

三层架构就是一种的项目的结构模式,基于该种模式开发程序,可以让每一层更加关心自己的核心功能,忽略其他层。

在这里插入图片描述

Controller【控制层】 :接收前台请求,向前台发送响应数据。其他的不关心(数据是怎么处理的)

Service【服务层】 :负责对数据进行逻辑处理,其他的不关心(数据要返回给谁,如何得到的)

Dao【持久层】 :负责对数据进行访问获取,其他的不关心(数据要返回给谁,如何处理)

服务层和持久层要先抽取规范,用于作为该层实现类都具备的功能逻辑!

五.SpringBoot分层解耦

解耦:降低每一层之间的耦合度。

public class EmpController {
    //在控制层中维护一个EmpService服务层接口的实现类对象 (硬编码)
    private EmpService empService = new EmpServiceImpl();
}

以上方式会导致耦合度过高,之后如果要换一个实现类,需要去修改代码!

public class EmpController {
    private EmpService empService = new EmpServiceImpl2();
}

为了解决降低代码的耦合度,我们可以将所有有关联的对象都交给Spring管理,让Spring来控制类与类之间的依赖关系,在此我们引入了两个新的概念,分别是IOC和DI.

在Spring框架中,IOC和DI是两个重要的概念:

  1. IOC(Inversion of Control):控制反转,是一种设计原则,它将对象的创建、依赖注入和生命周期管理等职责交给容器来完成,而不是由应用程序自己去创建和管理对象。通过IOC容器,对象之间的依赖关系会被动态地建立和维护,使得应用程序更加灵活、可扩展和易于测试。
  2. DI(Dependency Injection):依赖注入,是实现IOC的一种具体方式。它通过将依赖关系从代码中分离出来,由容器负责将依赖的对象注入到需要它们的地方。这样可以降低组件之间的耦合度,提高代码的可读性和可维护性。

总结起来,IOC是一种设计原则,而DI是实现IOC的具体方式之一。通过使用IOC和DI,我们可以更好地管理对象之间的依赖关系,提高代码的可扩展性和可测试性。

**(1)**SpringBoot的IOC-控制反转详解

要想把Servies层以及Dao曾的实现类交给IOC容器管理,就需要用到**@Component**注解,该注解标记的类会被Spring扫描到并添加到容器中进行统一管理

Bean的声明

要把某个对象交给IOC容器管理,需要在对应的类上加上如下注释之一:

在这里插入图片描述

注意事项:

声明Bean的时候,可以通过value属性指定bean的名字,如果没有指定,默认为类名的小驼峰形式

使用以上的四个注解都可以声明bean,但是在springboot继承web开发中,声明控制器bean只能用@Controller

IOC容器本质上就是一个Map集合,Map集合中的键默认就是类名的小驼峰,值就是对应的实现类对象

**(2)**SpirngBoot的DI-依赖注入详解

DI:依赖注入 让Spring来维护类中的依赖关系。

public class A {
   private B b; //A依赖B
}

之前代码:

public class EmpController {
    //控制层需要服务层才可以获取到处理后的员工信息List 将服务层对象维护为本类的成员变量
    private IEmpService empService = new EmpService();
}

加入了DI的代码:

public class EmpController {
    @Autowired 
    private IEmpService empService;
}
@Autowired注解的核心功能:当Spring扫描到类中@Autowired标记了成员变量,会认为成员变量是当前类的依赖。

Spring会自动从容器中找到满足成员变量类型的Bean进行赋值!但前提是只有一个Bean满足要求!

DI注入的问题1:容器没有满足要求的Bean

默认无法启动,会出现错误。 因为单独标记@Autowired注解要求必须容器有对应的Bean。

★解决方案(1):往容器存入一个满足要求的Bean

解决方案(2):@Autowired(required=false) 可以在没有满足要求的Bean的时候不出现错误(会使用默认值赋值)

@Autowired(required = false)
private IEmpService empService;
DI注入的问题2:容器中有满足要求的多个Bean

默认无法启动,会出现错误。多个Bean满足要求不知道注入哪个!

解决方案(1) : 添加@Primary注解 加到依赖的类上。哪个类上标记了@Primary注解该类在注入的时候享有优先权。
@Primary
@Service
public class EmpService2 implements IEmpService {}

有一些情况无法满足。要注入的类是第三方的类,则无法添加注解。无法提高优先级。

解决方案(2) : 在声明依赖的时候添加@Qualifier注解 并且指定要注入的Bean的容器名称
@Autowired
@Qualifier("empService2") //该注解必须和@Autowired一起出现否则没有意义 核心功能:将基于类型注入改为基于容器的名称注入
private IEmpService empService;
★解决方案(3) : 不使用@Autowired注解(Spring框架中的注解) 改为使用@Resource注解(Java原生代码中的注解)

使用@Resource注解与使用@Autowired注解达到的效果是一样。

@Resource
private IEmpService empService;

只通过@Resource注解标记默认也是基于类型注入。

如果想按照容器的名称注入,不需要额外的注解。由于@Resource是Java原生代码中的注解,所以使用该注解在任何框架中都可以,而且省去了@Autowired+@Qualifier(“bean名称”)的编写,更加方便

@Resource(name = "ergou")
private IEmpService empService;

使用@Resource注解替换@Autowired注解!

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值