溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何理解AOP中JDK代理實現(xiàn)的原理

發(fā)布時間:2021-10-09 14:05:54 來源:億速云 閱讀:174 作者:iii 欄目:編程語言

本篇內(nèi)容主要講解“如何理解AOP中JDK代理實現(xiàn)的原理”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“如何理解AOP中JDK代理實現(xiàn)的原理”吧!

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

動態(tài)代理技術(shù)在Spring AOP中分為兩種:

???? 基于JDK原生的動態(tài)代理.

提供一種在運行時創(chuàng)建一個實現(xiàn)了一組接口的新類。由于Java是不支持實例化接口的,因此JDK會在運行期間生成一個代理類對給定的接口進行實現(xiàn),在調(diào)用該代理類接口的時候,將實現(xiàn)邏輯轉(zhuǎn)發(fā)到調(diào)用處理器中(Invocation handler)。

  • JDK進行動態(tài)代理的類必須實現(xiàn)接口

  • 代理類是java.lang.reflect.Proxy子類,類名以$Proxy開始。


???? 基于CGLIB的動態(tài)代理.

CGLIB(Code Generation Library)是基于ASM(對Java字節(jié)碼進行操作的框架)的類庫。

  • Spring AOP中,如果被代理類(targetObject)沒有實現(xiàn)接口,即無法通過JDK的動態(tài)代理生成代理類,那么就會選擇CGLIB來進行代理。

  • CGLIB動態(tài)代理的原理:創(chuàng)建一個targetObject的子類,覆蓋掉需要父類的方法,在覆蓋的方法中對功能進行增強

注意,由于是采用繼承覆蓋的方式,所以由final方法修飾的類無法使用CGLIB進行代理。


???? 前提小結(jié)

  • JDK動態(tài)代理要求被代理類實現(xiàn)接口,切面類需要實現(xiàn)InvocationHandler。

  • CGLIB采用繼承+方法覆蓋實現(xiàn)切面,重寫方法將邏輯委托給MethodInterceptor#intercept。

  • CGLIB對代理類基本沒有限制,但是需要注意被代理的類不可以被final修飾符和private修飾因為Java無法重寫final類/private的方法。


???? Spring的AOP實現(xiàn)原理

???? @EnableAspectJAutoProxy
  • @EnableAspectJAutoProxy注解是Spring AOP開啟的標(biāo)志,在啟動類標(biāo)記此注解,即啟用可加載對應(yīng)的切面類邏輯。此注解的ElementType為TYPE,表示標(biāo)記在類上。

  • 同時用 @Retention(RetentionPolicy.RUNTIME) 聲明了注解在運行時得到保留。此外最重要的是使用了 @Import(AspectJAutoProxyRegistrar.class) 來注冊AOP的。

???? 實現(xiàn)方案

@EnableAspectJAutoProxy注解正是通過@Import的方式來將 AspectJAutoProxyRegistrar類注冊成Spring的Bean,以便在容器解析切面類時派上用場。那么AspectJAutoProxyRegistrar類的作用是什么?

@Import(AspectJAutoProxyRegistrar.class)
???? proxyTargetClass

@EnableAspectJAutoProxy支持處理標(biāo)有AspectJ的@Aspect批注的組件,用戶可以主動聲明proxyTargetClass來指定Spring AOP使用哪種動態(tài)代理方式來創(chuàng)建代理類(默認使用基于實現(xiàn)接口的JDK動態(tài)代理方式)。

  • 使用CGLIB動態(tài)代理來創(chuàng)建代理類

  @Configuration
  @EnableAspectJAutoProxy(proxyTargetClass=true)
  @ComponentScan("com.libo")
  public class AppConfig {
      // ...
  }
???? exposeProxy
  • 為了解決一些由于代理引發(fā)的切面失效問題,Spring AOP在Spring 4.3.1后引入了AopContext類來將代理類的引用存儲在ThreadLocal中,通過AopContext可以快速獲取當(dāng)前類的代理類。

  • 默認為不支持,如果聲明為true,即可使用AopContext獲取代理類,同時,為了使用AspectJ,需要確保當(dāng)前jar倉庫存在aspectjweaver。

???? AspectJAutoProxyRegistrar

通過@Import注冊AspectJAutoProxyRegistrar,通常情況下,我們的啟動類本身也是一個Bean,Spring支持使用 @Import來導(dǎo)入一個沒有標(biāo)記任何Spring注解 的類來將該Java類注冊成Spring的Bean。

Registers an AnnotationAwareAspectJAutoProxyCreator against the current BeanDefinitionRegistry as appropriate based on a given @EnableAspectJAutoProxy annotation.

根據(jù)當(dāng)前BeanDefinitionRegistry在適當(dāng)?shù)奈恢米訟nnotationAwareAspectJAutoProxyCreator。

如何理解AOP中JDK代理實現(xiàn)的原理

???? ImportBeanDefinitionRegistrar
  • 用來導(dǎo)入一些特殊的BeanDefinition,Spring在處理 @Configuration 時,會去掃描是否有通過 @Import 標(biāo)簽導(dǎo)入的類,對ImportBeanDefinitionRegistrar這類接口,還會執(zhí)行其中的registerBeanDefinitions方法。

AspectJAutoProxyRegistrar:實現(xiàn)了ImportBeanDefinitionRegistrar接口,用來注冊AspectJAnnotationAutoProxyCreator,也就是支持注解驅(qū)動(同時兼容XML)解析的AspectJ自動代理創(chuàng)建器。

???? AspectJAutoProxyRegistrar源碼分析
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    
	@Override
    public void registerBeanDefinitions(
         AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
         // 向容器注冊AspectJAnnotationAutoProxyCreator
        AopConfigUtils.
		    registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
        AnnotationAttributes enableAspectJAutoProxy =
        AnnotationConfigUtils.attributesFor(importingClassMetadata,
													EnableAspectJAutoProxy.class);
        // 如果@EnableAspectJAutoProxy上存在標(biāo)簽內(nèi)容
        if (enableAspectJAutoProxy != null) {
            // proxyTargetClass為true,則強制指定AutoProxyCreator使用CGLIB進行代理
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            // 是否開啟exposeProxy特性
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }
}
  • 向容器注冊AspectJAnnotationAutoProxyCreator

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

  • 隨后解析 @EnableAspectJAutoProxy 注解上的元數(shù)據(jù)來決定是否開啟上述我們講到的proxyTargetClassexposeProxy特性.

  • 為了了解registerBeanDefinitions方法的執(zhí)行鏈路和調(diào)用時機,我們使用IDE的debug來查看調(diào)用棧分析執(zhí)行流程。

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass(configClass.getImportBeanDefinitionRegistrars())

這行代碼,從代碼的語義上我們可以大致可以猜出來,這是解析當(dāng)前配置類上是否存在通過@Import導(dǎo)入的實現(xiàn)了ImportBeanDefinitionRegistrar的類。

如何理解AOP中JDK代理實現(xiàn)的原理

最終會調(diào)用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,注冊一個AnnotationAwareAspectJAutoProxyCreator,該類屬于AOP的核心類。

???? registerBeanDefinitions

執(zhí)行AspectJAutoProxyRegistrar#registerBeanDefinitions方法。

  • AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        BeanDefinitionRegistry registry, @Nullable Object source) {
    return      registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
???? 查找切面

從上面的代碼可以看出來AnnotationAwareAspectJAutoProxyCreator這個類作為實際操作者,查看該類的繼承關(guān)系圖。(省略一些不必要的類)。

如何理解AOP中JDK代理實現(xiàn)的原理

首先看第一個接口,BeanPostProcessor,這個接口作為頂層接口,肯定不會被外部直接調(diào)用,所以大概率是底下的幾個具體實現(xiàn)類被調(diào)用,然后通過判斷是不是InstantiationAwareBeanPostProcessor接口的類型,再執(zhí)行相應(yīng)邏輯,帶著這個疑惑,來看源碼。

???? AbstractAutoProxyCreator

AbstractAutoProxyCreator通過postProcessAfterInitialization實現(xiàn)AOP功能

   // 在實例化之后進行操作容器對象
    @Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String 
		beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

	protected Object wrapIfNecessary(Object bean, String beanName, Object 
		cacheKey) {
    // beanName不為空,并且存在于targetSourcedBeans中,也就是自定義的
	// TargetSource被解析過了
    if (StringUtils.hasLength(beanName) && 
		this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    // 如果Bean為advisedBeans,也不需要被代理
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    // isInfrastructureClass和shouldSkip的作用:
    // 識別切面類,加載切面類成advisors
    // 為什么又執(zhí)行一次是因為存在循環(huán)依賴的情況下無法加載advisor
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), 
		beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // Create proxy if we have advice.
    // 返回匹配當(dāng)前Bean的所有Advice、Advisor、Interceptor
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), 
		beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 創(chuàng)建Bean對應(yīng)的代理,SingletonTargetSource用于封裝實現(xiàn)類的信息
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new 
				SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
    // 下次代理不需要重復(fù)生成了
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}
  • 判斷緩存中是否存在當(dāng)前Bean或者是當(dāng)前Bean已經(jīng)被代理過了,那么直接返回bean.

  • 嘗試再次加載advisor,避免由于循環(huán)依賴導(dǎo)致advisor加載不完整.

  • 獲取當(dāng)前bean符合的advisor數(shù)組.

  • 創(chuàng)建代理類.

    • 本文來分析getAdvicesAndAdvisorsForBean方法是如何在所有的advisors中找到匹配的advisor的.

