JavaWeb笔记
灰羽 Lv3

JavaWeb入门

Spring官网 https://spring.io/

SpringFramework 配置繁琐 入门难度大

SpringBoot 简化配置、快速开发

SpringBoot可以帮助我们非常快速的构建应用程序、简化开发、提高效率。

1.SpringBootWeb快速入门

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

编写请求处理类HelloController,添加请求方法hello,并添加注解,最后运行启动类.

1
2
3
4
5
6
7
8
9
//请求处理类
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello(){
System.out.println("Hello World~");
return "Hello World~";
}
}

2.HTTP协议

2.1 HTTP概述

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

特点:

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

2.2 HTTP请求协议

HTTP-请求数据格式

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

请求头:第二行开始,格式 key:value

1
2
3
4
5
6
7
User-Agent: 浏览器版本
Accept:告诉浏览器,它所支持的数据类型
Accept-Encoding: 支持哪种编码格式
Accept-Language: 它的语言环境
Cache-Control: 缓存控制
Connection: 请求完成时,是断开还是保持连接
Host: 主机

请求体:POST请求,存放请求参数

请求方式:

GET请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效;

POST请求能够携带的参数没有限制,大小没有限制,补会在浏览器的URL地址栏显示数据内容,安全,但不高效;

2.3 HTTP响应协议

HTTP-响应格式

响应行:请求数据第一行(协议,状态码,描述)

响应头:第二行开始,格式 key:value

1
2
3
4
5
6
7
8
9
Content-Type:表示该响应内容的类型,例如text/html,image/jpeg ;

Content-Length:表示该响应内容的长度(字节数);

Content-Encoding:表示该响应压缩算法,例如gzip ;

Cache-Control:指示客户端应如何缓存,例如max-age=300表示可以最多缓存300秒 ;

Set-Cookie: 告诉浏览器为当前页面所在的域设置cookie ;

响应体:最后一部分,存放响应数据

响应状态码

状态码 信息
1xx 响应中-临时状态码
2xx 成功
3xx 重定向
4xx 客户端错误
5xx 服务端错误

2.4 HTTP-协议解析

目前比较流行的web服务器。如:Tomcat

3.WEB服务器

3.1 技术讲解

ASP:

  • 微软:国内最早流行的;
  • 在HTML中嵌入了VB的脚本,ASP+com;
  • 在ASP开发中,基本一个页面就有几千行业务代码,页面及其乱;
  • 维护成本高;

php:

  • 开发速度快,功能强大,跨平台,代码简单;
  • 无法承载大访问量的情况;

JSP/Servlet:

B/S:浏览和服务器

C/S:客户端和服务端

  • sun公司主推的B/S架构
  • 基于Java语言的
  • 可以承载三高问题带来的影响
  • 语法像ASP,加强市场强度

3.2 Tomcat

官网:Apache Tomcat® - Welcome!

启动 startup.bat

关闭 shutdown.bat

访问 http://localhost:8080/

配置 server.xml

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

网站是如何进行访问的:

  1. 输入域名
  2. 检查本机hosts配置文件下有没有这个域名的映射;
    • 有:直接返回对应的ip地址
    • 没有:去DNS服务器找,找到返回,找不到就返回找不到

发布一个网站

  • 将自己写的网站,放到服务器(Tomcat)中指定的web应用的文件夹(webapps)下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
--webapps:Tomcat服务器的web目录
-ROOT
-ceshi:网站目录名
-WEB-INF
-classes:java程序
-lib:web应用所依赖的jar包
-web.xml:网站配置文件
-index.html 默认的首页
-static
-css
-style.css
-js
-img
-......

4.请求响应

  • 请求(HttpServletRequest):获取请求数据
  • 响应(HttpServletResponse):设置响应数据
  • BS架构:客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端
  • CS架构:客户端/服务器架构模式

4.1 请求

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

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

简单参数

在向服务器发起请求时,向服务器传递的是一些普通的请求数据。

我们在这里讲解两种方式:

  1. 原始方式
  2. SpringBoot方式

4.1.1 原始方式

在原始的Web程序当中,需要通过Servlet中提供的API:HttpServletRequest(请求对象),获取请求的相关信息。比如获取请求参数:

Tomcat接收到http请求时:把请求的相关信息封装到HttpServletRequest对象中

在Controller中,我们要想获取Request对象,可以直接在方法的形参中声明 HttpServletRequest 对象。然后就可以通过该对象来获取请求信息:

1
2
//根据指定的参数名获取请求参数的数据值
String request.getParameter("参数名")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RestController
public class RequestController {
//原始方式
@RequestMapping("/simpleParam")
public String simpleParam(HttpServletRequest request){
// http://localhost:8080/simpleParam?name=Tom&age=10
// 请求参数: name=Tom&age=10 (有2个请求参数)
// 第1个请求参数: name=Tom 参数名:name,参数值:Tom
// 第2个请求参数: age=10 参数名:age , 参数值:10

String name = request.getParameter("name");//name就是请求参数名
String ageStr = request.getParameter("age");//age就是请求参数名

int age = Integer.parseInt(ageStr);//需要手动进行类型转换
System.out.println(name+" : "+age);
return "OK";
}
}

以上这种方式,我们仅做了解。(在以后的开发中不会使用到)

4.1.2 SpringBoot方式

在Springboot的环境中,对原始的API进行了封装,接收参数的形式更加简单。 如果是简单参数,参数名与形参变量名相同,定义同名的形参即可接收参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController
public class RequestController {
// http://localhost:8080/simpleParam?name=Tom&age=10
// 第1个请求参数: name=Tom 参数名:name,参数值:Tom
// 第2个请求参数: age=10 参数名:age , 参数值:10

//springboot方式
@RequestMapping("/simpleParam")
public String simpleParam(String name , Integer age ){//形参名和请求参数名保持一致
System.out.println(name+" : "+age);
return "OK";
}
}

结论:不论是GET请求还是POST请求,对于简单参数来讲,只要保证==请求参数名和Controller方法中的形参名保持一致==,就可以获取到请求参数中的数据值。

4.1.3 参数名不一致

如果方法形参名称与请求参数名称不一致,controller方法中的形参还能接收到请求参数值吗?

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class RequestController {
// http://localhost:8080/simpleParam?name=Tom&age=20
// 请求参数名:name

//springboot方式
@RequestMapping("/simpleParam")
public String simpleParam(String username , Integer age ){//请求参数名和形参名不相同
System.out.println(username+" : "+age);
return "OK";
}
}

答案:运行没有报错。 controller方法中的username值为:null,age值为20

结论:对于简单参数来讲,请求参数名和controller方法中的形参名不一致时,无法接收到请求数据

那么如果我们开发中,遇到了这种请求参数名和controller方法中的形参名不相同,怎么办?

解决方案:可以使用Spring提供的@RequestParam注解完成映射

在方法形参前面加上 @RequestParam 然后通过value属性执行请求参数名,从而完成映射。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class RequestController {
// http://localhost:8080/simpleParam?name=Tom&age=20
// 请求参数名:name

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

注意事项

@RequestParam中的required属性默认为true(默认值也是true),代表该请求参数必须传递,如果不传递将报错如果该参数是可选的,可以将required属性设置为false

1
2
3
4
5
@RequestMapping("/simpleParam")
public String simpleParam(@RequestParam(name = "name", required = false) String username, Integer age){
System.out.println(username+ ":" + age);
return "OK";
}

实体参数

在使用简单参数做为数据传递方式时,前端传递了多少个请求参数,后端controller方法中的形参就要书写多少个。如果请求参数比较多,通过上述的方式一个参数一个参数的接收,会比较繁琐。

此时,我们可以考虑将请求参数封装到一个实体类对象中。 要想完成数据封装,需要遵守如下规则:请求参数名与实体类的属性名相同

4.1.4 简单实体对象

定义POJO实体类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 +
'}';
}
}

Controller方法:

1
2
3
4
5
6
7
8
9
@RestController
public class RequestController {
//实体参数:简单实体对象
@RequestMapping("/simplePojo")
public String simplePojo(User user){
System.out.println(user);
return "OK";
}
}

4.1.5 复杂实体对象

复杂实体对象指的是,在实体类中有一个或多个属性,也是实体对象类型的。

  • 请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套实体类属性参数。

定义POJO实体类:

  • Address实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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 + '\'' +
'}';
}
}
  • User实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class User {
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 "User{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}

Controller方法:

1
2
3
4
5
6
7
8
9
@RestController
public class RequestController {
//实体参数:复杂实体对象
@RequestMapping("/complexPojo")
public String complexPojo(User user){
System.out.println(user);
return "OK";
}
}

4.1.6 数组集合参数

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

Controller方法:

1
2
3
4
5
6
7
8
9
@RestController
public class RequestController {
//数组集合参数
@RequestMapping("/listParam")
public String listParam(@RequestParam List<String> hobby){
System.out.println(hobby);
return "OK";
}
}

Postman测试:

方式一:xxxxxxxxxx?hobby=game&hobby=java

方式二:xxxxxxxxxx?hobby=game,java

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

默认情况下,请求中参数名相同的多个值,是封装到数组。如果要封装到集合,要使用@RequestParam绑定参数关系

1
2
3
4
5
6
7
8
9
@RestController
public class RequestController {
//数组集合参数
@RequestMapping("/listParam")
public String listParam(@RequestParam List<String> hobby){
System.out.println(hobby);
return "OK";
}
}

Postman测试:

方式一:xxxxxxxxxx?hobby=game&hobby=java

方式二:xxxxxxxxxx?hobby=game,java

4.1.7 日期参数

因为日期的格式多种多样(如:2022-12-12 10:05:45 、2022/12/12 10:05:45),那么对于日期类型的参数在进行封装的时候,需要通过@DateTimeFormat注解,以及其pattern属性来设置日期的格式。

  • @DateTimeFormat注解的pattern属性中指定了哪种日期格式,前端的日期参数就必须按照指定的格式传递。
  • 后端controller方法中,需要使用Date类型或LocalDateTime类型,来封装传递的参数。

Controller方法:

1
2
3
4
5
6
7
8
9
@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";
}
}

4.1.8 JSON参数

在学习前端技术时,我们有讲到过JSON,而在前后端进行交互时,如果是比较复杂的参数,前后端通过会使用JSON格式的数据进行传输。 (JSON是开发中最常用的前后端数据交互方式)

我们学习JSON格式参数,主要从以下两个方面着手:

  1. Postman在发送请求时,如何传递json格式的请求参数
  2. 在服务端的controller方法中,如何接收json格式的请求参数

Postman发送JSON格式数据:

1
2
3
4
5
6
7
8
{
"name":"Tom",
"age":10,
"address":{
"province":"beijing",
"city":"beijing"
}
}

服务端Controller方法接收JSON格式数据:

  • 传递json格式的参数,在Controller中会使用实体类进行封装。

  • 封装规则:JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接收参数。需要使用 @RequestBody标识。

  • @RequestBody注解:将JSON数据映射到形参的实体类对象中(JSON中的key和实体类中的属性名保持一致)

实体类:Address

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Address {
private String province;
private String city;

//省略GET , SET 方法
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
'}';
}
}

实体类:User

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class User {
private String name;
private Integer age;
private Address address;

//省略GET , SET 方法
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}

Controller方法:

1
2
3
4
5
6
7
8
9
@RestController
public class RequestController {
//JSON参数
@RequestMapping("/jsonParam")
public String jsonParam(@RequestBody User user){
System.out.println(user);
return "OK";
}
}

4.1.9 路径参数

在现在的开发中,经常还会直接在请求的URL中传递参数。例如:

1
2
http://localhost:8080/user/1		
http://localhost:8080/user/1/0

上述的这种传递请求参数的形式呢,我们称之为:路径参数。

路径参数:

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

Controller方法:

1
2
3
4
5
6
7
8
9
@RestController
public class RequestController {
//路径参数
@RequestMapping("/path/{id}")
public String pathParam(@PathVariable Integer id){
System.out.println(id);
return "OK";
}
}

传递多个路径参数:

Controller方法:

1
2
3
4
5
6
7
8
9
@RestController
public class RequestController {
//路径参数
@RequestMapping("/path/{id}/{name}")
public String pathParam2(@PathVariable Integer id, @PathVariable String name){
System.out.println(id+ " : " +name);
return "OK";
}
}

4.2 响应

4.2.1 @ResponseBody

@ResponseBody注解:

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

  • 位置:书写在Controller方法上或类上

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

  • @RestController = @Controller + @ResponseBody

测试下响应数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@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;
}
}

4.2.2 统一响应结果

前面所编写的这些Controller方法中,返回值各种各样,没有任何的规范。

前端:只需要按照统一格式的返回结果进行解析(仅一种解析方案),就可以拿到数据。

统一的返回结果使用类来描述,在这个结果中包含:

  • 响应状态码:当前请求是成功,还是失败

  • 状态码信息:给页面的提示信息

  • 返回的数据:给前端响应的数据(字符串、对象、集合)

定义在一个实体类Result来包含以上信息。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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(){
return new Result(1,"success",null);
}
//查询 成功响应(把查询结果做为返回数据响应给前端)
public static Result success(Object data){
return new Result(1,"success",data);
}
//失败响应
public static Result error(String msg){
return new Result(0,msg,null);
}
}

改造Controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@RestController
public class ResponseController {
//响应统一格式的结果
@RequestMapping("/hello")
public Result hello(){
System.out.println("Hello World ~");
//return new Result(1,"success","Hello World ~");
return Result.success("Hello World ~");
}

//响应统一格式的结果
@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);
}
}

Result(code、msg、data)

5.分层解耦

5.1 三层架构

5.1.1 介绍

单一职责原则:一个类或一个方法,就只做一件事情,只管一块功能。

这样就可以让类、接口、方法的复杂度更低,可读性更强,扩展性更好,也更利用后期的维护。

  • 数据访问:负责业务数据的维护操作,包括增、删、改、查等操作。
  • 逻辑处理:负责业务逻辑处理的代码。
  • 请求处理、响应数据:负责,接收页面的请求,给页面响应数据。

按照上述的三个组成部分,在我们项目开发中呢,可以将代码分为三层:

image-20221204193837678

  • Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。
  • Service:业务逻辑层。处理具体的业务逻辑。
  • Dao:数据访问层(Data Access Object),也称为持久层。负责数据访问操作,包括数据的增、删、改、查。

基于三层架构的程序执行流程:

image-20221204194207812

  • 前端发起的请求,由Controller层接收(Controller响应数据给前端)
  • Controller层调用Service层来进行逻辑处理(Service层处理完后,把处理结果返回给Controller层)
  • Serivce层调用Dao层(逻辑处理过程中需要用到的一些数据要从Dao层获取)
  • Dao层操作文件中的数据(Dao拿到的数据会返回给Service层)

三层架构的好处:

  1. 复用性强
  2. 便于维护
  3. 利用扩展

5.2 分层解耦

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

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

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

高内聚指的是:一个模块中各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高,即 “高内聚”。

低耦合指的是:软件中各个层、模块之间的依赖关联程序越低越好。

涉及到Spring中的两个核心概念:

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

    对象的创建权由程序员主动创建转移到容器(由容器创建、管理对象)。这个容器称为:IOC容器或Spring容器

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

    程序运行时需要某个资源,此时容器就为其提供这个资源。

    例:EmpController程序运行时需要EmpService对象,Spring容器就为其提供并注入EmpService对象

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

5.3 IOC&DI

上面我们引出了Spring中IOC和DI的基本概念,下面我们就来具体学习下IOC和DI的代码实现。

5.3.1 IOC&DI入门

任务:完成Controller层、Service层、Dao层的代码解耦

  • 思路:
    1. 删除Controller层、Service层中new对象的代码
    2. Service层及Dao层的实现类,交给IOC容器管理
    3. 为Controller及Service注入运行时依赖的对象
      • Controller程序中注入依赖的Service层对象
      • Service程序中注入依赖的Dao层对象

第1步:删除Controller层、Service层中new对象的代码

image-20221204212807207

第2步:Service层及Dao层的实现类,交给IOC容器管理

  • 使用Spring提供的注解:@Component ,就可以实现类交给IOC容器管理

image-20221204213328034

第3步:为Controller及Service注入运行时依赖的对象

  • 使用Spring提供的注解:@Autowired ,就可以实现程序运行时IOC容器自动注入需要的依赖对象

image-20221204213859112

完整的三层代码:

  • Controller层:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
public class EmpController {

@Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量
private EmpService empService ;

@RequestMapping("/listEmp")
public Result list(){
//1. 调用service, 获取数据
List<Emp> empList = empService.listEmp();

//3. 响应数据
return Result.success(empList);
}
}
  • Service层:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Component //将当前对象交给IOC容器管理,成为IOC容器的bean
public class EmpServiceA implements EmpService {

@Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量
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;
}
}

Dao层:

1
2
3
4
5
6
7
8
9
10
11
@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);
return empList;
}
}

5.3.2 IOC详解

前面我们提到IOC控制反转,就是将对象的控制权交给Spring的IOC容器,由IOC容器创建及管理对象。IOC容器创建的对象称为bean对象。

在之前的入门案例中,要把某个对象交给IOC容器管理,需要在类上添加一个注解:@Component

而Spring框架为了更好的标识web应用程序开发当中,bean对象到底归属于哪一层,又提供了@Component的衍生注解:

  • @Controller (标注在控制层类上)
  • @Service (标注在业务层类上)
  • @Repository (标注在数据访问层类上)

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

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

注意事项:

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

5.3.3 DI详解

依赖注入,是指IOC容器要为应用程序去提供运行时所依赖的资源,而资源指的就是对象。

在入门程序案例中,我们使用了@Autowired这个注解,完成了依赖注入的操作,而这个Autowired翻译过来叫:自动装配。

那如果在IOC容器中,存在多个相同类型的bean对象,会出现什么情况呢?

  • 程序运行会报错

如何解决上述问题呢?Spring提供了以下几种解决方案:

  • @Primary

  • @Qualifier

  • @Resource

使用@Primary注解:当存在多个相同类型的Bean注入时,加上@Primary注解,来确定默认的实现。

image-20221204232501679

使用@Qualifier注解:指定当前要注入的bean对象。 在@Qualifier的value属性中,指定注入的bean的名称。

  • @Qualifier注解不能单独使用,必须配合@Autowired使用

image-20221204233333606

使用@Resource注解:是按照bean的名称进行注入。通过name属性指定要注入的bean的名称。

image-20221204233637735

面试题 : @Autowird 与 @Resource的区别

  • @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
  • @Autowired 默认是按照类型注入,而@Resource是按照名称注入