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

Spring IOC 和 AOP 從入門到精通

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

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

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

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

1.1 什么是IOC?

?? 概念解釋

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

?? 通俗理解:想象你是一個(gè)老板,以前你需要自己去找員工(new 對象),現(xiàn)在你把這個(gè)任務(wù)交給HR部門(Spring容器),你只需要告訴HR你需要什么樣的員工(聲明依賴),HR會幫你找到合適的人(注入對象)。這就是"控制反轉(zhuǎn)"——對象創(chuàng)建的控制權(quán)從你的代碼轉(zhuǎn)移到了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
 * 缺點(diǎn):
 * 1. 高耦合:UserService 直接依賴 UserDao 的具體實(shí)現(xiàn)
 * 2. 難以測試:無法輕易替換 UserDao 的實(shí)現(xiàn)
 * 3. 不靈活:如果要換一個(gè) UserDao 實(shí)現(xiàn),需要修改 UserService 代碼
 */
public class UserService {
    // 直接在類內(nèi)部創(chuàng)建依賴對象
    private UserDao userDao = new UserDaoImpl();
    public void addUser(String username) {
        // 使用 userDao 進(jìn)行數(shù)據(jù)庫操作
        userDao.save(username);
    }
}
// UserDao 接口
interface UserDao {
    void save(String username);
}
// UserDao 的具體實(shí)現(xiàn)
class UserDaoImpl implements UserDao {
    @Override
    public void save(String username) {
        System.out.println("保存用戶:" + username);
    }
}

IOC方式(松耦合):

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

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

??? IOC的實(shí)現(xiàn)機(jī)制

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

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

?? IOC容器工作流程

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

?? AOP的實(shí)現(xiàn)機(jī)制

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

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

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

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

1.3 怎么理解Spring IOC?

?? 核心理解

Spring IOC的本質(zhì)是一個(gè)對象工廠,它負(fù)責(zé):

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

?? IOC容器的兩種類型

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

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

1.4 依賴注入(DI)詳解

?? 什么是依賴注入?

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

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

?? 依賴注入的三種方式

1?? 構(gòu)造器注入(Constructor Injection)

特點(diǎn):通過構(gòu)造方法注入依賴,適合必需的、不可變的依賴。

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

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

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

/**
 * Setter注入示例
 * 優(yōu)點(diǎn):
 * 1. 靈活性高,可以在對象創(chuàng)建后修改依賴
 * 2. 可以注入可選依賴
 * 
 * 缺點(diǎn):
 * 1. 可能造成對象處于不完整狀態(tài)
 * 2. 依賴關(guān)系不夠明確
 * 
 * 適用場景:可選依賴、需要重新配置的依賴
 */
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
@Service
public class EmailService {
    private MailSender mailSender;  // 非final,可以修改
    /**
     * Setter方法注入
     * Spring會自動調(diào)用這個(gè)方法,傳入MailSender實(shí)例
     * 
     * @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)

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

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

?? 三種注入方式對比

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

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

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

2.1 什么是AOP?

?? 概念解釋

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

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

?? 核心概念

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

?? AOP概念圖解

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

?? AOP的五種通知類型

/**
 * AOP通知類型完整示例
 * 演示五種通知類型的執(zhí)行時(shí)機(jī)和用法
 */
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
/**
 * 日志切面:演示各種通知類型
 */
@Aspect  // 標(biāo)記這是一個(gè)切面類
@Component  // 將切面類注冊為Spring Bean
public class LoggingAspect {
    /**
     * 1. 前置通知(Before Advice)
     * 在目標(biāo)方法執(zhí)行之前執(zhí)行
     * 
     * 使用場景:參數(shù)驗(yàn)證、權(quán)限檢查、日志記錄
     * 
     * @param joinPoint 連接點(diǎn),包含方法信息
     */
    @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)
     * 在目標(biāo)方法正常返回后執(zhí)行(不包括異常情況)
     * 
     * 使用場景:記錄返回值、數(shù)據(jù)處理、緩存更新
     * 
     * @param joinPoint 連接點(diǎn)
     * @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)
     * 在目標(biāo)方法拋出異常后執(zhí)行
     * 
     * 使用場景:異常日志記錄、異常轉(zhuǎn)換、告警通知
     * 
     * @param joinPoint 連接點(diǎn)
     * @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)
     * 在目標(biāo)方法執(zhí)行之后執(zhí)行(無論是否拋出異常)
     * 類似于finally塊
     * 
     * 使用場景:資源釋放、清理工作
     * 
     * @param joinPoint 連接點(diǎn)
     */
    @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)
     * 包圍目標(biāo)方法,可以控制方法的執(zhí)行
     * 最強(qiáng)大的通知類型
     * 
     * 使用場景:性能統(tǒng)計(jì)、事務(wù)管理、緩存處理
     * 
     * @param proceedingJoinPoint 可執(zhí)行的連接點(diǎn)
     * @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ǔn)備執(zhí)行方法:" + methodName);
        // 記錄開始時(shí)間
        long startTime = System.currentTimeMillis();
        Object result = null;
        try {
            // 執(zhí)行目標(biāo)方法(這是關(guān)鍵?。?
            result = proceedingJoinPoint.proceed();
            // 目標(biāo)方法執(zhí)行成功
            System.out.println("方法執(zhí)行成功,返回值:" + result);
        } catch (Throwable throwable) {
            // 目標(biāo)方法執(zhí)行失敗
            System.out.println("方法執(zhí)行失?。? + throwable.getMessage());
            throw throwable;  // 重新拋出異常
        } finally {
            // 計(jì)算執(zhí)行時(shí)間
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
            System.out.println("方法執(zhí)行耗時(shí):" + executionTime + "ms");
            System.out.println("=== 環(huán)繞通知結(jié)束 ===");
        }
        // 返回結(jié)果(可以修改返回值)
        return result;
    }
}

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

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

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

?? 解決的核心問題

Spring AOP主要解決代碼重復(fù)橫切關(guān)注點(diǎn)分離的問題。

?? 橫切關(guān)注點(diǎn)示例

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

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

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

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

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

三、反射機(jī)制詳解

3.1 什么是反射?

?? 概念解釋

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

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

?? 反射的核心功能

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

?? 反射API核心類

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

?? 反射完整示例

/**
 * 反射機(jī)制完整示例
 * 演示反射的各種用法
 */
import java.lang.reflect.*;
/**
 * 用戶類(作為反射操作的目標(biāo))
 */
class User {
    // 私有字段
    private String username;
    private int age;
    // 無參構(gòu)造器
    public User() {
        System.out.println("無參構(gòu)造器被調(diào)用");
    }
    // 有參構(gòu)造器
    public User(String username, int age) {
        this.username = username;
        this.age = age;
        System.out.println("有參構(gòu)造器被調(diào)用:" + username + ", " + age);
    }
    // 公共方法
    public void sayHello() {
        System.out.println("Hello, I'm " + username);
    }
    // 私有方法
    private void secretMethod() {
        System.out.println("這是一個(gè)私有方法");
    }
    // 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("=== 反射機(jī)制演示 ===\n");
        // 1. 獲取Class對象的三種方式
        demonstrateGetClass();
        // 2. 創(chuàng)建對象
        demonstrateCreateInstance();
        // 3. 訪問和修改字段
        demonstrateFieldAccess();
        // 4. 調(diào)用方法
        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:使用無參構(gòu)造器創(chuàng)建對象
        System.out.println("方式1:使用無參構(gòu)造器");
        User user1 = (User) clazz.getDeclaredConstructor().newInstance();
        // 方式2:使用有參構(gòu)造器創(chuàng)建對象
        System.out.println("\n方式2:使用有參構(gòu)造器");
        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");
        // 設(shè)置可訪問(繞過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();
    }
    /**
     * 演示調(diào)用方法
     */
    private static void demonstrateMethodInvocation() throws Exception {
        System.out.println("=== 4. 調(diào)用方法 ===");
        User user = new User("趙六", 28);
        Class<?> clazz = user.getClass();
        // 調(diào)用公共方法
        System.out.println("調(diào)用公共方法:");
        Method sayHelloMethod = clazz.getMethod("sayHello");
        sayHelloMethod.invoke(user);
        // 調(diào)用私有方法
        System.out.println("\n調(diào)用私有方法:");
        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使用反射實(shí)現(xiàn)動態(tài)代理Spring AOP, MyBatis
注解處理讀取和處理注解JUnit, Spring
序列化/反序列化JSON與對象互轉(zhuǎn)Jackson, Gson, Fastjson
ORM框架數(shù)據(jù)庫記錄映射為對象Hibernate, MyBatis
插件系統(tǒng)動態(tài)加載和執(zhí)行插件Eclipse插件, IDEA插件
工具類通用的工具方法BeanUtils, PropertyUtils

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

優(yōu)點(diǎn):

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

缺點(diǎn):

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

五、常見面試問題匯總

?? IOC相關(guān)面試題

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

答案:

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

好處:

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

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

答案:

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

關(guān)系: IOC是目標(biāo),DI是實(shí)現(xiàn)IOC的手段。

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

答案:

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

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

答案:

對比項(xiàng)@Autowired@Resource
來源Spring框架Java標(biāo)準(zhǔn)(JSR-250)
裝配方式默認(rèn)按類型(byType)默認(rèn)按名稱(byName)
required屬性支持不支持
適用范圍字段、構(gòu)造器、方法字段、方法
推薦Spring項(xiàng)目推薦跨框架時(shí)使用

示例:

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

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

答案:

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

示例:

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

?? AOP相關(guān)面試題

Q6: 什么是AOP?AOP的應(yīng)用場景有哪些?

答案:

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

應(yīng)用場景:

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

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

答案:

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

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

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

示例:

// JDK動態(tài)代理:目標(biāo)類實(shí)現(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代理:目標(biāo)類沒有實(shí)現(xiàn)接口
@Service
public class ProductService {
    public void addProduct(String productName) {
        System.out.println("添加商品:" + productName);
    }
}

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

答案:

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

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

@Around 開始 → @Before → 目標(biāo)方法 → @AfterReturning → @After → @Around 結(jié)束

Q9: 切點(diǎn)表達(dá)式怎么寫?

答案:

語法格式:

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

常用通配符:

  • *:匹配任意字符
  • ..:匹配任意層級的包或任意個(gè)參數(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. 匹配只有一個(gè)String參數(shù)的方法
execution(* com.example.service.*Service.*(String))
// 7. 匹配第一個(gè)參數(shù)為String的方法
execution(* com.example.service.*Service.*(String,..))

?? 反射相關(guān)面試題

Q10: 什么是反射?反射有什么應(yīng)用?

答案:

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

應(yīng)用:

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

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

答案:

性能問題原因:

  1. 反射需要查找類信息,比直接調(diào)用慢
  2. 需要進(jìn)行權(quán)限檢查
  3. 涉及到對象包裝/拆包

優(yōu)化方案:

  1. 緩存反射結(jié)果:緩存Class、Method、Field對象
  2. 關(guān)閉安全檢查method.setAccessible(true)
  3. 使用MethodHandle:Java 7+提供的更高效的方式
  4. 減少反射使用:能直接調(diào)用就不用反射
  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);  // 關(guān)閉安全檢查
for (int i = 0; i < 10000; i++) {
    method.invoke(user);
}

Q12: 如何獲取Class對象?

答案:

有三種方式獲取Class對象:

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

選擇建議:

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

?? 總結(jié)

核心要點(diǎn)回顧

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

學(xué)習(xí)建議

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

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

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

相關(guān)文章

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

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

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

    Spring Security入門到實(shí)戰(zhàn)應(yīng)用

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

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

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

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

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

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

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

    簡單驗(yàn)證碼生成Java版

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

    Java中如何將json字符串轉(zhuǎn)換成map/list

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

    java之Synchronized與Lock使用及說明

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

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

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

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

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

最新評論

拜泉县| 营口市| 阿瓦提县| 青川县| 宣威市| 云南省| 定西市| 汨罗市| 稻城县| 林甸县| 安康市| 嵊泗县| 娱乐| 铜山县| 堆龙德庆县| 定边县| 通许县| 忻州市| 盐津县| 汝城县| 嫩江县| 墨江| 海晏县| 柳林县| 柳林县| 星子县| 阜平县| 大余县| 平乡县| 白河县| 金阳县| 靖远县| 张家界市| 富顺县| 济源市| 克拉玛依市| 扶绥县| 泸水县| 云龙县| 湄潭县| 威信县|