中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

如何理解AOP中JDK代理實現的原理

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

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

???? 動態代理

動態代理技術在Spring AOP中分為兩種:

???? 基于JDK原生的動態代理.

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

  • JDK進行動態代理的類必須實現接口

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


???? 基于CGLIB的動態代理.

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

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

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

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


???? 前提小結

  • JDK動態代理要求被代理類實現接口,切面類需要實現InvocationHandler。

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

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


???? Spring的AOP實現原理

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

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

???? 實現方案

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

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

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

  • 使用CGLIB動態代理來創建代理類

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

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

???? AspectJAutoProxyRegistrar

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

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

根據當前BeanDefinitionRegistry在適當的位置注冊AnnotationAwareAspectJAutoProxyCreator。

如何理解AOP中JDK代理實現的原理

???? ImportBeanDefinitionRegistrar
  • 用來導入一些特殊的BeanDefinitionSpring在處理 @Configuration 時,會去掃描是否有通過 @Import 標簽導入的類,對ImportBeanDefinitionRegistrar這類接口,還會執行其中的registerBeanDefinitions方法。

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

???? 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上存在標簽內容
        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 注解上的元數據來決定是否開啟上述我們講到的proxyTargetClassexposeProxy特性.

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

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

這行代碼,從代碼的語義上我們可以大致可以猜出來,這是解析當前配置類上是否存在通過@Import導入的實現了ImportBeanDefinitionRegistrar的類

如何理解AOP中JDK代理實現的原理

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

???? registerBeanDefinitions

執行AspectJAutoProxyRegistrar#registerBeanDefinitions方法。

  • AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

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

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

如何理解AOP中JDK代理實現的原理

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

???? AbstractAutoProxyCreator

AbstractAutoProxyCreator通過postProcessAfterInitialization實現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
    // 為什么又執行一次是因為存在循環依賴的情況下無法加載advisor
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), 
		beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

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

  • 嘗試再次加載advisor,避免由于循環依賴導致advisor加載不完整.

  • 獲取當前bean符合的advisor數組.

  • 創建代理類.

    • 本文來分析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();
    }
	//這里調用了findEligibleAdvisors來尋找合適的advisors,如果返回的集合為空,那么    // 最后返回null.
   // 如果返回了advisors,將其數組化返回.

    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String 
			beanName) {
        //BeanFactory 中所有 Advisor 的實現
        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中找到當前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) {
        // 當前advisor對象是否實現了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->判斷當前的advisor的pointcut表達式是否匹配當前class
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    return eligibleAdvisors;
}
	
	//最終是調用了AopUtils.findAdvisorsThatCanApply來篩選匹配Bean的Advisors.
???? 關于createProxy

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

Bean使用哪種代理

當Bean實現接口時,Spring就會用JDK的動態代理。 當Bean沒有實現接口時,Spring會自動使用CGlib實現,但是前提是項目中導入了CGlib的相關依賴,否則Spring只能使用JDK來代理那些沒有實現接口的類,這樣生成的代理類會報錯。 AopProxy有兩個實現類JdkDynamicAopProxy和CglibAopProxy。都是構造 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 {
    // 當所有攔截器都執行后,調用目標類的目標方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // 動態攔截器
        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的實現類在處理完自己的邏輯后,還是會調用procee(),傳入this就是為了達到這個目的
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

Advisor

如何理解AOP中JDK代理實現的原理

Advice

如何理解AOP中JDK代理實現的原理

Joinpoint

如何理解AOP中JDK代理實現的原理


????AnnotationAwareAspectJAutoProxyCreator

Spring用來處理應用上下文中被@AspectJ注解標記的類的。繼續進入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");
    // 當前容器是否包含 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的執行順序
	//調整為最優。
    // 在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. 首先查看當前容器中是否包含

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

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

  2. 設置proxyTargetClass與exposeProxy

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

// 如果@EnableAspectJAutoProxy上存在標簽內容
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);  
        // 設置proxyTargetClass為true
        definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
    }
}
  • 如容器中包含名org.springframework.aop.config.internalAutoProxyCreator,那么取出該BeanDefinition設置proxyTargetClass為true

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


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

通過@Import導入類讓Spring進行管理

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

在啟動類中將NeedImportBean導入

/**
 * @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);
    }
}
???? 實現ImportBeanDefinitionRegistrar

NeedImportBean實現ImportBeanDefinitionRegistrar接口,然后驗證兩個點:

  • 是否會回調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上的元數據

???? refresh

首先容器會加載refresh方法。

如何理解AOP中JDK代理實現的原理

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

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

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

如何理解AOP中JDK代理實現的原理

到此,相信大家對“如何理解AOP中JDK代理實現的原理”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

岚皋县| 华坪县| 安乡县| 东阳市| 陆河县| 乐陵市| 五常市| 易门县| 遂平县| 永和县| 全州县| 若羌县| 汝城县| 普兰县| 林口县| 巫山县| 耿马| 四子王旗| 大邑县| 榆中县| 宜黄县| 濮阳市| 江门市| 灵川县| 海南省| 许昌县| 武穴市| 宿州市| 江阴市| 黄浦区| 龙井市| 丰台区| 墨玉县| 汤阴县| 鹤岗市| 独山县| 安达市| 宁陕县| 镇赉县| 留坝县| 朝阳市|