介绍
Spring Boot可以帮助我们非常快速的构建应用程序、简化开发、提高效率。
SpringBootWeb快速入门
idea创建SpringBoot项目
(需要联网)
定义请求处理类
package com.study.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Auther lmy
* @date 2024-04-05 16:08
* @Description This is description of code
*/
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello() {
System.out.println("Hello world~");
return "Hello world~";
}
}
运行测试
Web分析
浏览器:
-
输入网址:
http://192.168.100.11:8080/hello
-
通过IP地址192.168.100.11定位到网络上的一台计算机
我们之前在浏览器中输入的localhost,就是127.0.0.1(本机)
-
通过端口号8080找到计算机上运行的程序
localhost:8080
, 意思是在本地计算机中找到正在运行的8080端口的程序 -
/hello是请求资源位置
-
资源:对计算机而言资源就是数据
-
web资源:通过网络可以访问到的资源(通常是指存放在服务器上的数据)
-
localhost:8080/hello
,意思是向本地计算机中的8080端口程序,获取资源位置是/hello的数据-
8080端口程序,在服务器找/hello位置的资源数据,发给浏览器
-
-
服务器:(可以理解为ServerSocket)
-
接收到浏览器发送的信息(如:/hello)
-
在服务器上找到/hello的资源
-
把资源发送给浏览器
我们在JavaSE阶段学习网络编程时,有讲过网络三要素:
IP :网络中计算机的唯一标识
端口 :计算机中运行程序的唯一标识
协议 :网络中计算机之间交互的规则
问题:浏览器和服务器两端进行数据交互,使用什么协议?
答案:http协议
HTTP协议
概述
HTTP协议是无状态协议,比如:账户登录后才能访问某个页面信息数据,但HTTP协议是无状态协议,在执行登录后请求响应已经结束,第二次访问信息数据时,又是另一次请求,两次请求响应都是独立的,在第二次请求时无法记录第一次请求的信息是否登录。后续学习到“web会话”能解决此问题。
请求协议
响应协议
常见的响应状态码
一般记住三个
200 | 客户端请求成功。 |
404 | 请求资源不存在,般是URL输入有误,或者网站资源被删除了。 |
500 | 服务器发生不可预期的错误。 |
响应头
协议解析
web服务器
Web服务器 - Tomcat
简介
Tomcat基本使用
(Springboot已经内置Tomcat)
安装+卸载+启动+停止
常见问题
配置Tomcat端口号
Tomcat项目部署
Springboot - 入门程序解析
请求响应
概述
请求
后端开发过程中,每开发完一个功能就需要对这个功能接口进行测试,由于是前后端分离开发,所以我们是没有前端页面的,这个时候我们该怎么测试?在入门程序阶段,直接打开浏览器,然后在浏览器的地址栏直接输入一个地址,这样就访问到开发的web应用,但是浏览器地址栏所发起的请求全部都是get请求,如果要测试post方式的请求,需要自己去写前端代码,然后再进行后端的功能接口测试,那这是比较繁琐的,因此我们就可以借助一款功能强大的接口测试工具postman,通过postman就可以轻松地解决各种接口测试的需求。
接口测试工具---postman
postman下载:https://app.getpostman.com/app/download/win64
Apifox下载:https://apifox.com/ 纯中文,推荐
接口测试工具---Apifox
1.创建项目
2.创建接口
运行springboot项目启动类---(以上案例)
请求方式 get
请求路径 http://localhost:8080/hello
点击发送
简单参数
代码:
Apifox接口工具测试
springboot方式
get请求
post请求
以上方式如果前端参数名和形参变量名不一致,不会报错,但接受不到参数的value,会显示null
如:
如果方法形参名称与请求参数名称不匹配,可以使用@RequestParam 完成映射。
以下两种方式均可,name和value接收请求参数名都行,可直接写参数值,不加name和value
@RequestParam中的required属性默认为true,代表该请求参数必须传递,如果不传递将报错,如下,不传递报400错误,代表客户端错误(未传递参数)。如果该参数是可选的,可以将required属性设置为false。
控制台报错:
Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'username' for method parameter type String is not present]
required属性设置为false
简单实体参数
定义User实体类:
package com.study.pojo;
/**
* @Auther lmy
* @date 2024-04-07 23:08
* @Description This is description of code
*/
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 +
'}';
}
}
在RequestController类中定义simplePojo(User user)方法
package com.study.controller;
import com.study.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Auther lmy
* @date 2024-04-07 23:10
* @Description This is description of code
*/
@RestController
public class RequestController {
@RequestMapping("/simplePojo")
public String simplePojo(User user) {
System.out.println(user);
return user.getName() + " " + user.getAge();
}
}
请求中传递name、age两个参数
复杂实体参数
定义User1实体类:三个属性:姓名、年龄、Address类型的地址
package com.study.pojo;
/**
* @Auther lmy
* @date 2024-04-07 23:28
* @Description This is description of code
*/
public class User1 {
private String name;
private Integer age;
private Address address;
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;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "User1{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}
定义Address实体类:两个属性:
private String province; //省
private String city; //市
package com.study.pojo;
/**
* @Auther lmy
* @date 2024-04-07 23:32
* @Description This is description of code
*/
public class Address {
private String province; //省
private 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 + '\'' +
'}';
}
}
在RequestController类中定义complexPojo(User1 user)方法
请求测试:
控制台输出:
数组集合参数
数组参数
集合参数
集合参数相对集合参数。多一个@RequestParam绑定参数关系
默认情况下,请求中参数名相同的多个值,是封装到数组。如果要封装到集合,要使用@RequestParam绑定参数关系
日期参数
因为日期的格式多种多样(如:2022-12-12 10:05:45 、2022/12/12 10:05:45),那么对于日期类型的参数在进行封装的时候,需要通过@DateTimeFormat注解,以及其pattern属性来设置日期的格式。
-
@DateTimeFormat注解的pattern属性中指定了哪种日期格式,前端的日期参数就必须按照指定的格式传递。
-
后端controller方法中,需要使用Date类型或LocalDateTime类型,来封装传递的参数。
Controller方法:
Json参数
json格式数据需要放在请求体中
服务端Controller方法接收JSON格式数据:
-
传递json格式的参数,在Controller中会使用实体类进行封装。
-
封装规则:JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接收参数。需要使用 @RequestBody标识。
-
@RequestBody注解:将JSON数据映射到形参的实体类对象中(JSON中的key和实体类中的属性名保持一致)
路径参数
在现在的开发中,经常还会直接在请求的URL中传递参数。例如:
http://localhost:8080/user/1 http://localhost:880/user/1/0
上述的这种传递请求参数的形式呢,我们称之为:路径参数。
学习路径参数呢,主要掌握在后端的controller方法中,如何接收路径参数。
路径参数:
-
前端:通过请求URL直接传递参数
-
后端:使用{…}来标识该路径参数,需要使用@PathVariable获取路径参数
单个路径参数
多个路径参数
响应
@ResponseBody
package com.study.controller;
import com.study.pojo.Address;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* @Auther lmy
* @date 2024-04-08 2:39
* @Description 测试响应数据
*/
@RestController
public class ResponseController {
@RequestMapping("/hello1")
public String hello() {
System.out.println("Hello World ~");
return "Hello World ~";
}
@RequestMapping("/getArr")
public Address getAddr() {
Address address = new Address();
address.setProvince("陕西");
address.setCity("西安");
System.out.println(address);
return address;
}
@RequestMapping("/listAddr")
public List<Address> listAddr() {
List<Address> list = new ArrayList<>();
Address addr1 = new Address();
addr1.setProvince("陕西");
addr1.setCity("西安");
Address addr2 = new Address();
addr2.setProvince("山西");
addr2.setCity("运城");
list.add(addr1);
list.add(addr2);
System.out.println(list);
return list;
}
}
统一响应结果
在前后端分离开发中,用上面形式返回字符串、json、json格式数组,前端接受到的响应数据很随意,无任何规范。如果没有一套统一的项目规范,前端人员发起请求,访问到后端所开发的接口,响应回去的数据各式各样,前端需要拿到结果进行解析,前后端开发成本就会增加,最终我们会发项目,不便管理,而且很难维护。所以,在项目开发中,一般都会给所有的功能接口设置一个统一的响应结果,而既然是一个统一的响应结果,就需要做到通用性,基本上要能够满足所有的业务场景,而这个统一的响应结果,可以考虑使用一个实体对象result来进行接收
编写一个Result类
package com.study.pojo;
/**
* 统一响应结果封装类
*/
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 +
'}';
}
}
测试
案例
步骤
1.在pom.xml文件中引入dom4j的依赖,用于解析XML文件
<!--dom4j依赖,解析xml-->
<!-- https://mvnrepository.com/artifact/org.dom4j/dom4j -->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
2.引入资料中提供的解析XML的工具类XMLParserUtils、对应的实体类Emp、XML文件 emp.xml
(1)解析XML的工具类XMLParserUtils
package com.study.utils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
public class XmlParserUtils {
public static <T> List<T> parse(String file, Class<T> targetClass) {
ArrayList<T> list = new ArrayList<T>(); //封装解析出来的数据
try {
//1.获取一个解析器对象
SAXReader saxReader = new SAXReader();
//2.利用解析器把xml文件加载到内存中,并返回一个文档对象
Document document = saxReader.read(new File(file));
//3.获取到根标签
Element rootElement = document.getRootElement();
//4.通过根标签来获取 user 标签
List<Element> elements = rootElement.elements("emp");
//5.遍历集合,得到每一个 user 标签
for (Element element : elements) {
//获取 name 属性
String name = element.element("name").getText();
//获取 age 属性
String age = element.element("age").getText();
//获取 image 属性
String image = element.element("image").getText();
//获取 gender 属性
String gender = element.element("gender").getText();
//获取 job 属性
String job = element.element("job").getText();
//组装数据
Constructor<T> constructor = targetClass.getDeclaredConstructor(String.class, Integer.class, String.class, String.class, String.class);
constructor.setAccessible(true);
T object = constructor.newInstance(name, Integer.parseInt(age), image, gender, job);
list.add(object);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
}
(2)实体类Emp
package com.study.pojo;
public class Emp {
private String name;
private Integer age;
private String image;
private String gender;
private String job;
public Emp() {
}
public Emp(String name, Integer age, String image, String gender, String job) {
this.name = name;
this.age = age;
this.image = image;
this.gender = gender;
this.job = job;
}
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;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
@Override
public String toString() {
return "Emp{" +
"name='" + name + '\'' +
", age=" + age +
", image='" + image + '\'' +
", gender='" + gender + '\'' +
", job='" + job + '\'' +
'}';
}
}
(3)XML文件 emp.xml(放在resources配置下)
<?xml version="1.0" encoding="UTF-8" ?>
<emps>
<emp>
<name>金毛狮王</name>
<age>55</age>
<image>https://web-framework.oss-cn-hangzhou.aliyuncs.com/web/1.jpg</image>
<!-- 1: 男, 2: 女 -->
<gender>1</gender>
<!-- 1: 讲师, 2: 班主任 , 3: 就业指导 -->
<job>1</job>
</emp>
<emp>
<name>白眉鹰王</name>
<age>65</age>
<image>https://web-framework.oss-cn-hangzhou.aliyuncs.com/web/2.jpg</image>
<gender>1</gender>
<job>1</job>
</emp>
<emp>
<name>青翼蝠王</name>
<age>45</age>
<image>https://web-framework.oss-cn-hangzhou.aliyuncs.com/web/3.jpg</image>
<gender>1</gender>
<job>2</job>
</emp>
<emp>
<name>紫衫龙王</name>
<age>38</age>
<image>https://web-framework.oss-cn-hangzhou.aliyuncs.com/web/4.jpg</image>
<gender>2</gender>
<job>3</job>
</emp>
</emps>
3.引入资料中提供的静态页面文件,放在resources下的static目录下
(文件在附件中)
Springboot项目的静态资源(html,css,js等前端资源)默认存放目录为:classpath:/static 、 classpath:/public、 classpath:/resources
(classpath:类路径,对于maven来说resources就是类路径)
4.编写EmpController类,处理请求,响应数据
package com.study.controller;
import com.study.pojo.Emp;
import com.study.pojo.Result;
import com.study.utils.XmlParserUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @Auther lmy
* @date 2024-04-11 1:14
* @Description 加载员工数据
*/
@RestController
public class EmpController {
@RequestMapping("/listEmp") //与前端页面emp.html中的请求路径一致
public Result list() {
//1. 加载并解析emp.xml
String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
System.out.println(file);
List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
System.out.println(empList);
//2.对数据进行转换处理 -gender,job
for (Emp emp : empList) {
//处理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("就业指导");
}
}
System.out.println(empList);
// 3.响应数据
return Result.success(empList);
}
}
Apifox测试接口
访问前端页面
分析
上述案例的功能,我们虽然已经实现,但是呢,我们会发现案例中:解析XML数据,获取数据的代码,处理数据的逻辑的代码,给页面响应的代码全部都堆积在一起了,全部都写在controller方法中了。
当前程序的这个业务逻辑还是比较简单的,如果业务逻辑再稍微复杂一点,我们会看到Controller方法的代码量就很大了。
-
当我们要修改操作数据部分的代码,需要改动Controller
-
当我们要完善逻辑处理部分的代码,需要改动Controller
-
当我们需要修改数据响应的代码,还是需要改动Controller
这样呢,就会造成我们整个工程代码的复用性比较差,而且代码难以维护。 那如何解决这个问题呢?其实在现在的开发中,有非常成熟的解决思路,那就是分层开发。
分层解耦
三层架构
在我们进行程序设计以及程序开发时,尽可能让每一个接口、类、方法的职责更单一些(单一职责原则)。
单一职责原则:一个类或一个方法,就只做一件事情,只管一块功能。
这样就可以让类、接口、方法的复杂度更低,可读性更强,扩展性更好,也更利用后期的维护。
那其实我们上述案例的处理逻辑呢,从组成上看可以分为三个部分:
-
数据访问:负责业务数据的维护操作,包括增、删、改、查等操作。
-
逻辑处理:负责业务逻辑处理的代码。
-
请求处理、响应数据:负责,接收页面的请求,给页面响应数据。
按照上述的三个组成部分,在我们项目开发中呢,可以将代码分为三层:
代码实现:
数据访问层:负责数据的访问操作,包含数据的增、删、改、查
-
数据访问接口
package com.study.dao;
import com.study.pojo.Emp;
import java.util.List;
/**
* @Auther lmy
* @date 2024-04-11 2:08
* @Description This is description of code
*/
public interface EmpDao {
//访问员工列表数据
public List<Emp> listEmp();
}
-
数据访问实现类
package com.study.dao.impl;
import com.study.dao.EmpDao;
import com.study.pojo.Emp;
import com.study.utils.XmlParserUtils;
import java.util.List;
/**
* @Auther lmy
* @date 2024-04-11 2:09
* @Description This is description of code
*/
public class EmpDaoA implements EmpDao {
@Override
public List<Emp> listEmp() {
//1. 加载并解析emp.xml
String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
System.out.println(file);
List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
System.out.println(empList);
return empList;
}
}
业务逻辑层:处理具体的业务逻辑
-
业务接口
package com.study.service;
import com.study.pojo.Emp;
import java.util.List;
/**
* @Auther lmy
* @date 2024-04-11 2:12
* @Description This is description of code
*/
public interface EmpService {
//获取员工列表
public List<Emp> listEmp();
}
-
业务实现类
package com.study.service.impl;
import com.study.dao.EmpDao;
import com.study.dao.impl.EmpDaoA;
import com.study.pojo.Emp;
import com.study.service.EmpService;
import java.util.List;
/**
* @Auther lmy
* @date 2024-04-11 2:14
* @Description This is description of code
*/
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
for (Emp emp : empList) {
//处理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("就业指导");
}
}
System.out.println(empList);
return empList;
}
}
控制层:接收前端发送的请求,对请求进行处理,并响应数据
package com.study.controller;
import com.study.pojo.Emp;
import com.study.pojo.Result;
import com.study.service.EmpService;
import com.study.service.impl.EmpServiceA;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @Auther lmy
* @date 2024-04-11 2:24
* @Description This is description of code
*/
@RestController
public class EmpControllerA {
private EmpService empService = new EmpServiceA();
@RequestMapping("listEmp")
public Result list() {
//调用service,获取数据
List<Emp> empList = empService.listEmp();
// 2.响应数据
return Result.success(empList);
}
}
效果和案例效果一致
分层解耦
耦合问题
首先需要了解软件开发涉及到的两个概念:内聚和耦合。
-
内聚:软件中各个功能模块内部的功能联系。
-
耦合:衡量软件中各个层/模块之间的依赖、关联的程度。
软件设计原则:高内聚低耦合。
高内聚指的是:一个模块中各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高,即 "高内聚"。
低耦合指的是:软件中各个层、模块之间的依赖关联程序越低越好。
程序中高内聚的体现:
- EmpServiceA类中只编写了和员工相关的逻辑处理代码
程序中耦合代码的体现:
-
把业务类变为EmpServiceB时,需要修改controller层中的代码
高内聚、低耦合的目的是使程序模块的可重用性、移植性大大增强。
解耦思路
之前我们在编写代码时,需要什么对象,就直接new一个就可以了。 这种做法呢,层与层之间代码就耦合了,当service层的实现变了之后, 我们还需要修改controller层的代码。
那应该怎么解耦呢?
-
首先不能在EmpController中使用new对象。代码如下:
-
此时,就存在另一个问题了,不能new,就意味着没有业务层对象(程序运行就报错),怎么办呢?
-
我们的解决思路是:
-
提供一个容器,容器中存储一些对象(例:EmpService对象)
-
controller程序从容器中获取EmpService类型的对象
-
-
我们想要实现上述解耦操作,就涉及到Spring中的两个核心概念:
IOC&DI
IOC&DI入门
任务:完成Controller层、Service层、Dao层的代码解耦
思路:
1. 删除Controller层、Service层中new对象的代码
2. Service层及Dao层的实现类,交给IOC容器管理
3. 为Controller及Service注入运行时依赖的对象
- Controller程序中注入依赖的Service层对象
- Service程序中注入依赖的Dao层对象
第1步:删除Controller层、Service层中new对象的代码
第2步:Service层及Dao层的实现类,交给IOC容器管理
-
使用Spring提供的注解:@Component ,就可以实现类交给IOC容器管理
第3步:为Controller及Service注入运行时依赖的对象
-
使用Spring提供的注解:@Autowired ,就可以实现程序运行时IOC容器自动注入需要的依赖对象
完整的三层代码:
Dao层:
package com.study.dao.impl;
import com.study.dao.EmpDao;
import com.study.pojo.Emp;
import com.study.utils.XmlParserUtils;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @Auther lmy
* @date 2024-04-11 2:09
* @Description This is description of code
*/
@Component //将当前类交给ioc容器管理,成为ioc容器中的bean
public class EmpDaoA implements EmpDao {
@Override
public List<Emp> listEmp() {
//1. 加载并解析emp.xml
String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
System.out.println(file);
List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
System.out.println(empList);
return empList;
}
}
Service层:
package com.study.service.impl;
import com.study.dao.EmpDao;
import com.study.pojo.Emp;
import com.study.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @Auther lmy
* @date 2024-04-11 2:14
* @Description This is description of code
*/
@Component //将当前类交给ioc容器管理,成为ioc容器中的bean
public class EmpServiceA implements EmpService {
@Autowired //运行时,ioc容器会提供该类型的bean对象,并赋值给该变量 ---依赖注入
private EmpDao empDao;
@Override
public List<Emp> listEmp() {
//1.调用dao,获取数据
List<Emp> empList = empDao.listEmp();
//2.对数据进行转换处理 -gender,job
for (Emp emp : empList) {
//处理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("就业指导");
}
}
System.out.println(empList);
return empList;
}
}
Controller层:
package com.study.controller;
import com.study.pojo.Emp;
import com.study.pojo.Result;
import com.study.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @Auther lmy
* @date 2024-04-11 2:24
* @Description This is description of code
*/
@RestController
public class EmpControllerA {
@Autowired //运行时,ioc容器会提供该类型的bean对象,并赋值给该变量 ---依赖注入
private EmpService empService;
@RequestMapping("listEmp")
public Result list() {
//调用service,获取数据
List<Emp> empList = empService.listEmp();
// 2.响应数据
return Result.success(empList);
}
}
IOC详解
通过IOC和DI的入门程序呢,我们已经基本了解了IOC和DI的基础操作。接下来呢,我们学习下IOC控制反转和DI依赖注入的细节。
bean的声明
前面我们提到IOC控制反转,就是将对象的控制权交给Spring的IOC容器,由IOC容器创建及管理对象。IOC容器创建的对象称为bean对象。
在之前的入门案例中,要把某个对象交给IOC容器管理,需要在类上添加一个注解:@Component
而Spring框架为了更好的标识web应用程序开发当中,bean对象到底归属于哪一层,又提供了@Component的衍生注解:
-
@Controller (标注在控制层类上)
-
@Service (标注在业务层类上)
-
@Repository (标注在数据访问层类上)
组件扫描
问题:使用前面学习的四个注解声明的bean,一定会生效吗?
答案:不一定。(原因:bean想要生效,还需要被组件扫描)
下面我们通过修改项目工程的目录结构,来测试bean对象是否生效:
运行程序后报错:
为什么没有找到bean对象呢?
-
使用四大注解声明的bean,要想生效,还需要被组件扫描注解@ComponentScan扫描
@ComponentScan注解虽然没有显式配置,但是实际上已经包含在了引导类声明注解 @SpringBootApplication 中,==默认扫描的范围是SpringBoot启动类所在包及其子包==。
-
解决方案:手动添加@ComponentScan注解,指定要扫描的包 (==仅做了解,不推荐==)
推荐做法(如下图):
-
将我们定义的controller,service,dao这些包呢,都放在引导类所在包com.study的子包下,这样我们定义的bean就会被自动的扫描到
DI详解
上一小节我们讲解了控制反转IOC的细节,接下来呢,我们学习依赖注解DI的细节。
依赖注入,是指IOC容器要为应用程序去提供运行时所依赖的资源,而资源指的就是对象。
在入门程序案例中,我们使用了@Autowired这个注解,完成了依赖注入的操作,而这个Autowired翻译过来叫:自动装配。
@Autowired注解,默认是按照类型进行自动装配的(去IOC容器中找某个类型的对象,然后完成注入操作)
入门程序举例:在EmpController运行的时候,就要到IOC容器当中去查找EmpService这个类型的对象,而我们的IOC容器中刚好有一个EmpService这个类型的对象,所以就找到了这个类型的对象完成注入操作。
那如果在IOC容器中,存在多个相同类型的bean对象,会出现什么情况呢?
-
程序运行会报错
如何解决上述问题呢?Spring提供了以下几种解决方案:
-
@Primary
-
@Qualifier
-
@Resource
使用@Primary注解:当存在多个相同类型的Bean注入时,加上@Primary注解,来确定默认的实现。
使用@Qualifier注解:指定当前要注入的bean对象。 在@Qualifier的value属性中,指定注入的bean的名称。
-
@Qualifier注解不能单独使用,必须配合@Autowired使用
使用@Resource注解:是按照bean的名称进行注入。通过name属性指定要注入的bean的名称。
面试题 : @Autowird 与 @Resource的区别
@Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
@Autowired 默认是按照类型注入,而@Resource是按照名称注入