spring @EnableAspectJAutoProxy背后的那些事(spring AOP源碼賞析)


在這個注解比較流行的年代里,當我們想要使用spring 的某些功能時只需要加上一行代碼就可以了,比如:

@EnableAspectJAutoProxy開啟AOP,

@EnableTransactionManagement開啟spring事務管理,

@EnableCaching開啟spring緩存

@EnableWebMvc 開啟webMvc

.....

       對於我們使用者而言十分簡單便利,然而,其背后所做的事,卻遠遠比一個注解復雜的多了,本篇只是簡略的介紹一下@EnableAspectJAutoProxy背后所發生的那些事,了解其工作原理,才能更好的運用,並從中領略大師的智慧.

廢話不多說,先來看一下源碼:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

    /**
     * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
     * to standard Java interface-based proxies. The default is {@code false}.
     */
    boolean proxyTargetClass() default false;

    /**
     * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
     * for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
     * Off by default, i.e. no guarantees that {@code AopContext} access will work.
     * @since 4.3.1
     */
    boolean exposeProxy() default false;

}

英文注解已經很詳細了,這里簡單介紹一下兩個參數,一個是控制aop的具體實現方式,為true 的話使用cglib,為false的話使用java的Proxy,默認為false,第二個參數控制代理的暴露方式,解決內部調用不能使用代理的場景,默認為false.

 

這里核心是@Import(AspectJAutoProxyRegistrar.class);在AspectJAutoProxyRegistrar里,核心的地方是

        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

一個AOP的工具類,這個工具類的主要作用是把AnnotationAwareAspectJAutoProxyCreator這個類定義為BeanDefinition放到spring容器中,這是通過實現ImportBeanDefinitionRegistrar接口來裝載的,具體裝載過程不是本篇的重點,這里就不贅述,我們重點看AnnotationAwareAspectJAutoProxyCreator這個類.

首先看看這個類圖:

 

從類圖是可以大致了解AnnotationAwareAspectJAutoProxyCreator這個類的功能.它實現了一系列Aware的接口,在Bean裝載的時候獲取BeanFactory(Bean容器),Bean的ClassLoader,還實現了order接口,繼承了PorxyConfig,ProxyConfig中主要封裝了代理的通用處理邏輯,比如設置目標類,設置使用cglib還是java proxy等一些基礎配置.

而能夠讓這個類參與到bean初始化功能,並為bean添加代理功能的還是因為它實現了BeanPostProcessor這個接口.這個接口的postProcessAfterInitialization方法會在bean初始化結束后(賦值完成)被調用,有關spring中bean初始化的過程可以參考我的另一篇博客:spring bean的裝載過程簡略賞析

這里先看一下最頂部的抽象類:AbstractAutoProxyCreator,這個抽象類主要抽象了實現代理的邏輯:

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        Object cacheKey = getCacheKey(beanClass, beanName);

        if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
            if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }

        // Create proxy here if we have a custom TargetSource.
        // Suppresses unnecessary default instantiation of the target bean:
        // The TargetSource will handle target instances in a custom fashion.
        TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
        if (targetSource != null) {
            if (StringUtils.hasLength(beanName)) {
                this.targetSourcedBeans.add(beanName);
            }
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);//獲取切面
            Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        return null;
    }

當我們開啟了EbableAspectJAutoProxy后,每次Bean的裝配時,都會執行這段邏輯.前面主要是校驗是否需要對bean進行代理(特殊的類,和已經被代理),核心邏輯在后面幾行.getAdvicesAndAdvisorsForBean方法來獲取所有符合條件的切面,具體的實現在子類,這里是抽象方法,獲取切面后就是創建代理:

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
            @Nullable Object[] specificInterceptors, TargetSource targetSource) {

        if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
            AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
        }

        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);

        if (!proxyFactory.isProxyTargetClass()) {
            if (shouldProxyTargetClass(beanClass, beanName)) {
                proxyFactory.setProxyTargetClass(true);
            }
            else {
                evaluateProxyInterfaces(beanClass, proxyFactory);
            }
        }

        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        proxyFactory.addAdvisors(advisors);
        proxyFactory.setTargetSource(targetSource);
        customizeProxyFactory(proxyFactory);

        proxyFactory.setFrozen(this.freezeProxy);
        if (advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }

        return proxyFactory.getProxy(getProxyClassLoader());
    }

TargetSource中存放被代理的對象,這段代碼主要是為了構建ProxyFactory,將配置信息(是否使用java proxy,是否threadlocal等),目標類,切面,傳入ProxyFactory中,而在ProxyFactory中,會通過createAopProxy()方法創建代理工廠DefaultAopProxyFactory,由代理廠生成具體的代理對目標類進行代理:

 

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

 

可以看到,在這里有我們在注解中設置的參數的判斷邏輯,是創建java代理,還是cglib代理,有關cglib的講解請看cglib的使用.

我們主要看一下JdkDynamicAopProxy的實現,cglib其實差不多。

@Override
    public Object getProxy() {
        return getProxy(ClassUtils.getDefaultClassLoader());
    }

    @Override
    public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
        }
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }

findDefinedEqualsAndHashCodeMethods方法是為了查詢被代理的接口是否包括equals和hashcode方法,這回影響到下面的調用。

可以看到InvocationHandler的實現就是this。我們看一下invoke方法的實現:

    @Override
    @Nullable
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodInvocation invocation;
        Object oldProxy = null;
        boolean setProxyContext = false;

        TargetSource targetSource = this.advised.targetSource;
        Object target = null;

        try {
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                // The target does not implement the equals(Object) method itself.
                return equals(args[0]);
            }
            else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                // The target does not implement the hashCode() method itself.
                return hashCode();
            }
            else if (method.getDeclaringClass() == DecoratingProxy.class) {
                // There is only getDecoratedClass() declared -> dispatch to proxy config.
                return AopProxyUtils.ultimateTargetClass(this.advised);
            }
            else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                    method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                // Service invocations on ProxyConfig with the proxy config...
                return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
            }

            Object retVal;

            if (this.advised.exposeProxy) {
                // Make invocation available if necessary.
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }

            // Get as late as possible to minimize the time we "own" the target,
            // in case it comes from a pool.
            target = targetSource.getTarget();
            Class<?> targetClass = (target != null ? target.getClass() : null);

            // Get the interception chain for this method.
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

            // Check whether we have any advice. If we don't, we can fallback on direct
            // reflective invocation of the target, and avoid creating a MethodInvocation.
            if (chain.isEmpty()) {
                // We can skip creating a MethodInvocation: just invoke the target directly
                // Note that the final invoker must be an InvokerInterceptor so we know it does
                // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
            }
            else {
                // We need to create a method invocation...
                invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                // Proceed to the joinpoint through the interceptor chain.
                retVal = invocation.proceed();
            }

            // Massage return value if necessary.
            Class<?> returnType = method.getReturnType();
            if (retVal != null && retVal == target &&
                    returnType != Object.class && returnType.isInstance(proxy) &&
                    !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                // Special case: it returned "this" and the return type of the method
                // is type-compatible. Note that we can't help if the target sets
                // a reference to itself in another returned object.
                retVal = proxy;
            }
            else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
                throw new AopInvocationException(
                        "Null return value from advice does not match primitive return type for: " + method);
            }
            return retVal;
        }
        finally {
            if (target != null && !targetSource.isStatic()) {
                // Must have come from TargetSource.
                targetSource.releaseTarget(target);
            }
            if (setProxyContext) {
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }

紅色的代碼是構建代理鏈,因為一個方法可能有多個切點匹配上,這個時候就需要構建一個鏈式的執行結構。

    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
        MethodCacheKey cacheKey = new MethodCacheKey(method);
        List<Object> cached = this.methodCache.get(cacheKey);
        if (cached == null) {
            cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                    this, method, targetClass);
            this.methodCache.put(cacheKey, cached);
        }
        return cached;
    }

這里做了一個緩存,雖然new了一個對象最為key,但是對象的equals和hashcode方法都被重寫了,所以沒有問題,我們主要來看一下它是如何組裝這個鏈式處理結構的:

    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
            Advised config, Method method, @Nullable Class<?> targetClass) {

        // This is somewhat tricky... We have to process introductions first,
        // but we need to preserve order in the ultimate list.
        List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length);
        Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
        boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
        AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

        for (Advisor advisor : config.getAdvisors()) {
            if (advisor instanceof PointcutAdvisor) {
                // Add it conditionally.
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                    if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
                        if (mm.isRuntime()) {
                            // Creating a new object instance in the getInterceptors() method
                            // isn't a problem as we normally cache created chains.
                            for (MethodInterceptor interceptor : interceptors) {
                                interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                            }
                        }
                        else {
                            interceptorList.addAll(Arrays.asList(interceptors));
                        }
                    }
                }
            }
            else if (advisor instanceof IntroductionAdvisor) {
                IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
                if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
                    Interceptor[] interceptors = registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                }
            }
            else {
                Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }

        return interceptorList;
    }

 

 

可以看到,它會遍歷自己的所有建言,那這些advisor是從哪里來的呢:

 還記得最開始,我們說過,AbstractAutoProxyCreator中通過getAdvicesAndAdvisorsForBean方法來裝載切面,而這個是一個抽象方法,現在來看它的實現,在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();
    }

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

 

findCandidateAdvisors又是一個抽象方法,主要功能就是找到候選的切面,為什么是候選的,因為它是加載了所有的切面,有些前面並不需要,在最底層AnnotationAwareAspectJAutoProxyCreator的實現類中也有:
protected List<Advisor> findCandidateAdvisors() {
        // Add all the Spring advisors found according to superclass rules.
        List<Advisor> advisors = super.findCandidateAdvisors();
        // Build Advisors for all AspectJ aspects in the bean factory.
        if (this.aspectJAdvisorsBuilder != null) {
            advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        }
        return advisors;
    }

可以看到,通過aspectJAdvisorsBuilder來將該類關心的所有的切面裝載進來,並添加到父類的集合里面.aspectJAdvisorsBuilder里緩存了advisor的信息,拿到切面后,通過findAdvisorsThatCanApply方法來篩選合適的切面,之后對切面進行排序(因為實現了Order接口),然后返回切面的鏈表.

好了,以上就是我們在寫下@EnableAspectJAutoProxy后發生的事情,可以看到,spring在背后確實為我們做了許多事情,感謝開源的人們的努力!

 

總結:回過頭看,spring抽象了最頂層的通用方法作為接口:,Bean生命周期處理,Order,排序,Aware接口,然后需要這些功能的類去實現這些接口,而實現了這些接口的類,會在特定的時候收到通知(觀察者模式),在上面的例子中,還可以看到典型的模板模式,這也是spring中用的最多的模式之一了,對於代理類的創建采用工廠模式,將類的創建和擁有者解耦,具有高擴展性.從spring整個架構上來看,也正是由一個個接口組成的(面向接口),所以spring不僅是一個很好的工具,還是一本很好的教科書.

 

 

最后貼出spring源碼地址:https://github.com/spring-projects/spring-framework.git

 

                                                                轉載請注明出處!

 

 

 

 

 

 

BeanPostProcessor


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM