目录
1 SpringBoot 入门
Spring发展到今天已经形成了一种开发生态圈,Spring提供了若干个子项目,每个项目用于完成特定的功能。我们在项目开发时,一般会偏向于选择这一套spring家族的技术,来解决对应领域的问题,那我们称这一套技术为spring全家桶。
官网:Spring
第1步:创建SpringBoot工程,并勾选Web开发相关依赖


第2步:定义HelloController类,添加方法hello,并添加注解

@RestController//表示当前类是一个请求处理类
public class HelloController {
@RequestMapping("/hello")//标识请求路径
public String hello(String name) {
System.out.println("name : " + name);
return "Hello " + name + "~";
}
}
第3步:以debug方式运行SpringBoot自动生成的引导类 (标识有 @SpringBootApplication 注解的类)
/**
* 启动类
*/
@SpringBootApplication
public class SpringbootWebQuickstartApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebQuickstartApplication.class, args);
}
}
第4步:打开浏览器,输入 http://localhost:8080/hello?name=heima

为什么一个main方法就可以将Web应用启动了?
因为我们在创建springboot项目的时候,选择了web开发的起步依赖 spring-boot-starter-web。而spring-boot-starter-web依赖,又依赖了spring-boot-starter-tomcat,由于maven的依赖传递特性,那么在我们创建的springboot项目中也就已经有了tomcat的依赖,这个其实就是springboot中内嵌的tomcat。
而我们运行引导类中的main方法,其实启动的就是springboot中内嵌的Tomcat服务器。 而我们所开发的项目,也会自动的部署在该tomcat服务器中,并占用8080端口号 。
2 HTTP 协议
HTTP:Hyper Text Transfer Protocol(超文本传输协议),规定了浏览器与服务器之间数据传输的规则。
-
基于TCP协议: 面向连接,安全。TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于字节流的传输层通信协议,在数据传输方面更安全。
-
基于请求-响应模型: 一次请求对应一次响应(先请求后响应)。
-
HTTP协议是无状态协议: 对于数据没有记忆能力,每次请求-响应都是独立的。
请求之间无法共享数据会引发的问题:
如:京东购物。加入购物车和去购物车结算是两次请求。由于HTTP协议的无状态特性,加入购物车请求响应结束后,并未记录加入购物车是何商品。发起去购物车结算的请求后,因为无法获取哪些商品加入了购物车,会导致此次请求无法正确展示数据。
具体使用的时候,我们发现京东是可以正常展示数据的,原因是Java早已考虑到这个问题,并提出了使用会话技术(Cookie、Session)来解决这个问题。具体如何来做,我们后面课程中会讲到。
2.1 HTTP 请求协议
请求协议:浏览器将数据以请求格式发送到服务器。包括:请求行、请求头 、请求体
GET方式的请求协议:

POST方式的请求协议:

- 请求行:HTTP请求中的第一行数据。由:请求方式、资源路径、协议/版本组成。
- 请求头:第二行开始,上图黄色部分内容就是请求头,格式为key: value形式。http是个无状态的协议,所以在请求头设置浏览器的一些自身信息和想要响应的形式。这样服务器在收到信息后,就可以知道是谁,想干什么了。
- 请求体 :存储请求参数,GET请求的请求参数在请求行中,故不需要设置请求体。
2.2 获取请求数据
Web服务器(Tomcat)对HTTP协议的请求数据进行解析,并进行了封装(HttpServletRequest),并在调用Controller方法的时候传递给了该方法。这样,就使得程序员不必直接对协议进行操作,让Web开发更加便捷。

@RestController
public class RequestController {
@RequestMapping("/request")
public String request(HttpServletRequest request) {
//获取请求方式
String method = request.getMethod();
System.out.println("请求方式: " + method);
//获取请求的url地址
String url = request.getRequestURL().toString();
System.out.println("请求url地址: " + url);
String uri = request.getRequestURI();
System.out.println("请求uri地址: " + uri);
//获取请求协议
String protocol = request.getProtocol();
System.out.println("请求协议: " + protocol);
//获取请求参数 - name, age
String name = request.getParameter("name");
String age = request.getParameter("age");
System.out.println("请求参数name: " + name + ", age: " + age);
//获取请求头 - Accept
String accept = request.getHeader("Accept");
System.out.println("请求头 - Accept: " + accept);
return "OK";
}
}
打开浏览器,输入: http://localhost:8080/request?name=heima&age=18
2.3 HTTP 相应协议
响应协议:服务器将数据以响应格式返回给浏览器。包括:响应行 、响应头 、响应体。

-
响应行:响应数据的第一行。响应行由协议及版本(HTTP/1.1)、响应状态码(200)、状态码描述(OK)组成。
响应状态码:
200 ok 客户端请求成功状态码分类 说明 1xx 响应中 --- 临时状态码。表示请求已经接受,告诉客户端应该继续请求或者如果已经完成则忽略 2xx 成功 --- 表示请求已经被成功接收,处理已完成 3xx 重定向 --- 重定向到其它地方,让客户端再发起一个请求以完成整个处理 4xx 客户端错误 --- 处理发生错误,责任在客户端,如:客户端的请求一个不存在的资源,客户端未被授权,禁止访问等 5xx 服务器端错误 --- 处理发生错误,责任在服务端,如:服务端抛出异常,路由出错,HTTP版本不支持等
404 Not Found 请求资源不存在500 Internal Server Error服务端发生不可预期的错误
响应状态码官网:https://cloud.tencent.com/developer/chapter/13553状态码 英文描述 解释 200 OK 客户端请求成功,即处理成功,这是我们最想看到的状态码 302 Found 指示所请求的资源已移动到由Location响应头给定的 URL,浏览器会自动重新访问到这个页面 304 Not Modified 告诉客户端,你请求的资源至上次取得后,服务端并未更改,你直接用你本地缓存吧。隐式重定向 400 Bad Request 客户端请求有语法错误,不能被服务器所理解 403 Forbidden 服务器收到请求,但是拒绝提供服务,比如:没有权限访问相关资源 404 Not Found 请求资源不存在,一般是URL输入有误,或者网站资源被删除了 405 Method Not Allowed 请求方式有误,比如应该用GET请求方式的资源,用了POST 428 Precondition Required 服务器要求有条件的请求,告诉客户端要想访问该资源,必须携带特定的请求头 429 Too Many Requests 指示用户在给定时间内发送了太多请求(“限速”),配合 Retry-After(多长时间后可以请求)响应头一起使用 431 Request Header Fields Too Large 请求头太大,服务器不愿意处理请求,因为它的头部字段太大。请求可以在减少请求头域的大小后重新提交。 500 Internal Server Error 服务器发生不可预期的错误。服务器出异常了,赶紧看日志去吧 503 Service Unavailable 服务器尚未准备好处理请求,服务器刚刚启动,还未初始化好 -
响应头:响应数据的第二行开始,格式为key:value形式。http是个无状态的协议,所以可以在请求头和响应头中设置一些信息和想要执行的动作,这样,对方在收到信息后,就可以知道你是谁,你想干什么。
-
响应体: 响应数据的最后一部分。存储响应的数据。
2.4 响应数据设置
Web服务器对HTTP协议的响应数据进行了封装(HttpServletResponse),并在调用Controller方法的时候传递给了该方法。这样,就使得程序员不必直接对协议进行操作,让Web开发更加便捷。

@RestController
public class ResponseController {
/**
* 方式一: HttpServletResponse 设置响应数据
*
* @param response
* @throws IOException
*/
@RequestMapping("/response")
public void response(HttpServletResponse response) throws IOException {
//设置响应状态码
response.setStatus(401);
//设置响应头
response.setHeader("name", "itheima");
//设置响应体
response.getWriter().write("<h1>hello response</h1>");
}
/**
* 方式二: ResponseEntity - Spring 中提供的方式
*
* @return
*/
@RequestMapping("/response2")
public ResponseEntity<String> response2() {
return ResponseEntity
.status(401)
.header("name", "javaweb-ai")
.body("<h1>hello response</h1>");
}
}
tips:响应状态码 和 响应头如果没有特殊要求的话,通常不手动设定。服务器会根据请求处理的逻辑,自动设置响应状态码和响应头。
3 SpringBoot 案例
3.1 需求说明
需求:基于SpringBoot开发web程序,完成用户列表的渲染展示

当在浏览器地址栏,访问前端静态页面(http://localhost:8080/usre.html)后,在前端页面上,会发送ajax请求,请求服务端(http://localhost:8080/list),服务端程序加载 user.txt 文件中的数据,读取出来后最终给前端页面响应json格式的数据,前端页面再将数据渲染展示在表格中。
3.2 代码实现
1.创建工程:再创建一个SpringBoot工程module,并勾选web依赖、lombok依赖。
- Web 依赖:提供开发 Web 应用(包括 RESTful 接口、Spring MVC 等)所需的核心组件,自动集成了 Spring MVC、Tomcat 服务器、JSON 处理等功能。
- Lombok 依赖:通过注解简化 Java 类的代码编写,自动生成 getter/setter、构造方法、toString() 等重复代码,减少模板代码冗余。
2.准备工作:引入资料中准备好的数据文件user.txt,以及static下的前端静态页面。
3.定义实体类:封装用户信息的实体类。在 com.itheima 下再定义一个包 pojo,专门用来存放实体类。 在该包下定义一个实体类User:
/**
* 封装用户信息
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
private String name;
private Integer age;
private LocalDateTime updateTime;
}
4.开发服务端程序:接收请求,读取文本数据并响应。
引入一个非常常用的工具包hutool,是一个功能丰富的工具类库,封装了大量常用操作(如字符串处理、日期时间、文件操作、HTTP 请求等),能极大简化开发。
/**
* 用户信息的 Controller
*/
@RestController
public class UserController {
@RequestMapping("/list")
public List<User> list() throws Exception{
//1.加载并读取 user.txt 文件
InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());
//2.解析用户信息,封装为User对象 -> list集合
List<User> userList = lines.stream().map(line -> {
String[] parts = line.split(",");
Integer id = Integer.parseInt(parts[0]);
String username = parts[1];
String password = parts[2];
String name = parts[3];
Integer age = Integer.parseInt(parts[4]);
LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
return new User(id, username, password, name, age, updateTime);
}).toList();
//3.返回数据(json)
return userList;
}
}
在 Maven/Gradle 项目中,src/main/resources 目录下的文件编译后会被自动复制到 类路径(classpath)的根目录(例如 target/classes/ 或 build/classes/)。
【读取类路径(classpath)下资源文件的经典代码】
InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
this.getClass():获取当前类的 Class 对象。
.getClassLoader():获取该类的类加载器(负责加载类路径下的资源)。
.getResourceAsStream("user.txt"):通过类加载器查找类路径下的 user.txt 文件,并返回其输入流(InputStream)。
.toList() / .collect(Collectors.toList()):将流(Stream)中的元素收集并转换为一个 List 集合。
3.3 @ResponseBody
-
类型:方法注解、类注解。
-
位置:书写在Controller方法上或类上,我们在类上加了@RestController注解,而这个注解是由两个注解组合起来的,分别是:@Controller 、@ResponseBody。 那也就意味着,我们在类上已经添加了@ResponseBody注解了。
-
作用:将方法返回值直接响应给浏览器,如果返回值类型是实体对象/集合,将会转换为JSON格式后在响应给浏览器。
4 分层解耦
4.1 三层架构
单一职责原则:一个类或一个方法,就只做一件事情,只管一块功能。这样就可以让类、接口、方法的复杂度更低,可读性更强,扩展性更好,也更利于后期的维护。
-
Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。
-
Service:业务逻辑层。负责业务逻辑处理的代码。
-
Dao:数据访问层(Data Access Object),也称为持久层。负责业务数据的维护操作,包括增、删、改、查等操作。
三层架构的好处:复用性强、便于维护、利用扩展
代码拆分:
-
控制层包名:com.itheima.controller
-
业务逻辑层包名:com.itheima.service
-
数据访问层包名:com.itheima.dao

(1)在 com.itheima.controller 中创建UserController类,代码如下:
/**
* 用户信息的 Controller
*/
@RestController
public class UserController {
private UserService userService = new UserServiceImpl();
@RequestMapping("/list")
public List<User> list() throws Exception {
//1. 调用service,获取数据
List<User> userList = userService.findAll();
//3. 返回数据(json)
return userList;
}
}
(2)在 com.itheima.service 中创建UserSerivce接口,代码如下:
public interface UserService {
/**
* 查询所有用户信息
* @return
*/
public List<User> findAll();
}
在 com.itheima.service.impl 中创建UserSerivceImpl实现类,代码如下:
public class UserServiceImpl implements UserService {
//面向接口的实现方式 + 多态
private UserDao userDao = new UserDaoImpl();
@Override
public List<User> findAll() {
//1. 调用dao获取数据
List<String> lines = userDao.findAll();
//2. 解析用户信息,封装为User对象 -> list集合
List<User> userList = lines.stream().map(line -> {
String[] parts = line.split(",");
Integer id = Integer.parseInt(parts[0]);
String username = parts[1];
String password = parts[2];
String name = parts[3];
Integer age = Integer.parseInt(parts[4]);
LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
return new User(id, username, password, name, age, updateTime);
}).toList();
return userList;
}
}
面向接口的实现方式 结合 多态,是面向对象编程中一种重要的设计思想。
核心是通过接口定义规范,让不同实现类提供具体实现,再利用多态特性灵活调用,从而实现 “规范与实现分离”。
作用-解耦:调用者只需依赖接口(UserDao),无需关心具体是 UserDaoImpl 还是别的什么实现,就算改变实现:
private UserDao userDao = new UserDaoImpl2();
也不用改下方调用部分的代码,降低代码耦合。
(3)在 com.itheima.dao 中创建UserDao接口,代码如下:
public interface UserDao {
/**
* 加载用户数据
* @return
*/
public List<String> findAll();
}
在 com.itheima.dao.impl 中创建UserDaoImpl实现类,代码如下:
public class UserDaoImpl implements UserDao {
@Override
public List<String> findAll() {
//1. 加载并读取 user.txt 文件
InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());
return lines;
}
}
4.2 分层解耦
上方代码在改变实现时:
private UserDao userDao = new UserDaoImpl2();
我们需要切换为 UserServiceImpl2 这套实现,就需要修改Contorller的代码,但其实换的是Service层的实现,那此处就会发生层与层耦合。
-
内聚:软件中各个功能模块内部的功能联系。
-
耦合:衡量软件中各个层/模块之间的依赖、关联的程度。
软件设计原则:高内聚低耦合。目的是使程序模块的可重用性、移植性大大增强。
4.2.1 解耦思路
(1)首先不能在EmpController中使用new对象。代码如下:
@RestController
public class UserController {
private UserService userService;
@RequestMapping("/list")
public List<User> list() throws Exception {
//1. 调用service,获取数据
List<User> userList = userService.findAll();
//3. 返回数据(json)
return userList;
}
}
但是,不能new对象,程序运行就报错,怎么办呢? 我们的解决思路是:
-
提供一个容器,容器中存储一些对象(例:UserService对象)。
-
Controller程序从容器中获取UserService类型的对象。
(2)将要用到的对象交给一个容器管理。

(3)应用程序中用到这个对象,就直接从容器中获取。

4.2.2 解耦问题
- 我们如何将对象交给容器管理呢?
- 程序运行时,容器如何为程序提供依赖的对象呢?
我们想要实现上述解耦操作,就涉及到Spring中的两个核心概念:
-
控制反转: Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部容器,这个容器称为:IOC容器或Spring容器。
-
依赖注入: Dependency Injection,简称DI。容器为应用程序提供运行时所依赖的资源,称之为依赖注入。例:EmpController程序运行时需要EmpService对象,Spring容器就为其提供并注入EmpService对象。
-
bean对象:IOC容器中创建、管理的对象,称之为:bean对象。

如此,便可实现层与层的解耦。
4.3 IOC & DI 入门
(1)将 Service 及 Dao 层的实现类,交给IOC容器管理。
在【实现类】(不是接口哦~)加上 @Component 注解,就代表把当前类产生的对象交给IOC容器管理。
component 组件,指 “组成整体的一个独立部分”。在软件领域,它被用来描述具有独立功能、可复用的模块 —— 这个模块可以独立完成某类职责。
(2)为 Controller 及 Service 注入运行时所依赖的对象。
在【声明成员变量的代码处】加上 @Autowired 注解,就代表IOC容器为当前程序注入运行时所依赖的资源。
autowired 自动连接,Spring 会自动找到当前类需要的依赖组件,并把它们 “连接”(注入)到一起,就像自动完成组件间的 “接线” 工作。
/**
* 用户信息的 Controller
*/
@RestController
public class UserController {
@Autowired //应用程序运行时,会自动的查询该类型的bean对象,并赋值给该成员变量
private UserService userService;
@RequestMapping("/list")
public List<User> list() throws Exception {
//1. 调用service,获取数据
List<User> userList = userService.findAll();
//3. 返回数据(json)
return userList;
}
}
@Component //将当前类交给IOC容器管理
public class UserServiceImpl implements UserService {
@Autowired //应用程序运行时,会自动的查询该类型的bean对象,并赋值给该成员变量
private UserDao userDao;
@Override
public List<User> findAll() {
//1. 调用dao获取数据
List<String> lines = userDao.findAll();
//2. 解析用户信息,封装为User对象 -> list集合
List<User> userList = lines.stream().map(line -> {
String[] parts = line.split(",");
Integer id = Integer.parseInt(parts[0]);
String username = parts[1];
String password = parts[2];
String name = parts[3];
Integer age = Integer.parseInt(parts[4]);
LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
return new User(id, username, password, name, age, updateTime);
}).toList();
return userList;
}
}
@Component //将当前类交给IOC容器管理
public class UserDaoImpl implements UserDao {
@Override
public List<String> findAll() {
//1. 加载并读取 user.txt 文件
InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());
return lines;
}
}
4.4 IOC 使用细节
Bean的声明:IOC控制反转,就是将对象的控制权交给Spring的IOC容器,由IOC容器创建及管理对象,IOC容器创建的对象称为bean对象。
而Spring框架为了更好的标识web应用程序开发当中,bean对象到底归属于哪一层,又提供了@Component的衍生注解:
| 注解 | 说明 | 位置 |
| @Component | 声明bean的基础注解 | 不属于以下三类时,用此注解 |
| @Controller | @Component的衍生注解 | 标注在控制层类上 |
| @Service | @Component的衍生注解 | 标注在业务层类上 |
| @Repository | @Component的衍生注解 | 标注在数据访问层类上(由于与mybatis整合,用的少) |
那么此时,我们就可以使用 @Service 注解声明Service层的bean。 使用 @Repository 注解声明Dao层的bean。 代码实现如下:
@Service //将当前类交给IOC容器管理
public class UserServiceImpl implements UserService {
@Autowired //应用程序运行时,会自动的查询该类型的bean对象,并赋值给该成员变量
private UserDao userDao;
@Override
public List<User> findAll() {
//1. 调用dao获取数据
List<String> lines = userDao.findAll();
//2. 解析用户信息,封装为User对象 -> list集合
List<User> userList = lines.stream().map(line -> {
String[] parts = line.split(",");
Integer id = Integer.parseInt(parts[0]);
String username = parts[1];
String password = parts[2];
String name = parts[3];
Integer age = Integer.parseInt(parts[4]);
LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
return new User(id, username, password, name, age, updateTime);
}).toList();
return userList;
}
}
@Repository //将当前类交给IOC容器管理
public class UserDaoImpl implements UserDao {
@Override
public List<String> findAll() {
//1. 加载并读取 user.txt 文件
InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());
return lines;
}
}
【注意事项】
- Controller层有 @RestController ,所以不用再加@Controller。原因我们之前也提到过,@RestController = @Controller + @ResponseBody
- 声明bean的时候,可以通过注解的value属性指定bean的名字,比如:@Repository("userDao"),如果没有指定,默认为类名首字母小写 userDaoImpl。
- 前面声明bean的四大注解,要想生效,还需要被组件扫描注解 @ComponentScan 扫描。该注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解 @SpringBootApplication 中,默认扫描的范围是启动类所在包及其子包。
4.5 DI 使用细节
依赖注入,是指IOC容器要为应用程序去提供运行时所依赖的资源,而资源指的就是对象。
在入门程序案例中,我们使用了@Autowired这个注解,完成了依赖注入的操作,而这个Autowired翻译过来叫:自动装配。@Autowired 注解,默认是按照类型进行自动装配的(去IOC容器中找某个类型的对象,然后完成注入操作)。
@Autowired 进行依赖注入,常见的方式,有如下三种:
(1)属性注入 ❤
@RestController
public class UserController {
//依赖注入方式一:属性注入
@Autowired
private UserService userService;
}
-
优点:代码简洁、方便快速开发。
-
缺点:隐藏了类之间的依赖关系、可能会破坏类的封装性。
(2)构造函数注入
使用 final 声明 userService:
1. 强制在构造器中初始化:final 成员变量必须在对象创建时完成初始化(要么直接赋值,要么在构造器中赋值)。这与 “构造器注入” 的场景天然匹配,强制要求必须在构造器中对 userService 赋值,避免了 “忘记注入” 导致的空指针异常。
2. 保证依赖不可变:final 修饰的变量一旦被赋值(在构造器中),就不能再被修改。这确保了 userService 从对象创建到销毁始终指向同一个实例,避免了在程序运行中被意外替换,保证了依赖的稳定性。
@RestController
public class UserController {
//依赖注入方式二:构造器注入
private final UserService userService;
@Autowired //只有一个构造函数时,可省略
public UserController(UserService userService) {
this.userService = userService;
}
}
-
优点:能清晰地看到类的依赖关系、提高了代码的安全性。
-
缺点:代码繁琐、如果构造参数过多,可能会导致构造函数臃肿。
-
注意:如果只有一个构造函数,@Autowired注解可以省略。(通常来说,也只有一个构造函数)
(3)setter注入
@RestController
public class UserController {
//依赖注入方式三:setter 注入
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
}
-
优点:保持了类的封装性,依赖关系更清晰。
-
缺点:需要额外编写setter方法,增加了代码量。
综上,在项目开发中,基于@Autowired进行依赖注入时,基本都是第一种和第二种方式。(官方推荐第二种方式,因为会更加规范)但是在企业项目开发中,很多的项目中,也会选择第一种方式因为更加简洁、高效(在规范性方面进行了妥协)。
那如果在IOC容器中,存在多个相同类型、不同的UserService的实现类的bean对象,会出现什么情况呢?
会报错,解决方法有以下三种:
(1)使用 @Primary 注解
当存在多个相同类型的Bean注入时,加上@Primary注解,来确定默认的实现。
@Primary
@Service
public class UserServiceImpl implements UserService {
}
(2)使用 @Qualifier("xxx") 注解
指定当前要注入的bean对象。 在@Qualifier的value属性中,指定注入的bean的名称。 @Qualifier注解不能单独使用,必须配合@Autowired使用。
@RestController
public class UserController {
@Qualifier("userServiceImpl")
@Autowired
private UserService userService;
(3)使用 @Resource(name = "xxx") 注解
是按照bean的名称进行注入。通过name属性指定要注入的bean的名称。
@Resource = @Qualifier + @Autowired
@RestController
public class UserController {
@Resource(name = "userServiceImpl")
private UserService userService;
面试题:@Autowired 与 @Resource的区别
@Autowired 默认是按照类型注入,而@Resource是按照名称注入
@Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
1040

被折叠的 条评论
为什么被折叠?



