最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring IOC 和 AOP 從入門到精通

 更新時間:2026年03月18日 11:52:46   作者:無名-CODING  
本文將深入淺出地講解Spring框架的兩大核心特性——IOC(控制反轉)和AOP(面向切面編程),本文結合實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧

?? 博客簡介:本文將深入淺出地講解Spring框架的兩大核心特性——IOC(控制反轉)和AOP(面向切面編程)。適合Spring初學者和準備面試的同學閱讀。

?? 知識目標:理解IOC和AOP的概念、實現(xiàn)機制、應用場景,并掌握依賴注入和反射的原理。

一、Spring IOC(控制反轉)詳解

1.1 什么是IOC?

?? 概念解釋

IOC(Inversion of Control,控制反轉) 是一種設計原則,用于降低代碼之間的耦合度。

?? 通俗理解:想象你是一個老板,以前你需要自己去找員工(new 對象),現(xiàn)在你把這個任務交給HR部門(Spring容器),你只需要告訴HR你需要什么樣的員工(聲明依賴),HR會幫你找到合適的人(注入對象)。這就是"控制反轉"——對象創(chuàng)建的控制權從你的代碼轉移到了Spring容器。

?? 核心思想

傳統(tǒng)開發(fā)模式:

對象A需要對象B → 對象A自己創(chuàng)建對象B(new B())

IOC模式:

對象A需要對象B → 對象A聲明需要B → Spring容器創(chuàng)建B并注入給A

?? 對比示例

傳統(tǒng)方式(緊耦合):

/**
 * 傳統(tǒng)方式:UserService 直接創(chuàng)建 UserDao
 * 缺點:
 * 1. 高耦合:UserService 直接依賴 UserDao 的具體實現(xiàn)
 * 2. 難以測試:無法輕易替換 UserDao 的實現(xiàn)
 * 3. 不靈活:如果要換一個 UserDao 實現(xiàn),需要修改 UserService 代碼
 */
public class UserService {
    // 直接在類內部創(chuàng)建依賴對象
    private UserDao userDao = new UserDaoImpl();
    public void addUser(String username) {
        // 使用 userDao 進行數(shù)據(jù)庫操作
        userDao.save(username);
    }
}
// UserDao 接口
interface UserDao {
    void save(String username);
}
// UserDao 的具體實現(xiàn)
class UserDaoImpl implements UserDao {
    @Override
    public void save(String username) {
        System.out.println("保存用戶:" + username);
    }
}

IOC方式(松耦合):

/**
 * IOC方式:UserService 不再自己創(chuàng)建 UserDao,而是通過依賴注入獲得
 * 優(yōu)點:
 * 1. 低耦合:UserService 只依賴 UserDao 接口,不關心具體實現(xiàn)
 * 2. 易于測試:可以輕松注入模擬對象(Mock)進行單元測試
 * 3. 靈活性高:可以通過配置切換不同的 UserDao 實現(xiàn),無需修改代碼
 */
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
@Service  // 告訴Spring這是一個服務類,需要被Spring管理
public class UserService {
    // 使用@Autowired注解,讓Spring自動注入UserDao的實例
    // Spring會在容器中查找UserDao類型的Bean,并自動賦值給這個變量
    @Autowired
    private UserDao userDao;
    public void addUser(String username) {
        // 使用注入的 userDao 對象
        userDao.save(username);
    }
}
// UserDao 接口定義
interface UserDao {
    void save(String username);
}
// UserDao 的實現(xiàn)類
import org.springframework.stereotype.Repository;
@Repository  // 告訴Spring這是一個數(shù)據(jù)訪問層組件,需要被Spring管理
class UserDaoImpl implements UserDao {
    @Override
    public void save(String username) {
        System.out.println("保存用戶到數(shù)據(jù)庫:" + username);
    }
}

1.2 IOC和AOP是通過什么機制來實現(xiàn)的?

??? IOC的實現(xiàn)機制

Spring IOC容器主要通過以下技術實現(xiàn):

  1. 反射(Reflection):動態(tài)創(chuàng)建對象
  2. 工廠模式:管理Bean的生命周期
  3. 注解或XML配置:描述Bean之間的依賴關系

?? IOC容器工作流程

┌─────────────────────────────────────────────────────┐
│                   Spring IOC容器                      │
│                                                       │
│  1. 讀取配置(XML/注解)                               │
│         ↓                                             │
│  2. 解析Bean定義信息                                   │
│         ↓                                             │
│  3. 使用反射創(chuàng)建Bean實例                               │
│         ↓                                             │
│  4. 進行依賴注入(屬性賦值)                           │
│         ↓                                             │
│  5. 初始化Bean(調用初始化方法)                       │
│         ↓                                             │
│  6. 將Bean放入容器(可供使用)                         │
│         ↓                                             │
│  7. 容器關閉時銷毀Bean                                 │
└─────────────────────────────────────────────────────┘

?? AOP的實現(xiàn)機制

Spring AOP主要通過以下技術實現(xiàn):

  1. 動態(tài)代理(Dynamic Proxy)
    • JDK動態(tài)代理:針對實現(xiàn)了接口的類
    • CGLIB代理:針對沒有實現(xiàn)接口的類
  2. 字節(jié)碼增強:在運行時修改或生成字節(jié)碼

