java要点小结

  1. Maven
  2. WEB开发
    1. 三次握手四次挥手
      1. ✅ 一、TCP 三次握手(Three-Way Handshake)
        1. 💡 详细步骤(带原因)
        2. 💬 面试官喜欢听的关键句
      2. ✅ 二、TCP 四次挥手(Four-Way Handshake)
        1. 💡 详细步骤(带原因)
        2. 💬 面试官喜欢听的关键句
      3. ✅ 三、进阶补充(加分项 💥)
        1. 🔹 为什么握手三次而不是两次?
        2. 🔹 为什么挥手是四次?
        3. 🔹 为什么有 TIME_WAIT?
      4. 🎯 一份面试官评分高的回答模板(背下来)
    2. Cookie 与 Session
      1. 🧠 一、基础理解类(必考)
        1. 1️⃣ Cookie 与 Session 的区别?
        2. 2️⃣ Session 是如何工作的?
        3. 3️⃣ Cookie 的作用是什么?
      2. 🔒 二、安全与机制类(进阶)
        1. 4️⃣ Cookie 有哪些安全属性?
        2. 5️⃣ Session 是如何防止伪造的?
        3. 6️⃣ 如果 Cookie 被禁用,Session 还能用吗?
      3. ⚙️ 三、应用与扩展类(常问实战)
        1. 7️⃣ Session 存在服务器,如何实现分布式共享?
        2. 8️⃣ Session 与 Token(JWT)有何区别?
        3. 9️⃣ Cookie 的大小与数量限制?
        4. 🔟 Cookie 与 LocalStorage、SessionStorage 的区别?
      4. 💡 四、常见加分题 / 深挖题
        1. 11 Cookie 的跨域问题怎么解决?
        2. 12 如何实现“记住我”功能?
      5. ✅ 面试总结建议
  3. 🧩 Java Web 三大组件
    1. 🌍Servlet
      1. 一、什么是 Servlet?
        1. 🧠 Servlet 的生命周期(Lifecycle)
        2. 💡 Servlet 接口定义
      2. 二、HttpServlet —— Servlet 的子类
        1. 🌍 HttpServlet 常用方法
        2. 🧩 示例:最简单的 HttpServlet
      3. 三、总结对比
    2. 🧱 Filter(过滤器)
      1. 💡 1. 定义
      2. ⚙️ 2. 关键方法
      3. 🧠 3. 运行流程
      4. ✍️ 4. 示例:登录验证过滤器
      5. 🧩 5. Filter 链执行顺序
    3. 🔔 Listener(监听器)
      1. 💡 1. 定义
      2. ⚙️ 2. 常见监听器类型
      3. ✍️ 3. 示例 1:统计在线人数
      4. ✍️ 4. 示例 2:应用启动初始化资源
    4. 🧠 Filter vs Listener 对比总结
    5. 🧩 面试高频问题 💬
    6. 🧩 整体结构图
    7. 🧠 图解总结
    8. ⚙️ 执行顺序举例
    9. 🧩 Listener 的触发时机图
    10. 📚 记忆口诀
  4. Spring
    1. IoC
    2. DI
    3. IOC 与 DI 的关系
    4. @Resource
      1. 1. 来源与规范
      2. 2. 注入逻辑(匹配方式)
        1. @Autowired
        2. @Resource
      3. 3. 支持的注入位置
      4. 4. required 属性(是否允许依赖为 null)
      5. 5. 总结:核心差异对比
      6. 使用建议
    5. @Component
      1. 1. @Component:类级别的 Bean 定义
        1. 核心特点:
        2. 衍生注解:
        3. 示例:
      2. 2. @Bean:方法级别的 Bean 定义
        1. 核心特点:
        2. 示例:
      3. 3. @ComponentScan:指定 Bean 的扫描范围
        1. 核心特点:
        2. 示例:
      4. 三者关系与区别
      5. 总结
    6. @SpringBootApplication
      1. 注解拆解:核心组成部分
        1. 1. @SpringBootConfiguration
        2. 2. @EnableAutoConfiguration(核心功能)
        3. 3. @ComponentScan(自定义扫描规则)
      2. 其他元注解(注解的注解)
      3. 核心作用
      4. 使用示例
      5. 总结
    7. AOP

Maven

image-20251010173036324

image-20251010173314464

image-20251010173552287

image-20251010174340226

image-20251010174432599

image-20251010174848440

image-20251010175306310


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
存储位置 客户端(浏览器) 服务器
数据存储 以键值对存储在浏览器中 保存在服务器内存或缓存中(如Redis)
安全性 较低,容易被伪造或窃取 较高,因为数据存在服务器
生命周期 可设定过期时间 通常依赖 Cookie 的 session_id,有效期短
用途 记录用户偏好、登录状态等 管理用户会话、登录信息

加分点:Session 底层通常依赖 Cookie 存放一个 session_id。


2️⃣ Session 是如何工作的?

回答要点:

  1. 用户第一次访问服务器 → 服务器创建 Session,并生成唯一的 session_id
  2. 服务器将 session_id 写入到 Cookie 中,返回给客户端。
  3. 客户端后续请求时,会自动携带该 Cookie。
  4. 服务器根据 session_id 找回对应的 Session 数据,实现会话保持。

加分点:如果浏览器禁用了 Cookie,可以通过 URL 重写(?sessionid=xxx)来维持 Session。


  • 保存用户登录状态(如 token)
  • 保存用户偏好(如语言、主题)
  • 统计用户行为(如访问次数)

加分点:Cookie 是 HTTP 无状态协议下维持状态的一种方式。


🔒 二、安全与机制类(进阶)

属性 作用
HttpOnly 阻止 JS 访问 Cookie,防止 XSS 窃取
Secure 仅在 HTTPS 传输时携带
SameSite 限制第三方请求携带 Cookie,防止 CSRF 攻击

加分点:在实际开发中 HttpOnly + Secure + SameSite=Lax 是推荐组合。


5️⃣ Session 是如何防止伪造的?

  • Session ID 是随机且唯一的。
  • Session ID 存储在 Cookie 中且服务器验证。
  • 可以配合 IP、User-Agent 校验。
  • 一旦用户登出或过期,Session 即无效。

✅ 可以,但需要:

  • 在 URL 参数中附加 session_id
  • 或使用隐藏表单字段传递

但安全性和体验会变差。


⚙️ 三、应用与扩展类(常问实战)

7️⃣ Session 存在服务器,如何实现分布式共享?

面试官想考: 你是否了解分布式架构思维。

回答要点:

  • 不能单纯存在单台机器内存中,否则多机登录失效。
  • 常见解决方案:
    1. Session 复制(性能差)
    2. Session 持久化到数据库
    3. Session 存储到 Redis(推荐)
    4. JWT(无状态替代方案)

加分点:提到“Session 共享”或“JWT无状态替代”都很亮眼。


8️⃣ Session 与 Token(JWT)有何区别?

特点 Session Token(JWT)
状态 有状态(需服务器存储) 无状态(自包含)
适用场景 Web 应用 前后端分离 / 移动端
安全性 高,服务器可控 一旦泄漏难撤销
扩展性 一般 优秀,跨服务可共享

加分点:Token 可在分布式系统中代替 Session,减少服务器存储负担。


  • 单个 Cookie 最大约 4KB。
  • 每个域名下浏览器通常最多支持 20~50 个 Cookie。
  • 超出部分会被丢弃。

项目 Cookie LocalStorage SessionStorage
存储位置 浏览器 浏览器 浏览器
生命周期 可自定义 永久(除非手动清除) 仅当前会话
是否随请求发送 ✅ 是 ❌ 否 ❌ 否
大小限制 ~4KB ~5MB ~5MB

加分点:SessionStorage 适合保存一次性会话数据。


💡 四、常见加分题 / 深挖题

  • 设置 Access-Control-Allow-Credentials: true
  • 设置 Access-Control-Allow-Origin 为具体域名(不能是 *
  • 客户端请求中配置 withCredentials: true

12 如何实现“记住我”功能?

  • 登录成功时,生成一个长期有效的 token(如 7 天)。
  • 存入 Cookie 中,并在下次登录时校验。

加分点:记住我用 Cookie,但不要直接存用户名密码,要用加密 token。


✅ 面试总结建议

题型 面试难度 掌握层次
区别题 必会
工作机制 ⭐⭐ 要理解流程
安全相关 ⭐⭐⭐ 可深入讲攻击防范
分布式/Token 扩展 ⭐⭐⭐⭐ 面试亮点加分题

🧩 Java Web 三大组件

  1. Servlet → 处理请求、返回响应(业务核心)
  2. Filter → 过滤器,负责“拦截与放行”
  3. Listener → 监听器,负责“监听事件”

🌍Servlet

一、什么是 Servlet?

Servlet(Server Applet) 是运行在服务器端的 Java 程序组件,用于处理客户端请求、生成响应。
它相当于 Java Web 中的“控制器”。

  • 属于 javax.servlet 包;
  • Servlet 容器(如 Tomcat) 调用执行;
  • 本质是一个遵守特定接口规范的 Java 类

🧠 Servlet 的生命周期(Lifecycle)

Servlet 的生命周期由容器(如 Tomcat)管理:

阶段 方法 说明
初始化 init() 只执行一次,Servlet 被加载时调用
处理请求 service() 每次请求都会调用
销毁 destroy() 当容器卸载 Servlet 时调用

image-20251011110922970

点击查看源网页


💡 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 等)
实现方式 @WebFilterweb.xml @WebListenerweb.xml

🧩 面试高频问题 💬

  1. Filter 与 Interceptor(拦截器)的区别?
    • Filter 属于 Servlet 规范,运行在 Servlet 容器层(更底层);
    • Interceptor 属于 Spring MVC,运行在控制器调用前后;
    • Interceptor 能拿到控制器方法信息(HandlerMethod),Filter 不行。
  2. Filter 执行顺序如何控制?
    • web.xml 配置顺序;
    • Spring Boot 可通过 @Order 注解控制。
  3. 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 的具体技术

注入方式

  1. 构造器注入:通过构造方法参数传递依赖。

    public class A {
        private B b;
        // 容器通过构造器注入 B
        public A(B b) { 
            this.b = b; 
        }
    }
    
  2. Setter 注入:通过 setter 方法传递依赖。

    public class A {
        private B b;
        // 容器通过 setter 注入 B
        public void setB(B b) { 
            this.b = b; 
        }
    }
    
  3. 字段注入:直接在字段上使用注解(如 @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 名称:默认是类名首字母小写(如 UserServiceuserService),也可通过 value 属性指定(如 @Component("myUserService"))。

衍生注解:

为了更清晰地表达组件的用途,Spring 提供了 @Component 的三个衍生注解(功能相同,语义不同):

  • @Service:用于标注「服务层」组件(业务逻辑类)。
  • @Controller:用于标注「控制层」组件(MVC 中的控制器)。
  • @Repository:用于标注「数据访问层」组件(DAO 类),还会自动转换数据库操作异常。

示例:

// 标注为组件,Spring 会自动创建其实例并纳入容器
@Component
public class UserService {
    // 业务逻辑...
}

2. @Bean:方法级别的 Bean 定义

作用:标注在方法上,表明该方法的返回值是一个需要被 Spring 管理的 Bean,适用于第三方类或无法直接修改源码的类的实例化。

核心特点:

  • 作用于方法:只能标注在方法上,方法的返回值会被注册为 Spring 容器中的 Bean。
  • 依赖外部类:通常用于创建第三方库的对象(如 DataSourceRestTemplate),或需要复杂初始化逻辑的对象。
  • 所属配置类:方法必须定义在被 @Configuration 标注的「配置类」中。
  • 默认 Bean 名称:默认是方法名,也可通过 valuename 属性指定(如 @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。

核心特点:

  • 扫描路径:默认扫描当前类所在的包及其子包,也可通过 basePackagesbasePackageClasses 手动指定扫描路径。
  • 过滤规则:可通过 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 中定义),加载并实例化所需的自动配置类(如 DispatcherServletAutoConfigurationDataSourceAutoConfiguration 等)。

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 { ... }

它的存在让开发者无需手动配置:

  1. 组件扫描范围(默认扫描主类所在包)。
  2. 自动配置逻辑(根据依赖自动装配组件)。
  3. 配置类声明(主类可直接定义 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

×

喜欢就点赞,疼爱就打赏