<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
首先我們都知道aop的基本原理就是動態代理思想,在設計模式之代理模式中有介紹過這兩種動態代理的使用與基本原理,再次不再敘述。
這裡分析的是,在spring中是如何基於動態代理的思想實現aop的。為了方便了解接下來的原始碼分析,這裡簡單化了一個流程圖分析aop的基本實現思想。
so,基於上面的流程,一步步分析spring原始碼中的aop實現方式。
採用一個簡單的aop例子,利用基於註解設定方式的切面設定,分析一個簡單的Before AOP例子。在spring boot下執行以下簡單例子。
AOP的advisor和advice設定。
@Component @Aspect public class AopConfig { @Pointcut("execution(* com.garine.debug.testcase.model.AopObject..*(..))") public void mypoint(){ //切面定義 } @Before("mypoint()") public void doAround() throws Throwable { System.out.println("before logic"); } }
AopObject,被代理攔截物件。
@Component public class AopObject { public void aoped(){ System.out.println("logic"); } }
首先是第一步內容,對我們在AopConfig中的AOP設定內容進行解析並且儲存到BeanFactory中,這個過程就是解析儲存切面資訊。
經過一遍的程式碼跟蹤,我瞭解到註解方式的AOP設定,都離不開一個類–AnnotationAwareAspectJAutoProxyCreator,這個類繼承了BeanPostProcessor介面,我們都知道BeanPostProcessor的實現類有多個執行處理節點,其中一個執行節點就是在Bean範例化之後。也就是在這個時機AnnotationAwareAspectJAutoProxyCreator攔截bean的初始化過程,根據提前解析得到的切面資訊,對bean的方法進行嘗試適配,如果有匹配則需要進行代理建立。
這裡先分析的就是AnnotationAwareAspectJAutoProxyCreator,在bean範例化第一次查詢所有切面資訊時,就會解析儲存Aop的資訊到範例中,跟蹤以下程式碼。
AbstractApplicationContext#refresh (上下文初始化主幹方法)
AbstractApplicationContext#registerBeanPostProcessors (執行範例化並儲存所有實現BeanPostProcessor介面的類)
按照上面的邏輯,registerBeanPostProcessors 會比一般的bean範例化邏輯要早執行,因此我們接下來只需要分析AnnotationAwareAspectJAutoProxyCreator的初始化過程。
通過上圖可以知道,AnnotationAwareAspectJAutoProxyCreator是繼承了BeanfactoryAware介面,所以在範例化時,會執行setFactory方法。而所有切面資訊解析的執行者BeanFactoryAspectJAdvisorsBuilderAdapter初始化的時機也是在setFactory方法。
跟蹤程式碼如下。
AbstractAdvisorAutoProxyCreator#setBeanFactory
AnnotationAwareAspectJAutoProxyCreator#initBeanFactory
在這個方法裡面會新建一個BeanFactoryAspectJAdvisorsBuilderAdapter,這個物件會根據Beanfactory內的aop設定資訊,進行解析儲存。但是需要注意,此時雖然新建了BeanFactoryAspectJAdvisorsBuilderAdapter物件.但是此時還不會馬上解析aop設定,需要在第一次個普通bean範例化時才執行解析aop設定。解析的方法就是
BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors,會在初次執行AnnotationAwareAspectJAutoProxyCreator呼叫postProcessBeforeInitialization時開始執行。
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) { super.initBeanFactory(beanFactory); //aspectJAdvisorsBuilder#buildAspectJAdvisors就是解析設定入口 this.aspectJAdvisorsBuilder = new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory); }
上面提到繼承結構圖中,AnnotationAwareAspectJAutoProxyCreator是實現了InstantiationAwareBeanPostProcessor介面的,InstantiationAwareBeanPostProcessor介面定義的postProcessBeforeInitialization方法是一個可以對已經注入依賴屬性的bean物件範例進行編輯操作的介面,會在
AbstractAutowireCapableBeanFactory#doCreateBean
AbstractAutowireCapableBeanFactory#initializeBean(String, Object, RootBeanDefinition)
AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation
方法中執行InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation,初次初始化快取切面資訊的話就是在這個方法裡面。。
具體的呼叫鏈如上所示。這裡的postProcessBeforeInstantiation方法實際上是AnnotationAwareAspectJAutoProxyCreator的範例進行呼叫,AnnotationAwareAspectJAutoProxyCreator實現InstantiationAwareBeanPostProcessor介面。
下面進入InstantiationAwareBeanPostProcessor#postProcessBeforeInitialization方法分析程式碼。
AbstractAutoProxyCreator#postProcessBeforeInstantiation
AspectJAwareAdvisorAutoProxyCreator#shouldSkip (關鍵程式碼)
進入如下程式碼AbstractAutoProxyCreator,這個範例也就是之前一開始初始化的AnnotationAwareAspectJAutoProxyCreator範例,進入範例的shouldSkip 方法
@Override protected boolean shouldSkip(Class<?> beanClass, String beanName) { // TODO: Consider optimization by caching the list of the aspect names //預先解析快取切面資訊 List<Advisor> candidateAdvisors = findCandidateAdvisors(); for (Advisor advisor : candidateAdvisors) { if (advisor instanceof AspectJPointcutAdvisor) { if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) { return true; } } } return super.shouldSkip(beanClass, beanName); }
AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
方法findCandidateAdvisors程式碼如下,這裡是預先解析快取所有切面advisor資訊,注意這一步操作是在AbstractAutoProxyCreator#postProcessBeforeInitialization處理,也就是開頭提到的切面解析操作,解析完成就進行快取。
@Override 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. //這裡就是前面提到的BeanFactoryAspectJAdvisorsBuilder解析所有切面資訊的呼叫點 advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); return advisors; }
然後繼續在這裡先提前看一下是如何解析aop設定的。跟蹤BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
public List<Advisor> buildAspectJAdvisors() { List<String> aspectNames = null; synchronized (this) { aspectNames = this.aspectBeanNames; if (aspectNames == null) { List<Advisor> advisors = new LinkedList<Advisor>(); aspectNames = new LinkedList<String>(); //查詢出Beanfactory中所有已經註冊的BeanName String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false); for (String beanName : beanNames) { if (!isEligibleBean(beanName)) { continue; } // We must be careful not to instantiate beans eagerly as in this // case they would be cached by the Spring container but would not // have been weaved Class<?> beanType = this.beanFactory.getType(beanName); if (beanType == null) { continue; } //判斷Bean是否是切面Bean,isAspect方法判斷[標註1] if (this.advisorFactory.isAspect(beanType)) { aspectNames.add(beanName); AspectMetadata amd = new AspectMetadata(beanType, beanName); if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); //解析aop class的設定,包返回Advisor物件[標註2] List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory); if (this.beanFactory.isSingleton(beanName)) { this.advisorsCache.put(beanName, classAdvisors); } else { this.aspectFactoryCache.put(beanName, factory); } advisors.addAll(classAdvisors); } else { // Per target or per this. if (this.beanFactory.isSingleton(beanName)) { throw new IllegalArgumentException("Bean with name '" + beanName + "' is a singleton, but aspect instantiation model is not singleton"); } MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName); this.aspectFactoryCache.put(beanName, factory); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } } this.aspectBeanNames = aspectNames; return advisors; } } if (aspectNames.isEmpty()) { return Collections.emptyList(); } List<Advisor> advisors = new LinkedList<Advisor>(); for (String aspectName : aspectNames) { List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName); if (cachedAdvisors != null) { advisors.addAll(cachedAdvisors); } else { MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } return advisors; }
**[標註1]如何判斷類是否是aop切面設定類? **
通過以下程式碼。
@Override public boolean isAspect(Class<?> clazz) { return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz)); } private boolean hasAspectAnnotation(Class<?> clazz) { //包含@Aspect註解 return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null); }
[標註2]如何解析為Advisor物件?
ReflectiveAspectJAdvisorFactory#getAdvisors 遍歷所有沒被@PointCut註解標註的方法,也就是遍歷切面內容方法
ReflectiveAspectJAdvisorFactory#getAdvisor 處理所有沒被@PointCut註解標註的方法,候選切面內容方法
程式碼如下。
@Override public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); //解析判斷候選方法是否有@Before,@After,@Around等註解,如果有,就繼續執行新建Advisor物件。 AspectJExpressionPointcut expressionPointcut = getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); if (expressionPointcut == null) { return null; } //建立advisor return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }
最終迴圈解析,@Before,@After,@Around等標註的方法都會新建一個Advisor物件。新建的Advisor物件都儲存在BeanFactoryAspectJAdvisorsBuilder#advisorsCache中,當AnnotationAwareAspectJAutoProxyCreator攔截bean的建立過程時,從這裡面適配是否有切面可用。
這裡解析得到的Advisor,大概有以下資訊。下面的資訊中,並沒有對@PointCut註解做處理,pointCut屬性只得出一個"mypoint()",此時還不知道Advisor實際對應的攔截表示式。
攔截表示式還是空的,會在AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInstantiation第一次執行時解析攔截表示式。
在AbstractAutoProxyCreator#postProcessAfterInitialization執行時,找到上面AbstractAutoProxyCreator#postProcessBeforeInitialization快取的所有的切面資訊,之後是如何進行切面適配,從而決定是否需要進行代理物件的建立呢?
在呼叫AbstractAutoProxyCreator#postProcessAfterInitialization方法時,進行切面適配,並且會根據適配建立代理物件。根據以下呼叫鏈。
AbstractAutoProxyCreator#postProcessAfterInitialization
AbstractAutoProxyCreator#wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (beanName != null && this.targetSourcedBeans.contains(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // Create proxy if we have advice. //查詢匹配切面 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); //建立代理物件 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; }
AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean
AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { //從快取取出所有切面資訊 List<Advisor> candidateAdvisors = findCandidateAdvisors(); //根據advisor資訊中的表示式進行方法對class的匹配 List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }
此時如果是第一次執行適配方法findAdvisorsThatCanApply的話,candidateAdvisors中的攔截表示式還是空的,需要進行表示式獲取,也就是@Pointcut的value。spring的操作的在第一次執行findAdvisorsThatCanApply時解析獲取攔截表示式的值,獲得攔截表示式值之後就跟當前class的方法進行匹配看是否需要進行代理。
繼續往下跟蹤程式碼
AopUtils#canApply(org.springframework.aop.Advisor, java.lang.Class<?>, boolean)
AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>, boolean)
AspectJExpressionPointcut#getClassFilter
AspectJExpressionPointcut#checkReadyToMatch
private void checkReadyToMatch() { if (getExpression() == null) { throw new IllegalStateException("Must set property 'expression' before attempting to match"); } if (this.pointcutExpression == null) { this.pointcutClassLoader = (this.beanFactory instanceof ConfigurableBeanFactory ? ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() : ClassUtils.getDefaultClassLoader()); //解析得到攔截表示式,例如根據@Before的value來關聯查詢出對應的表示式 this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader); } }
最終解析完之後,advisor中的表示式資訊結構如下圖。包含在pointcut屬性中,匹配時就根據pointcutExpression迴圈進行匹配class的方法。有興趣的可以繼續偵錯看看是如何實現匹配表示式的。
## 建立代理物件
如果在上面的匹配切面過程中,發現適配的切面,那就需要進行代理物件的建立了。
我們回到上面的AbstractAutoProxyCreator#wrapIfNecessary,主要看程式碼如下。
// Create proxy if we have advice. //查詢匹配切面 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); //建立代理物件 Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; }
所以,繼續看
AbstractAutoProxyCreator#createProxy
的建立代理物件方法。設定ProxyFactory建立Proxy需要的一切資訊。
protected Object createProxy( Class<?> beanClass, String beanName, 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); for (Advisor advisor : advisors) { proxyFactory.addAdvisor(advisor); } //設定被代理物件 proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } //建立代理物件 return proxyFactory.getProxy(getProxyClassLoader()); }
下面看ProxyFactory是如何建立代理物件,繼續跟蹤proxyFactory.getProxy(getProxyClassLoader());
public Object getProxy(ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); }
createAopProxy()作用是根據class的種類判斷採用的代理方式,看如下實現
DefaultAopProxyFactory#createAopProxy
@Override 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)) { //採用jdk動態代理必須基於介面 return new JdkDynamicAopProxy(config); } //基於cglib實現代理不需要介面 return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
所以在當前偵錯的例子中,使用cglib代理。所以執行如下代理。
@Override public Object getProxy(ClassLoader classLoader) { //。。。。。。 // Configure CGLIB Enhancer... Enhancer enhancer = createEnhancer(); if (classLoader != null) { enhancer.setClassLoader(classLoader); if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { enhancer.setUseCache(false); } } enhancer.setSuperclass(proxySuperClass); enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader)); //獲取攔截回撥函數 Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types); // Generate the proxy class and create a proxy instance. //返回一個cglib代理物件 return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException ex) { //、、、、、、 } }
getCallbacks(rootClass);在這個獲取回撥函數的方法中,普通的aop採用的回撥函數是如下的方式。
// Choose an "aop" interceptor (used for AOP calls). Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
cglib 的aop回撥函數如下。
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; Class<?> targetClass = null; Object target = null; try { //這裡注入的advised就是之前建立的ProxyFactory物件 if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // May be null. Get as late as possible to minimize the time we // "own" the target, in case it comes from a pool... target = getTarget(); if (target != null) { targetClass = target.getClass(); } //根據切面資訊建立切面內容呼叫鏈 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; // Check whether we only have one InvokerInterceptor: that is, // no real advice, but just reflective invocation of the target. if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { // 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 = methodProxy.invoke(target, argsToUse); } else { // We need to create a method invocation... //建立一個方法呼叫物件,具體呼叫實現沒分析,Before邏輯大概是先呼叫切面,在反射呼叫目標方法 retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); } retVal = processReturnType(proxy, target, method, retVal); return retVal; } finally { if (target != null) { releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }
以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45