?? 動態(tài)代理原理圖

                原始對象(Target)
                      ↓
            Spring AOP 檢測到需要增強
                      ↓
            創(chuàng)建代理對象(Proxy)
                      ↓
     ┌─────────────────────────────────┐
     │         代理對象                 │
     │  ┌──────────────────────────┐   │
     │  │  前置通知(Before)       │   │
     │  └──────────────────────────┘   │
     │              ↓                   │
     │  ┌──────────────────────────┐   │
     │  │  調用原始方法             │   │
     │  └──────────────────────────┘   │
     │              ↓                   │
     │  ┌──────────────────────────┐   │
     │  │  后置通知(After)        │   │
     │  └──────────────────────────┘   │
     └─────────────────────────────────┘

1.3 怎么理解Spring IOC?

?? 核心理解

Spring IOC的本質是一個對象工廠,它負責:

  1. 創(chuàng)建對象:根據(jù)配置信息創(chuàng)建Bean實例
  2. 管理對象:管理對象的生命周期(從創(chuàng)建到銷毀)
  3. 裝配對象:處理對象之間的依賴關系

?? IOC容器的兩種類型

容器類型說明應用場景
BeanFactory基礎容器,延遲加載Bean資源受限的環(huán)境
ApplicationContext高級容器,啟動時加載所有Bean企業(yè)級應用(推薦)

?? 生活類比:BeanFactory像外賣平臺,你點餐(調用getBean)時才開始做菜(創(chuàng)建Bean);ApplicationContext像自助餐廳,所有菜品(Bean)在開門時就已經準備好了。

1.4 依賴注入(DI)詳解

?? 什么是依賴注入?

依賴注入(Dependency Injection,DI) 是IOC的一種實現(xiàn)方式,它是指:由Spring容器在運行期間,動態(tài)地將依賴關系注入到對象中。

?? 通俗理解:你開了一家餐廳(類),需要廚師(依賴對象)。傳統(tǒng)方式是你自己去招聘(new對象),而依賴注入就是HR(Spring容器)直接把合格的廚師送到你的餐廳。

?? 依賴注入的三種方式

1?? 構造器注入(Constructor Injection)

特點:通過構造方法注入依賴,適合必需的、不可變的依賴。

/**
 * 構造器注入示例
 * 優(yōu)點:
 * 1. 依賴關系明確,必須在對象創(chuàng)建時提供
 * 2. 注入的對象可以聲明為final,保證不可變性
 * 3. 便于編寫單元測試
 * 
 * 適用場景:強制依賴、不可變對象
 */
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
@Service
public class OrderService {
    // 使用final確保依賴不可變
    private final UserService userService;
    private final ProductService productService;
    /**
     * 構造器注入
     * @Autowired 可以省略(Spring 4.3+,只有一個構造器時)
     * 
     * @param userService 用戶服務
     * @param productService 商品服務
     */
    @Autowired
    public OrderService(UserService userService, ProductService productService) {
        // Spring會自動傳入這兩個參數(shù)
        this.userService = userService;
        this.productService = productService;
        System.out.println("OrderService通過構造器注入創(chuàng)建");
    }
    /**
     * 創(chuàng)建訂單的業(yè)務方法
     */
    public void createOrder(String username, String productName) {
        // 使用注入的依賴
        userService.addUser(username);
        System.out.println("訂單創(chuàng)建成功:用戶=" + username + ", 商品=" + productName);
    }
}

2?? Setter方法注入(Setter Injection)

特點:通過setter方法注入依賴,適合可選的、可變的依賴。

/**
 * Setter注入示例
 * 優(yōu)點:
 * 1. 靈活性高,可以在對象創(chuàng)建后修改依賴
 * 2. 可以注入可選依賴
 * 
 * 缺點:
 * 1. 可能造成對象處于不完整狀態(tài)
 * 2. 依賴關系不夠明確
 * 
 * 適用場景:可選依賴、需要重新配置的依賴
 */
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
@Service
public class EmailService {
    private MailSender mailSender;  // 非final,可以修改
    /**
     * Setter方法注入
     * Spring會自動調用這個方法,傳入MailSender實例
     * 
     * @param mailSender 郵件發(fā)送器
     */
    @Autowired
    public void setMailSender(MailSender mailSender) {
        this.mailSender = mailSender;
        System.out.println("通過Setter方法注入MailSender");
    }
    /**
     * 發(fā)送郵件
     */
    public void sendEmail(String to, String content) {
        if (mailSender != null) {
            mailSender.send(to, content);
        } else {
            System.out.println("MailSender未注入,無法發(fā)送郵件");
        }
    }
}

3?? 字段注入(Field Injection)

特點:直接在字段上使用@Autowired注解,最簡潔但不推薦。

/**
 * 字段注入示例
 * 優(yōu)點:
 * 1. 代碼最簡潔
 * 2. 使用方便
 * 
 * 缺點:
 * 1. 難以進行單元測試(無法輕松注入Mock對象)
 * 2. 違反了依賴注入的原則(依賴關系不明確)
 * 3. 無法聲明為final
 * 4. IDE會給出警告
 * 
 * 適用場景:快速開發(fā)、原型項目(生產環(huán)境不推薦)
 */
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
@Service
public class PaymentService {
    // 直接在字段上注入,Spring通過反射設置值
    @Autowired
    private UserService userService;
    @Autowired
    private OrderService orderService;
    /**
     * 處理支付
     */
    public void processPayment(String username, double amount) {
        System.out.println("處理支付:用戶=" + username + ", 金額=" + amount);
        // 使用注入的依賴
        userService.addUser(username);
    }
}

?? 三種注入方式對比

注入方式語法復雜度測試友好度依賴明確性推薦度適用場景
構造器注入中等???????????????必需依賴、生產環(huán)境
Setter注入簡單??????????可選依賴
字段注入最簡單??????快速開發(fā)、原型

?? 最佳實踐:優(yōu)先使用構造器注入,它是Spring官方推薦的方式。

二、Spring AOP(面向切面編程)詳解

2.1 什么是AOP?

?? 概念解釋

AOP(Aspect Oriented Programming,面向切面編程) 是一種編程范式,它允許程序員模塊化橫切關注點(Cross-cutting Concerns)。

?? 通俗理解:想象你在開發(fā)一個電商系統(tǒng),每個功能(登錄、下單、支付)都需要記錄日志、檢查權限、統(tǒng)計性能。如果在每個方法里都寫這些代碼,會非常重復。AOP就像一個"管家",它可以在方法執(zhí)行前后自動幫你做這些事,而你只需要專注于業(yè)務邏輯。

?? 核心概念

概念英文說明舉例
切面Aspect橫切關注點的模塊化日志切面、事務切面
連接點Join Point程序執(zhí)行的某個點方法調用、異常拋出
切點Pointcut匹配連接點的表達式所有Service層的方法
通知Advice在切點執(zhí)行的代碼前置通知、后置通知
目標對象Target被增強的對象UserService實例
代理對象ProxyAOP創(chuàng)建的對象UserService的代理
織入Weaving將切面應用到目標的過程Spring在運行時織入

?? AOP概念圖解

                    切面(Aspect)
                    ┌─────────────────────┐
                    │  @Aspect             │
                    │  LoggingAspect       │
                    │                      │
                    │  切點(Pointcut)     │
                    │  ↓                   │
                    │  execution(* com.example..*(..))  │
                    │                      │
                    │  通知(Advice)       │
                    │  ↓                   │
                    │  @Before             │
                    │  @After              │
                    │  @Around             │
                    └─────────────────────┘
                              ↓
                        織入(Weaving)
                              ↓
                    ┌─────────────────────┐
                    │  目標對象(Target)   │
                    │  UserService         │
                    └─────────────────────┘
                              ↓
                    ┌─────────────────────┐
                    │  代理對象(Proxy)    │
                    │  UserServiceProxy    │
                    └─────────────────────┘

?? AOP的五種通知類型

/**
 * AOP通知類型完整示例
 * 演示五種通知類型的執(zhí)行時機和用法
 */
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
/**
 * 日志切面:演示各種通知類型
 */
@Aspect  // 標記這是一個切面類
@Component  // 將切面類注冊為Spring Bean
public class LoggingAspect {
    /**
     * 1. 前置通知(Before Advice)
     * 在目標方法執(zhí)行之前執(zhí)行
     * 
     * 使用場景:參數(shù)驗證、權限檢查、日志記錄
     * 
     * @param joinPoint 連接點,包含方法信息
     */
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
        // 獲取方法名
        String methodName = joinPoint.getSignature().getName();
        // 獲取參數(shù)
        Object[] args = joinPoint.getArgs();
        System.out.println("=== 前置通知 ===");
        System.out.println("即將執(zhí)行方法:" + methodName);
        System.out.println("方法參數(shù):" + java.util.Arrays.toString(args));
    }
    /**
     * 2. 后置通知(After Returning Advice)
     * 在目標方法正常返回后執(zhí)行(不包括異常情況)
     * 
     * 使用場景:記錄返回值、數(shù)據(jù)處理、緩存更新
     * 
     * @param joinPoint 連接點
     * @param result 方法返回值
     */
    @AfterReturning(
        pointcut = "execution(* com.example.service.*.*(..))",
        returning = "result"  // 指定返回值參數(shù)名
    )
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("=== 后置通知 ===");
        System.out.println("方法執(zhí)行成功:" + methodName);
        System.out.println("返回值:" + result);
    }
    /**
     * 3. 異常通知(After Throwing Advice)
     * 在目標方法拋出異常后執(zhí)行
     * 
     * 使用場景:異常日志記錄、異常轉換、告警通知
     * 
     * @param joinPoint 連接點
     * @param exception 拋出的異常
     */
    @AfterThrowing(
        pointcut = "execution(* com.example.service.*.*(..))",
        throwing = "exception"  // 指定異常參數(shù)名
    )
    public void afterThrowingAdvice(JoinPoint joinPoint, Exception exception) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("=== 異常通知 ===");
        System.out.println("方法執(zhí)行異常:" + methodName);
        System.out.println("異常信息:" + exception.getMessage());
    }
    /**
     * 4. 最終通知(After Advice)
     * 在目標方法執(zhí)行之后執(zhí)行(無論是否拋出異常)
     * 類似于finally塊
     * 
     * 使用場景:資源釋放、清理工作
     * 
     * @param joinPoint 連接點
     */
    @After("execution(* com.example.service.*.*(..))")
    public void afterAdvice(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("=== 最終通知 ===");
        System.out.println("方法執(zhí)行完畢:" + methodName);
    }
    /**
     * 5. 環(huán)繞通知(Around Advice)
     * 包圍目標方法,可以控制方法的執(zhí)行
     * 最強大的通知類型
     * 
     * 使用場景:性能統(tǒng)計、事務管理、緩存處理
     * 
     * @param proceedingJoinPoint 可執(zhí)行的連接點
     * @return 方法返回值
     * @throws Throwable 方法可能拋出的異常
     */
    @Around("execution(* com.example.service.*.*(..))")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        String methodName = proceedingJoinPoint.getSignature().getName();
        System.out.println("=== 環(huán)繞通知開始 ===");
        System.out.println("準備執(zhí)行方法:" + methodName);
        // 記錄開始時間
        long startTime = System.currentTimeMillis();
        Object result = null;
        try {
            // 執(zhí)行目標方法(這是關鍵?。?
            result = proceedingJoinPoint.proceed();
            // 目標方法執(zhí)行成功
            System.out.println("方法執(zhí)行成功,返回值:" + result);
        } catch (Throwable throwable) {
            // 目標方法執(zhí)行失敗
            System.out.println("方法執(zhí)行失?。? + throwable.getMessage());
            throw throwable;  // 重新拋出異常
        } finally {
            // 計算執(zhí)行時間
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
            System.out.println("方法執(zhí)行耗時:" + executionTime + "ms");
            System.out.println("=== 環(huán)繞通知結束 ===");
        }
        // 返回結果(可以修改返回值)
        return result;
    }
}

