当前位置: 首页 > 科技观察

冰冰的SpringAOP面试题

时间:2023-03-18 16:40:35 科技观察

Spring之前和同学们详细聊过IOC循环依赖问题,接下来给同学们讲讲另外一个模块,就是AOP,也是一道面试题官方更愿意询问的模块点。什么是AOP?AOP通常称为面向切面编程(Aspect-orientedProgramming,简称AOP),是一种编程范式,是一种通过预编译方法和运行时动态代理实现程序功能统一维护的技术。通常用来隔离不同的业务逻辑,比如常见的事务管理、日志管理等。同时,AOP的实现方式有两种:cglib和jdk。为什么我们需要AOP?假设有几个实现方法需要做日志处理。通常情况下,我们只需要手动添加日志即可。我们都知道,在真实的业务代码中,代码行数和方法数是一个天文数字。手动添加工作负载是不现实的。本着程序员思考如何偷懒的习惯,我们应该想办法提高效率。因此,AOP诞生了。AOP说白了就是通过一定的匹配规则来匹配方法,然后加上相应的日志处理。AOP本身的实现是通过ASM字节码框架的动态生成技术实现的。程序运行时,根据需求(添加文件)动态创建字节码文件,这在前面提到的设计模式中的动态代理模式中也有提到。你可以再复习一遍。Aspect,AOP的核心概念:类似于Java中的类声明,在应用中常用于配置事务或日志管理。一般使用@Aspect注解或者定义一个aspect。JoinPoint:程序执行中的一个特定的点,比如方法执行,处理异常等。Pointcut:一个规则匹配的正则表达式,当有一个joinpoint可以匹配到cutpoint时,就是指定的notification关联随着变化点将被触发。忠告(Advice):切面中某个连接点所采取的动作。around(surroundingadvice)的通知方式也有5种:addbefore(pre-advice)after(post-advice)exception(异常通知)return(返回)Notification)Weaving:将aspect和目标对象联系起来创建的过程通知对象。AOP其实是一种编程思想,以上几点就是编程的具体实现规范。一个应用程序中可以有多个通知方法,因此在AOP中引入了责任链模式的设计模式。每个通知都可以通过这个模型顺序执行。当然也可以使用@Order注解。配置号越小,越早执行。关于责任链模型的大家也可以去我之前写的设计模式复习一下。AOP的执行过程之前跟大家讲IOC的时候跟大家讲过它的启动过程。同样的AOP也有指定的执行过程,但是需要IOC作为基础。IOC容器启动,用于存储对象进行对象实例化和初始化操作,将生成和完成的对象存储在容器中(容器中运行的一些对象,如BeanFactoryProcesser、methodInterceptore等,还有很多其他对象)来自容器中创建的Acquisition需要对象调用特定的方法才能开始调用。说了这么多理论知识,如果想知道里面具体的执行流程,还是老老实实,一步步调试,进入源码查看流程。首先需要准备配置一个切面@Aspect@ComponentpublicclassLogUtil{@Pointcut("execution(public*com.ao.bing.demo.spring.aop..*.*(..))")publicvoidpctMethod(){}@Around(value="pctMethod()")publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{Objectret=pjp.proceed();System.out.println("Aroundadvice");returnret;}@Before("pctMethod()")publicvoidbefore(){System.out.println("Beforeadvice");}@After(value="pctMethod()")publicvoidafter(){System.out.println("Afteradvice");}@AfterReturning(value="pctMethod()")publicvoidafterReturning(){System.out.println("AfterReturningadvice");}@AfterThrowing(value="pctMethod()")publicvoidafterThrowing(){System.out.println("AfterThrowingadvice");}//主要方法测试);}}这里配置一个LogUtilsectiondemo,把5个notification都写上去,还有一个main方法,测试准备工作完成后,正式开始调试代码,首先我们在aopTestDemo中设置断点,此时,我们过去的bean对象aopTestDemo已经是一个动态代理生成的对象,里面有很多CALLBACK方法属性,什么是write方法属性呢,其实跟Spring的拦截器有关,其实是一种设计模式到说白了,观察者模式就是监听对象的行为,通知功能是通过回调机制实现的。那既然是回调方法,那就先进DynamicAdvisedInterceptor方法中privatestaticclassDynamicAdvisedInterceptorimplementsMethodInterceptor,Serializable{privatefinalAdvisedSupportadvised;publicDynamicAdvisedInterceptor(AdvisedSupportadvised){this.advised=advised;}@NullablepublicObjectintercept(Objectproxy,Methodmethod,Object[]args,MethodProxymethodProxy)throwsThrowable{ObjectoldProxy=null;booleansetProxyContext=false;Objecttarget=null;TargetSourcetargetSource=this.advised.getTargetSource();Objectvar16;try{if(this.advised.exposeProxy){oldProxy=AopContext.setCurrentProxy(proxy);setProxyContext=true;}target=targetSource.getTarget();ClasstargetClass=target!=null?target.getClass():null;//从advised-key获取配置的AOP通知方法Listchain=this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);ObjectretVal;//如果没有配置通知方法,直接调用目标对象的调用方法IfNecessary(method,args);retVal=methodProxy.invoke(target,argsToUse);}else{//通过CglibMethodInvocation-Key启动advice通知retVal=(newCglibAopProxy.CglibMethodInvocation(proxy,target,method,args,targetClass,chain,methodProxy)).proceed();}retVal=CglibAopProxy.processReturnType(proxy,target,method,retVal);var16=retVal;}finally{if(target!=null&&!targetSource.isStatic()){targetSource.releaseTarget(目标);}if(setProxyContext){AopContext.setCurrentProxy(oldProxy);}}returnvar16;}这里DynamicAdvisedInterceptor中有一个List链来获取我们配置的通知方式。从上面的截图中,我们可以看到Spring先拿到了所有的通知,放在一个list集合对象中,因为此时list不为空,会通过CglibMethodInvocationpublicclassReflectiveMethodInvocationimplementsProxyMethodInvocation,Cloneable{protectedfinalObjectproxy;@NullableprotectedfinalObjecttarget;protectedfinalMethodmethod;protectedObject[]arguments=new[0];@NullableprivatefinalClasstargetClass;/***Lazilyinitializedmapofuser-specificattributesforthisinvocation.*/@NullableprivateMapuserAttributes;/***ListofMethodInterceptorandInterceptorAndDynamicMethodMatcher*thatneeddynamicchecks.*/protectedfinalListinterceptorsAndDynamicMethodMatchers;/***Indexfrom0ofthecurrentinterceptorwe'rereinvoking.*-1untilweinvoke:thenthecurrentinterceptor.*/privateintcurrentInterceptorIndex=-1;/privateintcurrentInterceptorIndex=-1;/***索引从0开始Method@Override@NullablepublicObjectproceed()throwsThrowable{//Westartwithanindexof-1andincrementearly.//index为-1的拦截器还是会被调用,依次递增。如果在整个Listchain中调用完成,就会调用target的函数。//具体实现方法在AOPUtils.invokeJoinpointUsingRefection方法中。说白了,目标方法是通过反射实现的if(this.currentInterceptorIndex==this.interceptorsAndDynamicMethodMatchers.size()-1){returninvokeJoinpoint();}//获取下一个需要执行的拦截器,顺着处理定义的interceptorOrInterceptionAdvice链tcherhere:staticpartwillalreadyhave//beenevaluatedandfoundtomatch.//动态匹配拦截器,如果匹配定义的切入点,当前adviceInterceptorAndDynamicMethodMatcherdm=(InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;ClasstargetClass=(this.targetClass!=null?this.targetClass:this.method.getDeclaringClass());if(dm.methodMatcher.matches(this.method,targetClass,this.arguments)){returndm.interceptor.invoke(this);}else{//Dynamicmatchingfailed.//Skipthisinterceptorandinvokethenextinthechain.returnproceed();}}else{//是一个拦截器,sowejustinvokeit:Thepointcutwillhave//beenevaluatedstaticallybeforethisobjectwasconstructed.//普通拦截器,直接调用拦截器,使用当前this(CglibMethodInvocation)作为参数,保证调用链在当前实例执行return((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);}}//省略其他方法}这里需要注意的是cu的默认值上面源码中rrentInterceptorIndex定义为-1,相当于判断当前通知链是否执行完毕,则直接通过反射调用目标方法,否则继续执行((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this)interceptorOrInterceptionAdvice这个方法是关键点,即具体的调用拦截器去执行具体的方法publicfinalclassExposeInvocationInterceptorimplementsMethodInterceptor,PriorityOrdered,Serializable{@OverridepublicObjectinvoke(MethodInvocationmi)throwsThrowable{MethodInvocationoldInvocation=invocation.get();invocation.set(mi);try{//Executereturnmi.proceed();}finally{invocation.set(oldInvocation);}}}不知道你有没有发现问题,但是在上面的截图中,有是有6种通知方法,但是在配置的util中只配置了5种方法。第一个是ExposeInvocationInterceptor方法。那这是什么?其实这是Spring引入的另一种设计模式。责任链设计模式介绍这个设计模式的原因是配置了那么多的通知方法,spring怎么知道执行哪个advice呢?所以使用ExposeInvocationInterceptor(暴露调用者的拦截器)作为第一个通知方法,保证所有的通知方法都是按这个内联方法执行的。至此,SpringAOP的notification-connected结构调用流程开始,循环调用开始反复。直到整个链表链执行完毕,还有一些其他的逻辑需要细化。比如整条链有没有顺序执行?还是按照代码写的顺序执行?org.spring框架。aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#sortAdvisors会在获取到整个责任链后进行一次排序执行。这种排序默认使用拓扑排序的方式。可以详细查看sortAdvisors方法,执行每一个通知方法,都会有对应的切面通知方法org.springframework.aop.aspectj.AspectJAfterAdvice.springframework.aop.aopsringsAdviceAdviceArspectJorgaspectj.AspectJMethodBeforeAdvice我们配置的5个通知分别对应上面5个处理逻辑,所以通知调用环节可以理解为整个责任链的图结构:以上就是整个执行过程Spring通知,总结一下Spring中的五种通知,首先是通过Spring容器的启动过程来获取具体的通知。在调用对象时,通过动态代理ASM技术,将所有需要执行的通知放在一个链式对象的集合中。为了保证整条链的完整性,默认情况下调用会先调用ExposeInvocationInterceptor触发整条链执行。每条advice执行完后,会再次返回super的proceed方法执行下一条advice。当通知执行失败时,会有相应的切面通知方法。所有advice执行完毕后,通过反射调用目标方法。您可以自己调试整个过程。不是特别麻烦!!!AOP事务功能AOP处理常见的配置方面来处理日志等业务,也有大家比较熟悉的东西。那是生意。其实AOP事务也是通过advice的配置来执行的,notification将advice拆解来实现事务的功能。在查看源码的时候,能够给我们带来很多思考。在Spring中,我们可以使用@Transactional注解来实现事务功能。看过源码的同学一定看过TransactionAspectSupport类publicabstractclassTransactionAspectSupportimplementsBeanFactoryAware,InitializingBean{//省略其他代码。...@NullableprotectedObjectinvokeWithinTransaction(Methodmethod,@NullableClasstargetClass,finalInvocationCallbackinvocation)throwsThrowable{//如果transactionattribute为null,则方法为非transactional.TransactionAttributeSourcetas=getTransactionAttributeSource();finalTransactionAttributetxAttr=(tas!=null?tas.getTransactionAttribute(method,targetClass):null);finalPlatformTransactionManagertm=determineTransactionManager(txAttr);finalStringjoinpointIdentification=methodIdentification(method,targetClass,txAttr);if(txAttr==null||!(tminstanceofCallbackPreferringPlatformTransactionManager)){//StandardtransactiondemarcationwithgetTransactionandcommit/rollbackcalls.TransactionInfotxInfo=createTransactionIfNecessary(tm,txAttr,joinpointIdentification);ObjectretVal=null;try{//Thisisanaroundadvice:Invokethenextinterceptorinthechain.//Thiswillnormallyresultinatargetobjectbeinginvoked.retVal=invocation.proceedWithInvocation();}catch(Throwableex){//targetinvocationexception//异常回滚焦点completeTransactionAfterThrowing(txInfo,ex);throwex;}finally{cleanupTransactionInfo(txInfo);}//成功提交焦点commitTransactionAfterReturning(txInfo);returnretVal;}//省略其他代码。..因此,SpringAOP的语句事务也是通过通知来实现的。AOP的事务整体上还是比较简单的。说白了,就是通过advice的重组来完成交易功能。当然也是Spring的强项,扩展性真的很高。综上,SpringAop刚刚和学妹聊完。需要了解的是advice通知的整个过程,但是这个过程需要大家不断的看这个框架的源码。看源码的过程可以给我们很多思考,怎样才能写出高质量的代码?为了加强理解,还有两个比较常见的面试题,关于advice的notificationexecutionprocess?如果看完整个过程还是不明白,我觉得可以自己debug加深理解。我也在文中做了总结。但是如果真的自己去了解,就不会被面试官问到AOP中的Transactional事务是怎么实现的了?如果你了解通知的调用过程,这个问题就很容易回答了。【编辑推荐】HarmonyOS官方战略合作共建——HarmonyOS技术社区抄袭不翻车:可承受千万级流量的大规模分布式系统架构设计五大开源数字化转型的七大热点趋势与三渐变游戏化工具2021冷潮Windows11新预览版22449推送:开机动画变了怎么办?游戏玩家大规模退居Windows7:Windows10暴跌

最新推荐
猜你喜欢