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

Spring?AOP概念及原理解析

 更新時間:2024年07月31日 08:58:08   作者:Si_wuxie  
這篇文章主要介紹了Spring?AOP概念及原理?,通過使用?Spring?AOP?實現(xiàn)日志管理,我們可以將日志記錄的邏輯從業(yè)務(wù)邏輯中分離出來,簡化了代碼的維護,需要的朋友可以參考下

Spring AOP(面向切面編程)

以下內(nèi)容由ChatGPT生成

AOP(Aspect-Oriented Programming,面向切面編程)是一種編程范式,旨在通過分離關(guān)注點來提高程序的模塊化。Spring AOP 主要用于橫切關(guān)注點(如日志記錄、安全、事務(wù)管理等)的實現(xiàn)。在 Spring 中,AOP 的主要功能是為 Bean 增強功能,如添加額外的行為。

1. 靜態(tài)代理與動態(tài)代理

靜態(tài)代理動態(tài)代理是實現(xiàn) AOP 的兩種主要方式。

靜態(tài)代理

  • 在編譯時就已經(jīng)知道代理的目標(biāo)類,代理類在代碼中顯式地定義。
  • 靜態(tài)代理的缺點是需要為每個代理的類手動編寫代理類,導(dǎo)致代碼冗余且難以維護。

動態(tài)代理

  • 動態(tài)代理是在運行時生成代理類的,Java 中有兩種實現(xiàn)動態(tài)代理的方式:JDK 動態(tài)代理和 CGLIB。
  • 動態(tài)代理的優(yōu)點是可以為任意接口生成代理,不需要手動編寫代理類。

JDK 動態(tài)代理

  • JDK 動態(tài)代理只代理實現(xiàn)了接口的類。它通過 java.lang.reflect.Proxy 類和 java.lang.reflect.InvocationHandler 接口實現(xiàn)。
  • InvocationHandler 接口中定義了 invoke 方法,當(dāng)代理對象調(diào)用方法時,會執(zhí)行該方法。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyExample {
public static void main(String[] args) {
Foo foo = new FooImpl();
Foo proxyFoo = (Foo) Proxy.newProxyInstance(
Foo.class.getClassLoader(),
new Class<?>[]{Foo.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增強
System.out.println("Before method: " + method.getName());
Object result = method.invoke(foo, args);
// 后置增強
System.out.println("After method: " + method.getName());
return result;
}
});
proxyFoo.doSomething();
}
}
interface Foo {
void doSomething();
}
class FooImpl implements Foo {
public void doSomething() {
System.out.println("Doing something...");
}
}

CGLIB 動態(tài)代理

  • CGLIB 動態(tài)代理通過生成目標(biāo)類的子類來實現(xiàn)代理,因此可以代理沒有接口的類。CGLIB 使用 ASM 字節(jié)碼操作庫來生成代理類。
  • CGLIB 的代理類重寫目標(biāo)類的方法,通過調(diào)用父類的 super 方法來實現(xiàn)對目標(biāo)方法的調(diào)用。
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxyExample {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Foo.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 前置增強
System.out.println("Before method: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
// 后置增強
System.out.println("After method: " + method.getName());
return result;
}
});
Foo fooProxy = (Foo) enhancer.create();
fooProxy.doSomething();
}
}
class Foo {
public void doSomething() {
System.out.println("Doing something...");
}
}

2. Spring AOP 實現(xiàn)原理

Spring AOP 支持 JDK 動態(tài)代理和 CGLIB 兩種代理機制。

  • JDK 動態(tài)代理:當(dāng)目標(biāo)類實現(xiàn)了一個或多個接口時,Spring 默認(rèn)使用 JDK 動態(tài)代理來為目標(biāo)類創(chuàng)建代理對象。
  • CGLIB:如果目標(biāo)類沒有實現(xiàn)任何接口,Spring 則會使用 CGLIB 來生成目標(biāo)類的代理對象。

Spring 使用 AopProxy 接口和其兩個實現(xiàn)類 JdkDynamicAopProxy 和 CglibAopProxy 來分別處理這兩種代理機制。

Bean 被包裝成 Proxy

  • Spring 容器啟動時,解析配置文件或注解,生成 Bean 定義信息。
  • 在 Bean 初始化后,Spring AOP 的 BeanPostProcessor 之一(如 AbstractAutoProxyCreator 的子類)會檢查該 Bean 是否需要 AOP 增強。
  • 如果需要增強,則會生成一個代理對象,替換掉原始的 Bean。這一過程是通過調(diào)用 getProxy() 方法來完成的。

創(chuàng)建 Proxy 對象

AopProxy 接口定義了 getProxy() 方法:

  • JdkDynamicAopProxy:通過 JDK 動態(tài)代理的 Proxy.newProxyInstance() 方法創(chuàng)建代理對象。
  • CglibAopProxy:通過 CGLIB 的 Enhancer 類創(chuàng)建代理對象。

獲取代理對象

getProxy() 方法返回代理對象。代理對象的創(chuàng)建是在調(diào)用 getProxy() 方法時動態(tài)生成的,并且在這個方法中處理了所有的 AOP 增強邏輯。

InvocationHandler 的實現(xiàn)

在 JDK 動態(tài)代理中,InvocationHandler 的 invoke() 方法包含了攔截器鏈的邏輯。CglibAopProxy 通過 Callback 和 MethodInterceptor 實現(xiàn)類似的功能。

public class MyInvocationHandler implements InvocationHandler {
private final Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置處理
System.out.println("Before method: " + method.getName());
// 調(diào)用目標(biāo)對象的方法
Object result = method.invoke(target, args);
// 后置處理
System.out.println("After method: " + method.getName());
return result;
}
}

在 invoke() 方法中:

  • 執(zhí)行前置增強邏輯。
  • 使用反射調(diào)用目標(biāo)對象的方法。
  • 執(zhí)行后置增強邏輯。

3. 攔截器鏈與方法鏈的執(zhí)行

Spring AOP 中的攔截器鏈?zhǔn)怯?nbsp;Advisor 和 MethodInterceptor 組成的。Advisor 包含切點(Pointcut)和通知(Advice),切點定義了哪些方法需要攔截,通知則定義了攔截時執(zhí)行的邏輯。

在代理對象調(diào)用方法時:

  • AopProxy 調(diào)用鏈會依次調(diào)用攔截器鏈中的攔截器。
  • 攔截器鏈通過責(zé)任鏈模式處理每一個攔截器。
  • 如果攔截器鏈中的某個攔截器決定執(zhí)行目標(biāo)方法,則會調(diào)用 MethodInvocation.proceed() 方法。
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 前置處理
System.out.println("Before method: " + invocation.getMethod().getName());
// 調(diào)用目標(biāo)對象的方法
Object result = invocation.proceed();
// 后置處理
System.out.println("After method: " + invocation.getMethod().getName());
return result;
}
}

在 invoke() 方法中,proceed() 方法用于繼續(xù)執(zhí)行下一個攔截器或目標(biāo)方法。如果沒有其他攔截器,則執(zhí)行目標(biāo)方法。

總結(jié)

Spring AOP 使用代理模式實現(xiàn)橫切關(guān)注點的管理,主要通過 JDK 動態(tài)代理和 CGLIB 動態(tài)代理實現(xiàn)。代理對象是通過 AopProxy 創(chuàng)建的,其中的 InvocationHandler 和 MethodInterceptor 負(fù)責(zé)執(zhí)行攔截器鏈的邏輯。Spring AOP 提供了強大的功能來增強 Bean 的行為,使得切面邏輯與核心業(yè)務(wù)邏輯分離,提升了代碼的模塊化和可維護性。

以日志管理為例,Spring項目中如何使用AOP?

在 Spring 項目中使用 AOP 實現(xiàn)日志管理是一種常見的實踐。通過 AOP,我們可以將日志記錄等橫切關(guān)注點與業(yè)務(wù)邏輯分離,使代碼更加清晰、模塊化。下面是一個使用 Spring AOP 實現(xiàn)日志管理的示例,包括如何定義切面、配置切面以及如何在應(yīng)用中使用它。

1. 引入依賴

首先,需要在項目中引入 Spring AOP 相關(guān)的依賴。如果使用的是 Maven,可以在 pom.xml 中添加以下依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2. 定義切面(Aspect)

切面是包含橫切邏輯的類。在日志管理的示例中,我們將創(chuàng)建一個切面類來攔截方法調(diào)用并記錄日志。

package com.example.logging;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.JoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
    // 定義切點,攔截所有com.example.service包及其子包下的所有方法
    @Pointcut("execution(* com.example.service..*(..))")
    public void serviceMethods() {}
    // 方法執(zhí)行之前調(diào)用
    @Before("serviceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        logger.info("Before method: " + joinPoint.getSignature().getName() + " - Arguments: " + joinPoint.getArgs());
    }
    // 方法正常返回之后調(diào)用
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        logger.info("After method: " + joinPoint.getSignature().getName() + " - Result: " + result);
    }
    // 方法拋出異常時調(diào)用
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "error")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
        logger.error("Exception in method: " + joinPoint.getSignature().getName() + " - Exception: " + error);
    }
}

3. 解釋切面中的注解和方法

  • @Aspect: 標(biāo)注該類為一個切面類。
  • @Component: 將該切面類作為 Spring 的 Bean 進行管理。
  • @Pointcut: 定義一個切點,execution(* com.example.service..*(..)) 表示匹配 com.example.service 包及其子包下的所有方法。
  • @Before: 表示在目標(biāo)方法執(zhí)行之前執(zhí)行 logBefore() 方法。
  • @AfterReturning: 表示在目標(biāo)方法正常返回之后執(zhí)行 logAfterReturning() 方法,其中 returning 屬性指定了返回值的變量名。
  • @AfterThrowing: 表示在目標(biāo)方法拋出異常時執(zhí)行 logAfterThrowing() 方法,其中 throwing 屬性指定了異常的變量名。

4. 配置切面掃描

確保 Spring 能夠掃描到定義的切面類??梢栽谥髋渲妙悾ㄍǔJ菃宇悾┲刑砑?nbsp;@EnableAspectJAutoProxy 注解,啟用 AOP 功能:

package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@EnableAspectJAutoProxy
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}

5. 使用日志管理的示例

假設(shè)有一個服務(wù)類 UserService,日志切面將記錄其方法調(diào)用:

package com.example.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
    public String getUserInfo(String userId) {
        // 模擬獲取用戶信息的操作
        return "User info for " + userId;
    }
    public void createUser(String userId, String name) {
        // 模擬創(chuàng)建用戶的操作
        System.out.println("User created: " + userId + ", " + name);
    }
}

在 UserService 的方法調(diào)用之前、正常返回之后,以及拋出異常時,日志切面將分別記錄相關(guān)信息。

6. 總結(jié)

通過使用 Spring AOP 實現(xiàn)日志管理,我們可以將日志記錄的邏輯從業(yè)務(wù)邏輯中分離出來,簡化了代碼的維護。Spring AOP 提供了一種強大的方式來處理橫切關(guān)注點,使得業(yè)務(wù)邏輯更為簡潔和集中。

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

相關(guān)文章

  • Java 封裝基礎(chǔ)知識

    Java 封裝基礎(chǔ)知識

    這篇文章主要介紹了Java 封裝的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • RestTemplate接口調(diào)用神器常見用法匯總

    RestTemplate接口調(diào)用神器常見用法匯總

    這篇文章主要介紹了RestTemplate接口調(diào)用神器常見用法匯總,通過案例代碼詳細(xì)介紹RestTemplate接口調(diào)用神器常見用法,需要的朋友可以參考下
    2022-07-07
  • Gateway+Swagger2配置聚合文檔方式

    Gateway+Swagger2配置聚合文檔方式

    這篇文章主要介紹了Gateway+Swagger2配置聚合文檔方式,具有很好的參考價值,希望對大家有所幫助。
    2023-03-03
  • java之向linux文件夾下寫文件無權(quán)限的問題

    java之向linux文件夾下寫文件無權(quán)限的問題

    這篇文章主要介紹了java之向linux文件夾下寫文件無權(quán)限的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • MyBatis中的mapper.xml配置教程

    MyBatis中的mapper.xml配置教程

    這篇文章主要介紹了MyBatis中的mapper.xml配置,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2024-01-01
  • Java詳細(xì)介紹單例模式的應(yīng)用

    Java詳細(xì)介紹單例模式的應(yīng)用

    單例模式(Singleton Pattern)是 Java 中最簡單的設(shè)計模式之一。這種類型的設(shè)計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式
    2022-09-09
  • Java使用Canal同步MySQL數(shù)據(jù)到Redis

    Java使用Canal同步MySQL數(shù)據(jù)到Redis

    在現(xiàn)代微服務(wù)架構(gòu)中,數(shù)據(jù)同步是一個常見的需求,特別是將?MySQL?數(shù)據(jù)實時同步到?Redis,下面我們就來看看Java如何使用Canal同步MySQL數(shù)據(jù)到Redis吧
    2024-11-11
  • Java中使用Thread類和Runnable接口實現(xiàn)多線程的區(qū)別

    Java中使用Thread類和Runnable接口實現(xiàn)多線程的區(qū)別

    這篇文章主要介紹了使用Thread類和Runnable接口實現(xiàn)多線程的區(qū)別,本文給大家介紹了兩種實現(xiàn)方式的步驟,除了以上兩種多線程實現(xiàn)方式,還可以使用 Callable 接口實現(xiàn),本文通過實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • Mybatis-Plus通過配置在控制臺打印執(zhí)行日志的實現(xiàn)

    Mybatis-Plus通過配置在控制臺打印執(zhí)行日志的實現(xiàn)

    本文主要介紹了Mybatis-Plus通過配置在控制臺打印執(zhí)行日志的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-04-04
  • Java多線程面試題(面試官常問)

    Java多線程面試題(面試官常問)

    這篇文章主要介紹了Java多線程面試題(面試官常問),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03

最新評論

崇明县| 抚松县| 黔西县| 沈丘县| 尼木县| 塘沽区| 阿尔山市| 仙居县| 郑州市| 高邑县| 南昌县| 慈溪市| 敦化市| 杭锦旗| 且末县| 金华市| 共和县| 安陆市| 浦城县| 长阳| 蓬安县| 阳新县| 新营市| 阳山县| 永清县| 普宁市| 岑溪市| 长乐市| 杭州市| 浦北县| 澄迈县| 循化| 杂多县| 阿城市| 双江| 搜索| 托克托县| 新绛县| 苏尼特右旗| 望江县| 上饶市|