?? 通知執(zhí)行順序

正常執(zhí)行流程:
@Around 開始
    ↓
@Before
    ↓
目標方法執(zhí)行
    ↓
@AfterReturning
    ↓
@After
    ↓
@Around 結束
異常執(zhí)行流程:
@Around 開始
    ↓
@Before
    ↓
目標方法執(zhí)行(拋出異常)
    ↓
@AfterThrowing
    ↓
@After
    ↓
@Around 異常處理

2.2 Spring AOP主要想解決什么問題?

?? 解決的核心問題

Spring AOP主要解決代碼重復橫切關注點分離的問題。

?? 橫切關注點示例

        業(yè)務邏輯層
┌─────────────────────────┐
│                         │
│  UserService            │ ← 日志、事務、權限
│  OrderService           │ ← 日志、事務、權限
│  ProductService         │ ← 日志、事務、權限
│  PaymentService         │ ← 日志、事務、權限
│                         │
└─────────────────────────┘
橫切關注點:
- 日志記錄
- 事務管理
- 權限檢查
- 性能監(jiān)控

? 沒有AOP的代碼(問題代碼)

/**
 * 沒有AOP的代碼:每個方法都要寫重復的橫切邏輯
 * 問題:
 * 1. 代碼重復
 * 2. 業(yè)務邏輯和橫切邏輯混在一起
 * 3. 難以維護
 * 4. 修改橫切邏輯需要改動所有方法
 */
public class UserServiceWithoutAOP {
    public void addUser(String username) {
        // ===== 橫切邏輯開始 =====
        // 1. 日志記錄
        System.out.println("[日志] 開始執(zhí)行addUser方法,參數(shù):" + username);
        long startTime = System.currentTimeMillis();
        try {
            // 2. 權限檢查
            if (!checkPermission()) {
                System.out.println("[權限] 無權限執(zhí)行此操作");
                return;
            }
            // 3. 事務開啟
            beginTransaction();
            // ===== 業(yè)務邏輯開始 =====
            System.out.println("執(zhí)行添加用戶的業(yè)務邏輯:" + username);
            // 實際的業(yè)務代碼...
            // ===== 業(yè)務邏輯結束 =====
            // 4. 事務提交
            commitTransaction();
        } catch (Exception e) {
            // 5. 事務回滾
            rollbackTransaction();
            System.out.println("[異常] 方法執(zhí)行失?。? + e.getMessage());
        } finally {
            // 6. 性能統(tǒng)計
            long endTime = System.currentTimeMillis();
            System.out.println("[性能] 方法執(zhí)行耗時:" + (endTime - startTime) + "ms");
        }
        // ===== 橫切邏輯結束 =====
    }
    public void deleteUser(String username) {
        // 同樣的橫切邏輯又要寫一遍...
        System.out.println("[日志] 開始執(zhí)行deleteUser方法,參數(shù):" + username);
        // ... 重復代碼 ...
    }
    // 輔助方法
    private boolean checkPermission() { return true; }
    private void beginTransaction() { }
    private void commitTransaction() { }
    private void rollbackTransaction() { }
}

? 使用AOP的代碼(優(yōu)雅代碼)

/**
 * 使用AOP的代碼:業(yè)務邏輯和橫切邏輯完全分離
 * 優(yōu)點:
 * 1. 代碼簡潔,只關注業(yè)務邏輯
 * 2. 橫切邏輯集中管理
 * 3. 易于維護和擴展
 * 4. 修改橫切邏輯只需修改切面類
 */
import org.springframework.stereotype.Service;
@Service
public class UserServiceWithAOP {
    /**
     * 添加用戶
     * 注意:這個方法只包含業(yè)務邏輯
     * 日志、權限、事務等橫切邏輯由AOP切面自動處理
     */
    public void addUser(String username) {
        // 純粹的業(yè)務邏輯,簡潔明了
        System.out.println("執(zhí)行添加用戶的業(yè)務邏輯:" + username);
    }
    /**
     * 刪除用戶
     */
    public void deleteUser(String username) {
        // 純粹的業(yè)務邏輯
        System.out.println("執(zhí)行刪除用戶的業(yè)務邏輯:" + username);
    }
}

三、反射機制詳解

3.1 什么是反射?

?? 概念解釋

反射(Reflection) 是Java提供的一種能力,允許程序在運行時檢查和操作類、方法、字段等信息,即使在編譯時不知道它們的名稱。

?? 通俗理解:想象你是一個偵探,反射就像是一個X光機,可以讓你在程序運行時"透視"一個對象,看到它的內部結構(字段)、能力(方法)、身份(類型),甚至可以修改它們。

?? 反射的核心功能

  1. 獲取類信息:獲取類的名稱、父類、接口、修飾符等
  2. 創(chuàng)建對象:動態(tài)創(chuàng)建類的實例
  3. 訪問字段:獲取和修改對象的字段值(包括私有字段)
  4. 調用方法:動態(tài)調用對象的方法(包括私有方法)
  5. 操作數(shù)組:動態(tài)創(chuàng)建和操作數(shù)組

?? 反射API核心類

類名說明主要方法
Class表示類的信息forName(), newInstance(), getName()
Constructor表示構造方法newInstance(), getParameterTypes()
Method表示方法invoke(), getName(), getReturnType()
Field表示字段get(), set(), setAccessible()

?? 反射完整示例

/**
 * 反射機制完整示例
 * 演示反射的各種用法
 */
import java.lang.reflect.*;
/**
 * 用戶類(作為反射操作的目標)
 */
class User {
    // 私有字段
    private String username;
    private int age;
    // 無參構造器
    public User() {
        System.out.println("無參構造器被調用");
    }
    // 有參構造器
    public User(String username, int age) {
        this.username = username;
        this.age = age;
        System.out.println("有參構造器被調用:" + username + ", " + age);
    }
    // 公共方法
    public void sayHello() {
        System.out.println("Hello, I'm " + username);
    }
    // 私有方法
    private void secretMethod() {
        System.out.println("這是一個私有方法");
    }
    // Getter和Setter
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    @Override
    public String toString() {
        return "User{username='" + username + "', age=" + age + "}";
    }
}
/**
 * 反射示例類
 */
public class ReflectionDemo {
    public static void main(String[] args) throws Exception {
        System.out.println("=== 反射機制演示 ===\n");
        // 1. 獲取Class對象的三種方式
        demonstrateGetClass();
        // 2. 創(chuàng)建對象
        demonstrateCreateInstance();
        // 3. 訪問和修改字段
        demonstrateFieldAccess();
        // 4. 調用方法
        demonstrateMethodInvocation();
        // 5. 獲取類信息
        demonstrateClassInfo();
    }
    /**
     * 演示獲取Class對象的三種方式
     */
    private static void demonstrateGetClass() throws Exception {
        System.out.println("=== 1. 獲取Class對象 ===");
        // 方式1:通過類名.class
        Class<?> clazz1 = User.class;
        System.out.println("方式1(類名.class):" + clazz1.getName());
        // 方式2:通過對象.getClass()
        User user = new User();
        Class<?> clazz2 = user.getClass();
        System.out.println("方式2(對象.getClass()):" + clazz2.getName());
        // 方式3:通過Class.forName()(最常用)
        Class<?> clazz3 = Class.forName("User");
        System.out.println("方式3(Class.forName()):" + clazz3.getName());
        System.out.println();
    }
    /**
     * 演示創(chuàng)建對象
     */
    private static void demonstrateCreateInstance() throws Exception {
        System.out.println("=== 2. 創(chuàng)建對象 ===");
        Class<?> clazz = User.class;
        // 方式1:使用無參構造器創(chuàng)建對象
        System.out.println("方式1:使用無參構造器");
        User user1 = (User) clazz.getDeclaredConstructor().newInstance();
        // 方式2:使用有參構造器創(chuàng)建對象
        System.out.println("\n方式2:使用有參構造器");
        Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
        User user2 = (User) constructor.newInstance("張三", 25);
        System.out.println("創(chuàng)建的對象:" + user2);
        System.out.println();
    }
    /**
     * 演示訪問和修改字段
     */
    private static void demonstrateFieldAccess() throws Exception {
        System.out.println("=== 3. 訪問和修改字段 ===");
        User user = new User("李四", 30);
        Class<?> clazz = user.getClass();
        // 獲取私有字段
        Field usernameField = clazz.getDeclaredField("username");
        Field ageField = clazz.getDeclaredField("age");
        // 設置可訪問(繞過private限制)
        usernameField.setAccessible(true);
        ageField.setAccessible(true);
        // 讀取字段值
        System.out.println("原始username:" + usernameField.get(user));
        System.out.println("原始age:" + ageField.get(user));
        // 修改字段值
        usernameField.set(user, "王五");
        ageField.set(user, 35);
        System.out.println("修改后的對象:" + user);
        System.out.println();
    }
    /**
     * 演示調用方法
     */
    private static void demonstrateMethodInvocation() throws Exception {
        System.out.println("=== 4. 調用方法 ===");
        User user = new User("趙六", 28);
        Class<?> clazz = user.getClass();
        // 調用公共方法
        System.out.println("調用公共方法:");
        Method sayHelloMethod = clazz.getMethod("sayHello");
        sayHelloMethod.invoke(user);
        // 調用私有方法
        System.out.println("\n調用私有方法:");
        Method secretMethod = clazz.getDeclaredMethod("secretMethod");
        secretMethod.setAccessible(true);  // 繞過private限制
        secretMethod.invoke(user);
        System.out.println();
    }
    /**
     * 演示獲取類信息
     */
    private static void demonstrateClassInfo() throws Exception {
        System.out.println("=== 5. 獲取類信息 ===");
        Class<?> clazz = User.class;
        // 獲取類名
        System.out.println("類名:" + clazz.getName());
        System.out.println("簡單類名:" + clazz.getSimpleName());
        // 獲取所有公共字段
        System.out.println("\n公共字段:");
        Field[] publicFields = clazz.getFields();
        for (Field field : publicFields) {
            System.out.println("  " + field.getName());
        }
        // 獲取所有字段(包括私有)
        System.out.println("\n所有字段:");
        Field[] allFields = clazz.getDeclaredFields();
        for (Field field : allFields) {
            System.out.println("  " + Modifier.toString(field.getModifiers()) + 
                             " " + field.getType().getSimpleName() + 
                             " " + field.getName());
        }
        // 獲取所有方法
        System.out.println("\n所有方法:");
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("  " + method.getName() + "()");
        }
        System.out.println();
    }
}

3.2 反射的使用場景

?? 主要使用場景

場景說明示例
框架開發(fā)Spring IOC容器使用反射創(chuàng)建BeanSpring, Hibernate
動態(tài)代理AOP使用反射實現(xiàn)動態(tài)代理Spring AOP, MyBatis
注解處理讀取和處理注解JUnit, Spring
序列化/反序列化JSON與對象互轉Jackson, Gson, Fastjson
ORM框架數(shù)據(jù)庫記錄映射為對象Hibernate, MyBatis
插件系統(tǒng)動態(tài)加載和執(zhí)行插件Eclipse插件, IDEA插件
工具類通用的工具方法BeanUtils, PropertyUtils

?? 反射的優(yōu)缺點

優(yōu)點:

  • ? 增加程序的靈活性,可以在運行時動態(tài)操作
  • ? 降低耦合度,無需在編譯時確定具體類型
  • ? 支持動態(tài)創(chuàng)建對象和調用方法

缺點:

  • ? 性能開銷大,比直接調用慢10-100倍
  • ? 破壞封裝性,可以訪問私有成員
  • ? 代碼復雜度高,難以理解和維護

五、常見面試問題匯總

?? IOC相關面試題

Q1: 什么是IOC?IOC有什么好處?

答案:

IOC(控制反轉)是一種設計原則,將對象創(chuàng)建和依賴關系的管理從應用程序代碼轉移到容器中。

好處:

  1. 降低耦合度:對象之間不直接依賴,而是通過接口依賴
  2. 提高可測試性:可以輕松注入Mock對象進行單元測試
  3. 提高可維護性:修改依賴關系只需修改配置,無需修改代碼
  4. 統(tǒng)一管理:所有對象的生命周期由容器統(tǒng)一管理

Q2: IOC和DI的區(qū)別是什么?

答案:

  • IOC(控制反轉):是一種設計思想,指將對象創(chuàng)建的控制權交給容器
  • DI(依賴注入):是IOC的一種實現(xiàn)方式,指容器在運行時動態(tài)地將依賴注入到對象中

關系: IOC是目標,DI是實現(xiàn)IOC的手段。

Q3: Spring IOC容器的初始化過程是怎樣的?

答案:

  1. 加載配置:讀取XML配置文件或掃描注解
  2. 解析配置:將配置信息解析為BeanDefinition對象
  3. 注冊BeanDefinition:將BeanDefinition注冊到容器中
  4. 實例化Bean:使用反射創(chuàng)建Bean實例
  5. 屬性賦值:進行依賴注入,為Bean的屬性賦值
  6. 初始化Bean:調用初始化方法(如@PostConstruct、InitializingBean)
  7. 使用Bean:Bean可以被應用程序使用
  8. 銷毀Bean:容器關閉時調用銷毀方法(如@PreDestroy、DisposableBean)

Q4: @Autowired和@Resource的區(qū)別?

答案:

對比項@Autowired@Resource
來源Spring框架Java標準(JSR-250)
裝配方式默認按類型(byType)默認按名稱(byName)
required屬性支持不支持
適用范圍字段、構造器、方法字段、方法
推薦Spring項目推薦跨框架時使用

示例:

// @Autowired:按類型注入
@Autowired
private UserService userService;
// @Resource:按名稱注入
@Resource(name = "userServiceImpl")
private UserService userService;

Q5: Spring Bean的作用域有哪些?

答案:

作用域說明使用場景
singleton單例(默認)無狀態(tài)的Bean(Service、Dao)
prototype每次獲取創(chuàng)建新實例有狀態(tài)的Bean
request每次HTTP請求創(chuàng)建Web應用(請求級數(shù)據(jù))
session每個Session創(chuàng)建Web應用(用戶會話數(shù)據(jù))
application整個ServletContext創(chuàng)建Web應用(全局數(shù)據(jù))
websocket每個WebSocket創(chuàng)建WebSocket應用

示例:

@Service
@Scope("prototype")  // 設置為原型作用域
public class UserService {
    // ...
}

?? AOP相關面試題

Q6: 什么是AOP?AOP的應用場景有哪些?

答案:

AOP(面向切面編程)是一種編程范式,用于將橫切關注點(如日志、事務、權限)從業(yè)務邏輯中分離出來。

應用場景:

  1. 日志記錄:記錄方法調用信息
  2. 事務管理:自動開啟、提交、回滾事務
  3. 權限驗證:檢查用戶是否有權限執(zhí)行操作
  4. 性能監(jiān)控:統(tǒng)計方法執(zhí)行時間
  5. 異常處理:統(tǒng)一處理異常
  6. 緩存處理:自動緩存方法返回值

Q7: AOP的實現(xiàn)原理是什么?JDK動態(tài)代理和CGLIB代理的區(qū)別?

答案:

實現(xiàn)原理: Spring AOP使用動態(tài)代理技術,在運行時為目標對象創(chuàng)建代理對象,在代理對象中織入橫切邏輯。

JDK動態(tài)代理 vs CGLIB代理:

對比項JDK動態(tài)代理CGLIB代理
原理基于接口,實現(xiàn)InvocationHandler基于繼承,生成子類
要求目標類必須實現(xiàn)接口目標類不能是final
性能略慢略快
Spring選擇實現(xiàn)了接口時默認使用未實現(xiàn)接口時使用

示例:

// JDK動態(tài)代理:目標類實現(xiàn)接口
public interface UserService {
    void addUser(String username);
}
@Service
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username) {
        System.out.println("添加用戶:" + username);
    }
}
// CGLIB代理:目標類沒有實現(xiàn)接口
@Service
public class ProductService {
    public void addProduct(String productName) {
        System.out.println("添加商品:" + productName);
    }
}

Q8: AOP的五種通知類型分別在什么時候執(zhí)行?

答案:

通知類型注解執(zhí)行時機使用場景
前置通知@Before方法執(zhí)行前參數(shù)驗證、權限檢查
后置通知@AfterReturning方法正常返回后記錄返回值、緩存更新
異常通知@AfterThrowing方法拋出異常后異常日志、告警通知
最終通知@After方法執(zhí)行后(無論是否異常)資源釋放、清理工作
環(huán)繞通知@Around包圍整個方法性能統(tǒng)計、事務管理

執(zhí)行順序(正常情況):

@Around 開始 → @Before → 目標方法 → @AfterReturning → @After → @Around 結束

Q9: 切點表達式怎么寫?

答案:

語法格式:

execution(修飾符? 返回類型 包名.類名.方法名(參數(shù)) 異常?)

常用通配符:

  • *:匹配任意字符
  • ..:匹配任意層級的包或任意個參數(shù)
  • +:匹配指定類及其子類

示例:

// 1. 匹配所有public方法
execution(public * *(..))
// 2. 匹配service包下所有類的所有方法
execution(* com.example.service.*.*(..))
// 3. 匹配service包及子包下所有類的所有方法
execution(* com.example.service..*.*(..))
// 4. 匹配UserService類的所有方法
execution(* com.example.service.UserService.*(..))
// 5. 匹配add開頭的方法
execution(* com.example.service.*Service.add*(..))
// 6. 匹配只有一個String參數(shù)的方法
execution(* com.example.service.*Service.*(String))
// 7. 匹配第一個參數(shù)為String的方法
execution(* com.example.service.*Service.*(String,..))

?? 反射相關面試題

Q10: 什么是反射?反射有什么應用?

答案:

反射是Java提供的一種能力,允許程序在運行時檢查和操作類、方法、字段等信息。

應用:

  1. Spring框架:IOC容器使用反射創(chuàng)建Bean
  2. ORM框架:Hibernate、MyBatis使用反射映射數(shù)據(jù)
  3. 動態(tài)代理:AOP使用反射實現(xiàn)
  4. 序列化:JSON庫使用反射轉換對象
  5. 單元測試:Junit使用反射執(zhí)行測試方法
  6. 注解處理:框架使用反射讀取注解

Q11: 反射的性能問題如何優(yōu)化?

答案:

性能問題原因:

  1. 反射需要查找類信息,比直接調用慢
  2. 需要進行權限檢查
  3. 涉及到對象包裝/拆包

優(yōu)化方案:

  1. 緩存反射結果:緩存Class、Method、Field對象
  2. 關閉安全檢查method.setAccessible(true)
  3. 使用MethodHandle:Java 7+提供的更高效的方式
  4. 減少反射使用:能直接調用就不用反射
  5. 使用反射工具:Apache Commons BeanUtils

示例:

// 優(yōu)化前:每次都獲取Method對象
for (int i = 0; i < 10000; i++) {
    Method method = clazz.getMethod("sayHello");
    method.invoke(user);
}
// 優(yōu)化后:緩存Method對象
Method method = clazz.getMethod("sayHello");
method.setAccessible(true);  // 關閉安全檢查
for (int i = 0; i < 10000; i++) {
    method.invoke(user);
}

Q12: 如何獲取Class對象?

答案:

有三種方式獲取Class對象:

// 方式1:通過類名.class(編譯時確定)
Class<?> clazz1 = User.class;
// 方式2:通過對象.getClass()(運行時獲?。?
User user = new User();
Class<?> clazz2 = user.getClass();
// 方式3:通過Class.forName()(最靈活)
Class<?> clazz3 = Class.forName("com.example.User");

選擇建議:

  • 編譯時已知類型:使用 類名.class
  • 已有對象實例:使用 對象.getClass()
  • 類名在配置文件中:使用 Class.forName()

?? 總結

核心要點回顧

  • IOC(控制反轉)
    • 將對象創(chuàng)建的控制權交給Spring容器
    • 通過依賴注入實現(xiàn)
    • 優(yōu)先使用構造器注入
  • AOP(面向切面編程)
    • 用于模塊化橫切關注點
    • 通過動態(tài)代理實現(xiàn)
    • 五種通知類型,環(huán)繞通知最強大
  • 反射(Reflection)
    • 運行時操作類和對象
    • Spring框架的基礎技術
    • 注意性能問題,合理使用緩存

學習建議

  1. ?? 理論學習:先理解概念,再看源碼實現(xiàn)
  2. ?? 實踐練習:動手寫代碼,加深理解
  3. ?? 源碼閱讀:閱讀Spring源碼,學習設計思想
  4. ?? 總結歸納:整理筆記,形成知識體系

?? 恭喜你! 你已經完成了Spring IOC和AOP的學習。希望這篇博客能幫助你更好地理解和使用Spring框架。繼續(xù)加油!

到此這篇關于Spring IOC 和 AOP 從入門到精通的文章就介紹到這了,更多相關Spring IOC 和 AOP 原理內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • java使用軟引用實現(xiàn)緩存機制示例

    java使用軟引用實現(xiàn)緩存機制示例

    這篇文章主要為大家介紹了java使用軟引用實現(xiàn)緩存機制示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • Spring Security入門到實戰(zhàn)應用

    Spring Security入門到實戰(zhàn)應用

    本文全面介紹了SpringSecurity的核心概念和使用方法,包括認證、授權、防護、JWT認證等,并通過實戰(zhàn)案例展示了如何在實際應用中應用SpringSecurity,感興趣的朋友跟隨小編一起看看吧
    2026-02-02
  • SpringBoot定時監(jiān)聽RocketMQ的NameServer問題及解決方案

    SpringBoot定時監(jiān)聽RocketMQ的NameServer問題及解決方案

    這篇文章主要介紹了SpringBoot定時監(jiān)聽RocketMQ的NameServer問題及解決方案,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2023-12-12
  • JAVA實現(xiàn)Token自動續(xù)期機制的示例代碼

    JAVA實現(xiàn)Token自動續(xù)期機制的示例代碼

    本文主要介紹了JAVA實現(xiàn)Token自動續(xù)期機制的示例代碼,通過動態(tài)調整會話生命周期平衡安全性與用戶體驗,解決固定有效期Token帶來的風險與不便,感興趣的可以了解一下
    2025-09-09
  • Java垃圾回收之分代收集算法詳解

    Java垃圾回收之分代收集算法詳解

    今天小編就為大家分享一篇關于Java垃圾回收之分代收集算法詳解,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-10-10
  • 簡單驗證碼生成Java版

    簡單驗證碼生成Java版

    這篇文章主要為大家詳細介紹了簡單驗證碼生成Java版,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • Java中如何將json字符串轉換成map/list

    Java中如何將json字符串轉換成map/list

    這篇文章主要介紹了Java中如何將json字符串轉換成map/list,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • java之Synchronized與Lock使用及說明

    java之Synchronized與Lock使用及說明

    Synchronized和Lock都是Java中用于實現(xiàn)線程同步的機制,但它們在實現(xiàn)方式、使用方式以及提供的特性上存在顯著的區(qū)別,Synchronized是Java語言內置的同步機制,基于監(jiān)視器鎖(monitorlock)或對象鎖實現(xiàn),Lock是Java5中引入的一個接口,提供了比synchronized更靈活的鎖操作
    2026-01-01
  • xxl-job的部署及springboot集成使用示例詳解

    xxl-job的部署及springboot集成使用示例詳解

    XXL-Job是一個分布式任務調度平臺,可進行任務調度、管理和監(jiān)控,并提供任務分片、失敗重試、動態(tài)分配等功能,這篇文章主要介紹了xxl-job的部署及springboot集成使用,需要的朋友可以參考下
    2023-06-06
  • SpringBoot常見get/post請求參數(shù)處理、參數(shù)注解校驗及參數(shù)自定義注解校驗詳解

    SpringBoot常見get/post請求參數(shù)處理、參數(shù)注解校驗及參數(shù)自定義注解校驗詳解

    這篇文章主要給大家介紹了關于SpringBoot常見get/post請求參數(shù)處理、參數(shù)注解校驗及參數(shù)自定義注解校驗的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-03-03

最新評論

绿春县| 唐山市| 当涂县| 辽阳市| 青州市| 忻城县| 石台县| 连城县| 宜川县| 靖远县| 永昌县| 邹城市| 凤山县| 合江县| 广元市| 浦东新区| 江西省| 松江区| 治多县| 平原县| 江山市| 湄潭县| 客服| 娄烦县| 永州市| 调兵山市| 晋中市| 黔西| 锡林浩特市| 昆明市| 苗栗市| 鄱阳县| 雷波县| 都匀市| 嘉义县| 阿拉善右旗| 长葛市| 东莞市| 宾阳县| 吉首市| 乐山市|