在這個注解比較流行的年代里,當我們想要使用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