???? AbstractAdvisorAutoProxyCreator
    @Override
    @Nullable
    protected Object[] getAdvicesAndAdvisorsForBean(
            Class<?> beanClass, String beanName, @Nullable TargetSource 
			targetSource) {
        List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        return advisors.toArray();
    }
	//這里調(diào)用了findEligibleAdvisors來尋找合適的advisors,如果返回的集合為空,那么    // 最后返回null.
   // 如果返回了advisors,將其數(shù)組化返回.

    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String 
			beanName) {
        //BeanFactory 中所有 Advisor 的實現(xiàn)
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        // 有資格的 Advisor
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, 
		beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }
	
//首先獲取之前解析過的advisors列表-candidateAdvisors,這里是所有的切面類解析成的advisors.
//在candidateAdvisors中找到當(dāng)前Bean匹配的advisor-findAdvisorsThatCanApply.
//將獲取到的eligibleAdvisors進行排序.

    protected List<Advisor> findCandidateAdvisors() {
        Assert.state(this.advisorRetrievalHelper != null, "No 
					 BeanFactoryAdvisorRetrievalHelper available");
        return this.advisorRetrievalHelper.findAdvisorBeans();
    }

    protected List<Advisor> findAdvisorsThatCanApply(
            List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
        	ProxyCreationContext.setCurrentProxiedBeanName(beanName);
			try {
				return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
			}
			finally {
				ProxyCreationContext.setCurrentProxiedBeanName(null);
			}
    }
	
	public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> 	candidateAdvisors, Class<?> clazz) {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    // 存儲最終匹配的Advisor集合
    List<Advisor> eligibleAdvisors = new ArrayList<>();
    for (Advisor candidate : candidateAdvisors) {
        // 當(dāng)前advisor對象是否實現(xiàn)了IntroductionAdvisor接口
        if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
            eligibleAdvisors.add(candidate);
        }
    }
    boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor) {
            // already processed
            continue;
        }
        // canApply->判斷當(dāng)前的advisor的pointcut表達式是否匹配當(dāng)前class
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    return eligibleAdvisors;
}
	
	//最終是調(diào)用了AopUtils.findAdvisorsThatCanApply來篩選匹配Bean的Advisors.
???? 關(guān)于createProxy

ProxyFactory 對象中有要代理的bean和這個Bean上的advisor

Bean使用哪種代理

當(dāng)Bean實現(xiàn)接口時,Spring就會用JDK的動態(tài)代理。 當(dāng)Bean沒有實現(xiàn)接口時,Spring會自動使用CGlib實現(xiàn),但是前提是項目中導(dǎo)入了CGlib的相關(guān)依賴,否則Spring只能使用JDK來代理那些沒有實現(xiàn)接口的類,這樣生成的代理類會報錯。 AopProxy有兩個實現(xiàn)類JdkDynamicAopProxy和CglibAopProxy。都是構(gòu)造 ReflectiveMethodInvocation.proceed()。

JdkDynamicAopProxy

invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();

CglibAopProxy

// CglibMethodInvocation 繼承于 ReflectiveMethodInvocation
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

ReflectiveMethodInvocation.proceed()

public Object proceed() throws Throwable {
    // 當(dāng)所有攔截器都執(zhí)行后,調(diào)用目標(biāo)類的目標(biāo)方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // 動態(tài)攔截器
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            // Dynamic matching failed.
            // Skip this interceptor and invoke the next in the chain.
            return proceed();
        }
    }
    else {
        // MethodInterceptor的實現(xiàn)類在處理完自己的邏輯后,還是會調(diào)用procee(),傳入this就是為了達到這個目的
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

Advisor

如何理解AOP中JDK代理實現(xiàn)的原理

Advice

如何理解AOP中JDK代理實現(xiàn)的原理

Joinpoint

如何理解AOP中JDK代理實現(xiàn)的原理


????AnnotationAwareAspectJAutoProxyCreator

Spring用來處理應(yīng)用上下文中被@AspectJ注解標(biāo)記的類的。繼續(xù)進入registerOrEscalateApcAsRequired方法中看看注冊流程.

org.springframework.aop.config.AopConfigUtils#registerOrEscalateApcAsRequired
private static BeanDefinition registerOrEscalateApcAsRequired(
        Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    // 當(dāng)前容器是否包含 org.springframework.aop.config.internalAutoProxyCreator
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = 
			registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        return null;
    }
	
    // 將傳入的class包裝成BeanDefinition,然后注冊到容器中,并講其order的執(zhí)行順序
	//調(diào)整為最優(yōu)。
    // 在aop中,這里會注冊AnnotationAwareAspectJAutoProxyCreator.class這個類
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", 
										  Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME,     
									beanDefinition);
    return beanDefinition;
}
  1. 首先查看當(dāng)前容器中是否包含

org.springframework.aop.config.internalAutoProxyCreator的BeanDefiniton.

  1. 如果沒有,將傳入的class(在此處傳入了AnnotationAwareAspectJAutoProxyCreator.class)包裝成RootBeanDefinition,然后注冊到容器中.

  2. 設(shè)置proxyTargetClass與exposeProxy

我們看一下如何設(shè)置proxyTargetClass即可,大體上設(shè)置proxyTargetClass與exposeProxy的邏輯都是相通的.

// 如果@EnableAspectJAutoProxy上存在標(biāo)簽內(nèi)容
if (enableAspectJAutoProxy != null) {
    // 如果proxyTargetClass為true,則強制指定AutoProxyCreator使用CGLIB進行代理
    if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
        AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
    }
    // 是否開啟exposeProxy特性
    if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
        AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
    }
}
進入AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);

AopConfigUtils#forceAutoProxyCreatorToUseClassProxying
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {  
    // 如果容器中包含 org.springframework.aop.config.internalAutoProxyCreator
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {  
        // 取出 org.springframework.aop.config.internalAutoProxyCreator的BeanDefinition
        BeanDefinition definition =     
		 registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);  
        // 設(shè)置proxyTargetClass為true
        definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
    }
}
  • 如容器中包含名org.springframework.aop.config.internalAutoProxyCreator,那么取出該BeanDefinition設(shè)置proxyTargetClass為true。

  • Spring為了兼容不同的BeanDefinition持有不同的屬性值,將它們都抽象成了MutablePropertyValues,definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE),就跟我們平時JavaBean中的set方法是一樣的.


簡單理解 @ImportImportBeanDefinitionRegistrar,下面我們通過兩個用例來理解@Import和ImportBeanDefinitionRegistrar

通過@Import導(dǎo)入類讓Spring進行管理

public class NeedImportBean {
    public void doSomething(){
        Logger.getGlobal().info("Through @Import registry to a bean ");
    }
}

在啟動類中將NeedImportBean導(dǎo)入

/**
 * @author jaymin
 * 2020/11/30 20:13
 */
@Configuration
@ComponentScan(value = "com.xjm")
@Import(NeedImportBean.class)
@EnableAspectJAutoProxy
public class ApplicationConfig {
    public static AnnotationConfigApplicationContext getApplicationContext() {
        return new AnnotationConfigApplicationContext(ApplicationConfig.class);
    }
}

//對NeedImportBean做getBean

public class BeanFactoryDemo {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext applicationContext = 
				ApplicationConfig.getApplicationContext();
               NeedImportBean needImportBean = 
			   applicationContext.getBean(NeedImportBean.class);
    }
}
???? 實現(xiàn)ImportBeanDefinitionRegistrar

NeedImportBean實現(xiàn)ImportBeanDefinitionRegistrar接口,然后驗證兩個點:

  • 是否會回調(diào)registerBeanDefinitions方法。

  • 通過getBean是否能獲取NeedImportBean

public class NeedImportBean implements ImportBeanDefinitionRegistrar{

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Logger.getGlobal().info("Through implements ImportBeanDefinitionRegistrar and @Import to callback me.");
    }

    public void doSomething(){
        Logger.getGlobal().info("Through @Import registry to a bean ");
    }

}
  • refresh中激活后置處理器ConfigurationClassPostProcessor加載@Configuration上的元數(shù)據(jù)

???? refresh

首先容器會加載refresh方法。

如何理解AOP中JDK代理實現(xiàn)的原理

  • 執(zhí)行invokeBeanFactoryPostProcessors(beanFactory);激活工廠級別的后置處理器。

  • 由于啟動類都是被 @Configuration 標(biāo)記的,Spring會使用ConfigurationClassPostProcessor來解析被 @Configuration 的類。

  • 使用ConfigurationClassBeanDefinitionReader加載配置類解析成BeanDefinition。

如何理解AOP中JDK代理實現(xiàn)的原理

到此,相信大家對“如何理解AOP中JDK代理實現(xiàn)的原理”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI