Maven







WEB开发
三次握手四次挥手
✅ 一句话总述机制
✅ 然后按步骤说明每一步的作用 + 谁发 + 为什么要这样做
✅ 最后简短总结安全性或可靠性设计理念
✅ 一、TCP 三次握手(Three-Way Handshake)
一句话总结:
三次握手是 TCP 建立连接的过程,用来确保客户端和服务端双方都能互相确认“发送”和“接收”能力正常。
💡 详细步骤(带原因)
1️⃣ 第一次握手:客户端 → 服务端,发送 SYN
- 客户端发送一个带
SYN=1的报文(比如SYN seq=x)表示想建立连接。 - 目的:告诉服务端“我想连接你,并且我能发送数据”。
2️⃣ 第二次握手:服务端 → 客户端,发送 SYN + ACK
- 服务端收到后,回复一个
SYN=1, ACK=x+1的报文。 - 目的:
- 确认收到了客户端的请求;
- 同时也告诉客户端“我也可以发送数据”。
3️⃣ 第三次握手:客户端 → 服务端,发送 ACK
- 客户端收到服务端的
SYN+ACK后,再发一个确认报文ACK=y+1。 - 目的:确认服务端的应答报文已经收到。
- 至此,双方都知道对方的“发送和接收能力”正常,连接建立成功。
💬 面试官喜欢听的关键句
“三次握手的核心目的是双方互相确认收发能力,两次不够是因为第三次才能确认服务端的发送能力。”
(很多人只背流程,但加上“为什么两次不行”这一句就拉开差距)
✅ 二、TCP 四次挥手(Four-Way Handshake)
一句话总结:
四次挥手是 TCP 断开连接的过程,确保双方都能安全、完整地关闭连接,不丢数据。
💡 详细步骤(带原因)
1️⃣ 第一次挥手:客户端 → 服务端,发送 FIN
- 客户端发送
FIN=1,表示自己已经没有数据要发了(但仍能接收数据)。 - 进入 FIN_WAIT_1 状态。
2️⃣ 第二次挥手:服务端 → 客户端,发送 ACK
- 服务端收到后回复
ACK,表示确认收到关闭请求,但可能还有数据要发。 - 客户端进入 FIN_WAIT_2 状态。
3️⃣ 第三次挥手:服务端 → 客户端,发送 FIN
- 服务端数据发完后,再发送
FIN=1报文表示自己也要关闭连接。 - 进入 LAST_ACK 状态。
4️⃣ 第四次挥手:客户端 → 服务端,发送 ACK
- 客户端发送最后一个确认报文后,等待一段时间(
TIME_WAIT),防止服务端没收到 ACK。 - 等待结束后彻底关闭连接。
💬 面试官喜欢听的关键句
“挥手要四次是因为 TCP 是全双工的,每一方都要单独关闭自己的发送通道。”
“客户端最后会进入 TIME_WAIT 状态,确保对方能收到 ACK,防止旧连接影响新连接。”
✅ 三、进阶补充(加分项 💥)
如果你面的是开发或网络底层岗位,可以补一句深入理解:
🔹 为什么握手三次而不是两次?
- 因为两次握手无法确保服务端的发送能力。
如果第二次握手(服务端 SYN+ACK)丢了,客户端无法确认连接是否建立,会造成半连接状态。
🔹 为什么挥手是四次?
- 因为 TCP 是全双工通信,关闭时需要双方分别确认自己方向的数据都传完。
🔹 为什么有 TIME_WAIT?
- 防止最后一个 ACK 丢失,服务端重发 FIN 时客户端还能处理;
- 确保老连接残留报文不会影响后续新连接。
🎯 一份面试官评分高的回答模板(背下来)
“TCP 建立连接要三次握手:
第一次客户端发 SYN 表示想连接;
第二次服务端回 SYN+ACK 确认并回应;
第三次客户端回 ACK 确认服务端的发送能力,这样双方的收发能力都确认无误。断开连接是四次挥手:
因为 TCP 是全双工的,双方要各自关闭发送通道。
客户端先发 FIN,请求断开;
服务端回 ACK 表示收到,但可能还有数据要发;
数据发完后服务端再发 FIN;
客户端回 ACK 并进入 TIME_WAIT 状态,等确保对方收到后再彻底关闭。”
Cookie 与 Session
🧠 一、基础理解类(必考)
这些是入门必问题:
1️⃣ Cookie 与 Session 的区别?
面试官想考: 你是否理解两者的职责与存储位置。
回答要点:
| 项目 | Cookie | Session |
|---|---|---|
| 存储位置 | 客户端(浏览器) | 服务器 |
| 数据存储 | 以键值对存储在浏览器中 | 保存在服务器内存或缓存中(如Redis) |
| 安全性 | 较低,容易被伪造或窃取 | 较高,因为数据存在服务器 |
| 生命周期 | 可设定过期时间 | 通常依赖 Cookie 的 session_id,有效期短 |
| 用途 | 记录用户偏好、登录状态等 | 管理用户会话、登录信息 |
✅ 加分点:Session 底层通常依赖 Cookie 存放一个 session_id。
2️⃣ Session 是如何工作的?
回答要点:
- 用户第一次访问服务器 → 服务器创建 Session,并生成唯一的
session_id。 - 服务器将
session_id写入到 Cookie 中,返回给客户端。 - 客户端后续请求时,会自动携带该 Cookie。
- 服务器根据
session_id找回对应的 Session 数据,实现会话保持。
✅ 加分点:如果浏览器禁用了 Cookie,可以通过 URL 重写(?sessionid=xxx)来维持 Session。
3️⃣ Cookie 的作用是什么?
- 保存用户登录状态(如 token)
- 保存用户偏好(如语言、主题)
- 统计用户行为(如访问次数)
✅ 加分点:Cookie 是 HTTP 无状态协议下维持状态的一种方式。
🔒 二、安全与机制类(进阶)
4️⃣ Cookie 有哪些安全属性?
| 属性 | 作用 |
|---|---|
HttpOnly |
阻止 JS 访问 Cookie,防止 XSS 窃取 |
Secure |
仅在 HTTPS 传输时携带 |
SameSite |
限制第三方请求携带 Cookie,防止 CSRF 攻击 |
✅ 加分点:在实际开发中 HttpOnly + Secure + SameSite=Lax 是推荐组合。
5️⃣ Session 是如何防止伪造的?
- Session ID 是随机且唯一的。
- Session ID 存储在 Cookie 中且服务器验证。
- 可以配合 IP、User-Agent 校验。
- 一旦用户登出或过期,Session 即无效。
6️⃣ 如果 Cookie 被禁用,Session 还能用吗?
✅ 可以,但需要:
- 在 URL 参数中附加
session_id - 或使用隐藏表单字段传递
但安全性和体验会变差。
⚙️ 三、应用与扩展类(常问实战)
7️⃣ Session 存在服务器,如何实现分布式共享?
面试官想考: 你是否了解分布式架构思维。
回答要点:
- 不能单纯存在单台机器内存中,否则多机登录失效。
- 常见解决方案:
- Session 复制(性能差)
- Session 持久化到数据库
- Session 存储到 Redis(推荐)
- JWT(无状态替代方案)
✅ 加分点:提到“Session 共享”或“JWT无状态替代”都很亮眼。
8️⃣ Session 与 Token(JWT)有何区别?
| 特点 | Session | Token(JWT) |
|---|---|---|
| 状态 | 有状态(需服务器存储) | 无状态(自包含) |
| 适用场景 | Web 应用 | 前后端分离 / 移动端 |
| 安全性 | 高,服务器可控 | 一旦泄漏难撤销 |
| 扩展性 | 一般 | 优秀,跨服务可共享 |
✅ 加分点:Token 可在分布式系统中代替 Session,减少服务器存储负担。
9️⃣ Cookie 的大小与数量限制?
- 单个 Cookie 最大约 4KB。
- 每个域名下浏览器通常最多支持 20~50 个 Cookie。
- 超出部分会被丢弃。
🔟 Cookie 与 LocalStorage、SessionStorage 的区别?
| 项目 | Cookie | LocalStorage | SessionStorage |
|---|---|---|---|
| 存储位置 | 浏览器 | 浏览器 | 浏览器 |
| 生命周期 | 可自定义 | 永久(除非手动清除) | 仅当前会话 |
| 是否随请求发送 | ✅ 是 | ❌ 否 | ❌ 否 |
| 大小限制 | ~4KB | ~5MB | ~5MB |
✅ 加分点:SessionStorage 适合保存一次性会话数据。
💡 四、常见加分题 / 深挖题
11 Cookie 的跨域问题怎么解决?
- 设置
Access-Control-Allow-Credentials: true - 设置
Access-Control-Allow-Origin为具体域名(不能是*) - 客户端请求中配置
withCredentials: true
12 如何实现“记住我”功能?
- 登录成功时,生成一个长期有效的 token(如 7 天)。
- 存入 Cookie 中,并在下次登录时校验。
✅ 加分点:记住我用 Cookie,但不要直接存用户名密码,要用加密 token。
✅ 面试总结建议
| 题型 | 面试难度 | 掌握层次 |
|---|---|---|
| 区别题 | ⭐ | 必会 |
| 工作机制 | ⭐⭐ | 要理解流程 |
| 安全相关 | ⭐⭐⭐ | 可深入讲攻击防范 |
| 分布式/Token 扩展 | ⭐⭐⭐⭐ | 面试亮点加分题 |
🧩 Java Web 三大组件
- Servlet → 处理请求、返回响应(业务核心)
- Filter → 过滤器,负责“拦截与放行”
- Listener → 监听器,负责“监听事件”
🌍Servlet
一、什么是 Servlet?
Servlet(Server Applet) 是运行在服务器端的 Java 程序组件,用于处理客户端请求、生成响应。
它相当于 Java Web 中的“控制器”。
- 属于 javax.servlet 包;
- 由 Servlet 容器(如 Tomcat) 调用执行;
- 本质是一个遵守特定接口规范的 Java 类。
🧠 Servlet 的生命周期(Lifecycle)
Servlet 的生命周期由容器(如 Tomcat)管理:
| 阶段 | 方法 | 说明 |
|---|---|---|
| 初始化 | init() |
只执行一次,Servlet 被加载时调用 |
| 处理请求 | service() |
每次请求都会调用 |
| 销毁 | destroy() |
当容器卸载 Servlet 时调用 |


💡 Servlet 接口定义
public interface Servlet {
void init(ServletConfig config) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
二、HttpServlet —— Servlet 的子类
HttpServlet 是对 Servlet 的一个简化版实现,专门用来处理 HTTP 协议 的请求(如浏览器访问)。
属于:javax.servlet.http.HttpServlet
🌍 HttpServlet 常用方法
HttpServlet 重写了 service(),会自动根据 HTTP 请求方式分发到不同的方法:
| HTTP 请求方式 | 对应方法 | 说明 |
|---|---|---|
| GET | doGet(HttpServletRequest req, HttpServletResponse resp) |
浏览器直接访问页面 |
| POST | doPost(HttpServletRequest req, HttpServletResponse resp) |
表单提交或 AJAX |
| PUT | doPut(...) |
上传文件、更新资源 |
| DELETE | doDelete(...) |
删除资源 |
🧩 示例:最简单的 HttpServlet
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("<h1>Hello, Servlet!</h1>");
}
}
配置(web.xml):
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
访问路径示例:
http://localhost:8080/myweb/hello
三、总结对比
| 项目 | Servlet | HttpServlet |
|---|---|---|
| 包名 | javax.servlet | javax.servlet.http |
| 面向协议 | 通用(可支持任意协议) | 专用于 HTTP |
| 方法 | service() | doGet() / doPost() 等 |
| 常用场景 | 通用服务接口 | Web开发(最常用) |
| 实现复杂度 | 稍高 | 简洁、方便 |
🧱 Filter(过滤器)
💡 1. 定义
Filter 是一种 请求过滤机制,
它能在请求到达 Servlet 之前、响应返回客户端之前,对请求或响应做统一处理。
比如:
- 登录验证
- 日志记录
- 请求编码统一
- 跨域处理(CORS)
- 权限校验
⚙️ 2. 关键方法
Filter 接口中主要有 3 个方法:
public interface Filter {
void init(FilterConfig filterConfig) throws ServletException; // 初始化
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException; // 核心逻辑
void destroy(); // 销毁(应用停止时调用)
}
🧠 3. 运行流程
客户端请求 → Filter → Servlet → Filter → 客户端响应
Filter 相当于“关卡”,可以:
- 放行:
chain.doFilter(request, response) - 拦截:直接返回响应,不调用
chain.doFilter()
✍️ 4. 示例:登录验证过滤器
@WebFilter("/*") // 拦截所有请求
public class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
// 获取session中的用户信息
Object user = req.getSession().getAttribute("user");
// 允许登录请求通过
String path = req.getRequestURI();
if (path.contains("/login") || user != null) {
chain.doFilter(request, response); // 放行
} else {
res.sendRedirect("/login.jsp"); // 拦截并重定向
}
}
}
🧩 5. Filter 链执行顺序
多个 Filter 时,执行顺序由:
web.xml中声明顺序决定(旧写法)@WebFilter+@WebListener自动扫描顺序不固定(建议手动排序)
执行顺序:
请求进入时:Filter1 → Filter2 → Servlet
响应返回时:Filter2 → Filter1 → 客户端
🔔 Listener(监听器)
💡 1. 定义
Listener 是一种 事件监听机制,
当 Web 应用中某些对象(如 ServletContext、Session、Request)
创建、销毁、修改属性 时自动触发回调。
⚙️ 2. 常见监听器类型
| 监听器接口 | 监听对象 | 作用 |
|---|---|---|
ServletContextListener |
Web 应用上下文(全局) | 应用启动 / 停止时触发 |
HttpSessionListener |
Session | Session 创建 / 销毁时触发 |
ServletRequestListener |
Request 请求 | 请求创建 / 销毁时触发 |
ServletContextAttributeListener |
Application 域属性 | 属性增删改监听 |
HttpSessionAttributeListener |
Session 域属性 | 属性增删改监听 |
ServletRequestAttributeListener |
Request 域属性 | 属性增删改监听 |
✍️ 3. 示例 1:统计在线人数
@WebListener
public class OnlineCountListener implements HttpSessionListener {
private static int onlineCount = 0;
@Override
public void sessionCreated(HttpSessionEvent se) {
onlineCount++;
System.out.println("新用户上线,当前在线人数:" + onlineCount);
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
onlineCount--;
System.out.println("用户下线,当前在线人数:" + onlineCount);
}
}
✍️ 4. 示例 2:应用启动初始化资源
@WebListener
public class AppInitListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("🌟 Web应用启动:加载全局配置");
// 比如加载数据库连接池、初始化缓存
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("💤 Web应用停止:释放资源");
}
}
🧠 Filter vs Listener 对比总结
| 特性 | Filter | Listener |
|---|---|---|
| 目的 | 过滤请求和响应 | 监听系统事件 |
| 是否参与请求链 | ✅ 是 | ❌ 否 |
| 典型用途 | 登录验证、日志、编码、跨域 | 统计人数、初始化资源、监控会话 |
| 执行时机 | 每次请求前后 | 对象创建/销毁时 |
| 主要接口 | Filter |
各种 Listener 接口(Context、Session、Request 等) |
| 实现方式 | @WebFilter 或 web.xml |
@WebListener 或 web.xml |
🧩 面试高频问题 💬
- Filter 与 Interceptor(拦截器)的区别?
- Filter 属于 Servlet 规范,运行在 Servlet 容器层(更底层);
- Interceptor 属于 Spring MVC,运行在控制器调用前后;
- Interceptor 能拿到控制器方法信息(HandlerMethod),Filter 不行。
- Filter 执行顺序如何控制?
web.xml配置顺序;- Spring Boot 可通过
@Order注解控制。
- Listener 能监听哪些事件?
- 应用启动、停止;
- Session 创建、销毁;
- Request 创建、销毁;
- 属性变化。
🧩 整体结构图
┌──────────────────────────────────────────────┐
│ 客户端(浏览器) │
└──────────────────────────────────────────────┘
│
▼
┌──────────────────────────┐
│ Http 请求进入 Web 容器 │
└──────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Listener 阶段 │
│ ① contextInitialized() 应用启动 │
│ ② requestInitialized() 请求创建 │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Filter 链 │
│ (可有多个) │
│ │
│ ┌───────────────────────────────┐ │
│ │ Filter1.doFilter() 前置处理 │ │
│ ├───────────────────────────────┤ │
│ │ Filter2.doFilter() 前置处理 │ │
│ ├───────────────────────────────┤ │
│ │ ... │ │
│ ├───────────────────────────────┤ │
│ │ FilterN.doFilter() 前置处理 │ │
└──┴───────────────────────────────┴───────────────────────┘
│
▼
┌────────────────────────┐
│ Servlet执行 │
│ (service/doGet/doPost) │
└────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Filter 链 后置阶段 │
│ FilterN → ... → Filter2 → Filter1 │
│ (按相反顺序执行响应处理逻辑) │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Listener 阶段 │
│ ① requestDestroyed() 请求销毁 │
│ ② sessionDestroyed() 会话销毁(如超时) │
│ ③ contextDestroyed() 应用停止 │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ 响应返回客户端 │
└──────────────────────────────────────────────┘
🧠 图解总结
| 阶段 | 组件 | 方法 | 说明 |
|---|---|---|---|
| 应用启动 | ServletContextListener |
contextInitialized() |
初始化资源,如数据库连接池 |
| 请求创建 | ServletRequestListener |
requestInitialized() |
每个请求创建时调用 |
| 请求过滤 | Filter |
doFilter() |
拦截请求,可修改请求或响应 |
| 核心处理 | Servlet |
service() / doGet() / doPost() |
执行业务逻辑 |
| 响应过滤 | Filter |
doFilter()(后置部分) |
处理响应结果,如统一封装 |
| 请求结束 | ServletRequestListener |
requestDestroyed() |
请求销毁 |
| Session销毁 | HttpSessionListener |
sessionDestroyed() |
用户退出或超时 |
| 应用停止 | ServletContextListener |
contextDestroyed() |
释放全局资源 |
⚙️ 执行顺序举例
假设系统中有两个过滤器:
LogFilter:记录请求日志AuthFilter:验证是否登录
执行顺序如下:
请求进入:
LogFilter.doFilter(前) →
AuthFilter.doFilter(前) →
Servlet →
AuthFilter.doFilter(后) →
LogFilter.doFilter(后)
响应返回客户端
👉 前后顺序是嵌套的(像洋葱一样),
进入时顺序执行,返回时逆序执行。
🧩 Listener 的触发时机图
┌──────────────────────────────────────────────────────────┐
│ ServletContextListener (全局) │
│ ├── contextInitialized() → Web 应用启动 │
│ ├── contextDestroyed() → Web 应用停止 │
└──────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐
│ HttpSessionListener (会话) │
│ ├── sessionCreated() → 用户登录 / Session 新建 │
│ ├── sessionDestroyed() → Session 超时 / 退出 │
└──────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐
│ ServletRequestListener (请求级) │
│ ├── requestInitialized() → 每次请求开始 │
│ ├── requestDestroyed() → 每次请求结束 │
└──────────────────────────────────────────────────────────┘
📚 记忆口诀
☕ 三大组件一句话总结:
- Servlet:干正事(业务逻辑)
- Filter:把关卡(过滤控制)
- Listener:打下手(监听事件)
Spring
IoC
IOC(Inversion of Control,控制反转)
- 实际上是通过反射机制帮助我们创建的对象。
- 单例模式,每次获取到的对象都是同一个。
核心思想:将对象的创建、管理(如生命周期、依赖关系)的控制权从代码本身转移到外部容器(如 Spring 容器),而非由开发者在代码中手动创建和组装对象。
传统方式:如果类 A 依赖类 B,需要在 A 中手动
new B()来创建 B 的实例,控制权在 A 自身。public class A { private B b; public A() { this.b = new B(); // 手动创建依赖,控制权在 A 中 } }IOC 方式:对象的创建由容器负责,A 只需声明依赖,容器会自动将 B 的实例 “推送” 给 A,控制权转移到容器。
public class A { private B b; // 容器通过构造器或 setter 注入 B 的实例,A 无需关心 B 的创建 public A(B b) { this.b = b; } }
作用:解除了类之间的硬编码依赖,让类更专注于自身业务逻辑,便于测试和扩展(如替换依赖的实现类只需修改容器配置)。
DI
DI(Dependency Injection,依赖注入):IOC 的具体实现方式,指容器在创建对象时,自动将其依赖的其他对象(依赖)注入到该对象中。
本质:“依赖注入” 是 “控制反转” 的手段,IOC 是目的,DI 是实现 IOC 的具体技术
注入方式:
构造器注入:通过构造方法参数传递依赖。
public class A { private B b; // 容器通过构造器注入 B public A(B b) { this.b = b; } }Setter 注入:通过 setter 方法传递依赖。
public class A { private B b; // 容器通过 setter 注入 B public void setB(B b) { this.b = b; } }字段注入:直接在字段上使用注解(如
@Autowired)注入(Spring 支持,但不推荐过度使用)。public class A { @Autowired private B b; // 容器直接注入字段 }
IOC 与 DI 的关系
- IOC 是设计思想:强调 “控制权从代码转移到容器”。
- DI 是实现手段:容器通过注入依赖的方式实现控制反转。
举个生活例子:
- 传统方式:你(类 A)需要一杯咖啡(依赖 B),必须自己买材料、煮咖啡(手动创建 B)。
- IOC/DI 方式:你(类 A)只需告诉咖啡店(容器)你要咖啡(声明依赖),咖啡店会直接把做好的咖啡(B 的实例)送到你手上(注入),你无需关心咖啡的制作过程。
@Resource
随着Java版本的更新迭代,某些javax包下的包,会被逐渐弃用并移除。在JDK11版本以后,javax.annotation这个包被移除并且更名为jakarta.annotation(我们在JavaWeb篇已经介绍过为什么要改名字了)其中有一个非常重要的注解,叫做@Resource,它的作用与@Autowired时相同的,也可以实现自动装配,但是在IDEA中并不推荐使用@Autowired注解对成员字段进行自动装配,而是推荐使用@Resource,如果需要使用这个注解,还需要额外导入包:
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
@Resource 和 @Autowired 都是 Java 中用于依赖注入(DI)的注解,用于自动获取并注入所需的对象(依赖),但它们来自不同的规范,功能和使用上存在一些差异。
1. 来源与规范
- **
@Autowired**来自 Spring 框架(org.springframework.beans.factory.annotation.Autowired),是 Spring 定义的注解,仅在 Spring 环境中生效。 - **
@Resource**来自 Java EE 规范(javax.annotation.Resource,JSR-250 标准),是 Java 官方定义的注解,不仅适用于 Spring,还可在其他支持该规范的框架中使用(如 Jakarta EE 环境)。
2. 注入逻辑(匹配方式)
两者的核心功能都是 “自动注入依赖”,但匹配依赖的优先级不同:
@Autowired
默认按照 类型(Type) 匹配:
- 容器会查找与目标字段 / 方法参数类型相同的 Bean 进行注入。
- 若存在多个同类型的 Bean,会抛出
NoUniqueBeanDefinitionException异常。此时需配合@Qualifier注解指定 Bean 的名称(@Qualifier("beanName")),按名称匹配。
示例:
@Service
public class UserService {
// 按类型匹配名为 "userDao" 或 "userDaoImpl" 的 Bean(需类型为 UserDao)
@Autowired
// 若有多个 UserDao 类型的 Bean,通过 @Qualifier 指定名称
@Qualifier("userDaoImpl")
private UserDao userDao;
}
@Resource
默认按照 名称(Name) 匹配,若名称匹配失败,再按类型(Type)匹配:
- 名称的默认值为目标字段名或 setter 方法对应的属性名(如字段
userDao默认匹配名称为userDao的 Bean)。 - 可通过
name属性显式指定要注入的 Bean 名称(@Resource(name = "userDaoImpl"))。 - 若指定
type属性,则强制按类型匹配(@Resource(type = UserDao.class))。
示例:
@Service
public class UserService {
// 1. 默认按名称 "userDao" 匹配,若不存在则按类型 UserDao 匹配
@Resource
private UserDao userDao;
// 2. 显式指定名称,只按名称 "userDaoImpl" 匹配
@Resource(name = "userDaoImpl")
private UserDao userDao2;
}
3. 支持的注入位置
- **
@Autowired**可用于 字段、构造方法、setter 方法 上。- 字段注入:直接标注在成员变量上。
- 构造方法注入:标注在构造方法上(Spring 4.3+ 后,单构造方法可省略
@Autowired)。 - setter 方法注入:标注在 setter 方法上。
- **
@Resource**可用于 字段、setter 方法 上,但 不支持构造方法注入。
4. required 属性(是否允许依赖为 null)
**
@Autowired**有required属性(默认true),表示依赖必须存在,若不存在则抛出异常;若设为false,则允许依赖为null。@Autowired(required = false) // 允许 userDao 为 null private UserDao userDao;**
@Resource**没有required属性,默认要求依赖必须存在,若不存在则抛出异常。若需允许 null,需结合其他方式(如 Spring 的@Nullable注解)。
5. 总结:核心差异对比
| 特性 | @Autowired |
@Resource |
|---|---|---|
| 来源 | Spring 框架 | Java EE 规范(JSR-250) |
| 默认匹配方式 | 类型(Type) | 名称(Name),失败则按类型 |
| 支持的注入位置 | 字段、构造方法、setter 方法 | 字段、setter 方法(不支持构造方法) |
| 处理多实例冲突 | 需配合 @Qualifier 指定名称 |
直接通过 name 属性指定 |
| 是否有 required 属性 | 有(默认 true) | 无(默认必须存在) |
| 适用范围 | 仅 Spring 环境 | 支持 Java EE 规范的框架(含 Spring) |
使用建议
- 若项目仅依赖 Spring 框架,
@Autowired+@Qualifier是更常用的组合,功能灵活且 Spring 对其支持更完善。 - 若需兼顾 Java 标准规范(如可能迁移到其他框架),
@Resource是更通用的选择,尤其在按名称注入时无需额外注解。 - 构造方法注入更推荐(依赖不可变,便于测试),此时
@Autowired是唯一选择(@Resource不支持)。
@Component
@Component、@Bean 和 @ComponentScan 都是 Spring 框架中用于管理 Bean 的核心注解,它们的作用和使用场景不同,共同支撑了 Spring 的 IoC 容器功能。
1. @Component:类级别的 Bean 定义
作用:标记一个类为 Spring 管理的「组件」(Bean),Spring 容器会自动扫描并将其实例化,纳入容器管理。
核心特点:
- 作用于类:只能标注在类上,表明该类是一个可被 Spring 管理的组件。
- 自动扫描:需要配合
@ComponentScan才能生效(Spring boot 主类上的@SpringBootApplication已包含@ComponentScan)。 - 默认 Bean 名称:默认是类名首字母小写(如
UserService→userService),也可通过value属性指定(如@Component("myUserService"))。
衍生注解:
为了更清晰地表达组件的用途,Spring 提供了 @Component 的三个衍生注解(功能相同,语义不同):
@Service:用于标注「服务层」组件(业务逻辑类)。@Controller:用于标注「控制层」组件(MVC 中的控制器)。@Repository:用于标注「数据访问层」组件(DAO 类),还会自动转换数据库操作异常。
示例:
// 标注为组件,Spring 会自动创建其实例并纳入容器
@Component
public class UserService {
// 业务逻辑...
}
2. @Bean:方法级别的 Bean 定义
作用:标注在方法上,表明该方法的返回值是一个需要被 Spring 管理的 Bean,适用于第三方类或无法直接修改源码的类的实例化。
核心特点:
- 作用于方法:只能标注在方法上,方法的返回值会被注册为 Spring 容器中的 Bean。
- 依赖外部类:通常用于创建第三方库的对象(如
DataSource、RestTemplate),或需要复杂初始化逻辑的对象。 - 所属配置类:方法必须定义在被
@Configuration标注的「配置类」中。 - 默认 Bean 名称:默认是方法名,也可通过
value或name属性指定(如@Bean("myDataSource"))。
示例:
// 配置类:存放 @Bean 方法的类
@Configuration
public class AppConfig {
// 定义一个 DataSource 类型的 Bean,由 Spring 管理
@Bean
public DataSource dataSource() {
// 复杂的初始化逻辑(如配置数据库连接信息)
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
return dataSource;
}
}
3. @ComponentScan:指定 Bean 的扫描范围
作用:告诉 Spring 容器需要扫描哪些包及其子包,以发现被 @Component(或其衍生注解 @Service、@Controller 等)标注的类,并将其注册为 Bean。
核心特点:
- 扫描路径:默认扫描当前类所在的包及其子包,也可通过
basePackages或basePackageClasses手动指定扫描路径。 - 过滤规则:可通过
includeFilters(包含)和excludeFilters(排除)指定需要扫描或忽略的类(如只扫描带特定注解的类)。 - 常用场景:标注在配置类上(如 Spring 主启动类),Spring Boot 的
@SpringBootApplication已内置@ComponentScan,无需手动添加。
示例:
// 配置类:指定扫描 com.example.service 和 com.example.controller 包
@Configuration
@ComponentScan(basePackages = {
"com.example.service",
"com.example.controller"
})
public class AppConfig {
// ...
}
// 过滤规则示例:只扫描带 @Service 注解的类
@ComponentScan(
includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Service.class),
useDefaultFilters = false // 禁用默认过滤规则(否则会扫描所有 @Component 衍生类)
)
三者关系与区别
| 注解 | 作用场景 | 标注位置 | 适用对象 | 与容器的关系 |
|---|---|---|---|---|
@Component |
标记自定义类为 Bean | 类 | 自己编写的类 | 自动扫描并实例化 |
@Bean |
定义第三方类或复杂对象为 Bean | 方法(配置类中) | 第三方类(如 DataSource) |
手动编写方法返回实例,容器管理 |
@ComponentScan |
指定扫描 @Component 类的范围 |
配置类 / 主类 | 无(扫描规则) | 指导容器发现并注册 @Component 类 |
总结
@Component是「声明式」定义 Bean,适用于自己开发的类,通过类注解让 Spring 自动管理。@Bean是「编程式」定义 Bean,适用于第三方类或复杂初始化逻辑,通过方法返回值手动创建 Bean。@ComponentScan是「扫描规则」,告诉 Spring 去哪里找@Component标注的类,确保它们能被容器发现。
三者配合使用,共同完成 Spring 容器中 Bean 的注册和管理,是 IoC 容器的核心实现机制。
@SpringBootApplication
@SpringBootApplication 是 Spring Boot 的核心注解,用于标记 Spring Boot 应用的主启动类,它是一个组合注解(由多个注解组合而成),简化了 Spring Boot 应用的配置和启动流程。
注解拆解:核心组成部分
从代码可以看出,@SpringBootApplication 组合了以下关键注解,每个注解都有其特定作用:
1. @SpringBootConfiguration
- 本质是
@Configuration的特殊形式,表明当前类是一个配置类(可定义@Bean方法)。 - 作用:使主启动类具备配置类的功能,允许在主类中直接定义 Bean 或配置。
2. @EnableAutoConfiguration(核心功能)
- 开启 Spring Boot 的自动配置机制:Spring Boot 会根据项目依赖(如
pom.xml中的 jar 包)自动配置相关组件(如引入spring-boot-starter-web依赖后,自动配置 Tomcat、Spring MVC 等)。 - 原理:通过扫描
META-INF/spring.factories文件(各 Starter 中定义),加载并实例化所需的自动配置类(如DispatcherServletAutoConfiguration、DataSourceAutoConfiguration等)。
3. @ComponentScan(自定义扫描规则)
- 用于指定 Spring 容器扫描组件(
@Component、@Service、@Controller等)的范围。 - 这里的默认配置:
- 扫描路径:默认扫描主启动类所在的包及其子包(因此通常将主类放在项目根包下,确保所有组件能被扫描到)。
- 排除规则(
excludeFilters):TypeExcludeFilter:允许通过自定义逻辑排除某些类(用于 Spring Boot 内部或扩展)。AutoConfigurationExcludeFilter:排除自动配置类(避免自动配置类被当作普通组件扫描,因为它们由@EnableAutoConfiguration单独处理)。
其他元注解(注解的注解)
@Target({ElementType.TYPE}):限制@SpringBootApplication只能标注在类或接口上(实际只用于主启动类)。@Retention(RetentionPolicy.RUNTIME):表明该注解在运行时有效,Spring 容器可在运行时读取并处理。@Documented:该注解会被包含在 Javadoc 文档中。@Inherited:表明该注解可被子类继承(主启动类通常无子类,此处更多是规范)。
核心作用
@SpringBootApplication 的核心价值是简化配置,通过一个注解替代了三个关键注解的组合:
// 等价于:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class MyApplication { ... }
它的存在让开发者无需手动配置:
- 组件扫描范围(默认扫描主类所在包)。
- 自动配置逻辑(根据依赖自动装配组件)。
- 配置类声明(主类可直接定义 Bean)。
使用示例
Spring Boot 应用的主启动类通常如下:
@SpringBootApplication
public class MallApplication {
public static void main(String[] args) {
// 启动 Spring Boot 应用,创建并启动 Spring 容器
SpringApplication.run(MallApplication.class, args);
}
}
- 执行
main方法时,@SpringBootApplication会触发自动配置和组件扫描,最终启动整个应用。
总结
@SpringBootApplication 是 Spring Boot 的 “门面注解”,通过组合 @SpringBootConfiguration、@EnableAutoConfiguration 和 @ComponentScan,实现了:
- 自动扫描并注册应用中的组件。
- 根据依赖自动配置框架组件(如 Web 服务器、数据库连接等)。
- 简化主启动类的配置,让开发者可以 “零配置” 快速启动应用。
它是 Spring Boot “约定大于配置” 理念的集中体现。
AOP
AOP(Aspect Oriented Programming)思想实际上就是:在运行时,动态地将代码切入到类的指定方法、指定位置上。也就是说,我们可以使用AOP来帮助我们在方法执行前或执行之后,做一些额外的操作,实际上,它就是代理!
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 jungle8884@163.com