前面我们看过javaassit是如何破解java应用,核心都是aop相关的知识,今天我们看下spring aop是怎么回事!
spring-aopspring 5.x版本
aop面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。aop是oop的延续,从另一视角扩展了对面向对象编程的形式。利用aop可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
spring aop与ioc作为整个spring框架最为核心的两个部分,其意义不言而喻。在spring中,我们都是面向bean的装配与管理,一个bean是一个对象,也可以是一个代理。
概念aspect(切面):aspect声明类似于java中的类声明,在aspect中会包含着一些pointcut以及相应的 advice。
jointpoint(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它joint point。
pointcut(切点):按规则匹配的jointpoint,这些jointpoint或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的advice执行的具体地方。
advice(通知):advice定义了在pointcut里面定义的程序点具体要做的操作,它通过before、after和around来区别是在每个jointpoint之前、之后还是代替执行的代码。
target(目标对象):将advice织入到目标对象.。
weaving(织入):将aspect和其他对象连接起来, 并创建增强(代理)对象的过程
下面从几个方面了解spring中如何使用aop,你会发现,所有的配置都是围绕着这些概念。spring提供了aop代理的上下文环境,而对目标对象的加强(aspect、advisor)都是由开发者自己完成。
spring-aop.xml基于aspect配置aop,一个aspect包含pointcut与advice: 基于advice配置aop,这种方式更方便advice的复用 @aspect注解基于注解@aspect定义切面来增强目标对象,与上面的xml第一种配置对应,记得使用@enableaspectjautoproxy注解开启
@slf4j@aspectpublic class logaspect { @pointcut(execution(* com.sucl.blog.springaop.service..*(..))) public void pointcut(){} /** * 方法执行前执行 * @param joinpoint * @param arg1 */ @before(value = pointcut()) public void before(joinpoint joinpoint){ log.info( >> > 执行 before); } /** * 方法出现异常时执行 * @param joinpoint */ @afterthrowing(value = pointcut(), throwing = e) public void afterthrowing(joinpoint joinpoint, exception e){ log.info( >> > 执行 afterthrowing: {}, e.getmessage()); } /** * 方法返回后执行,异常时不执行 * @param joinpoint */ @afterreturning(value = pointcut(), returning = result) public void afterreturning(joinpoint joinpoint, object result){ log.info( >> > 执行 afterreturning: {} ,result); } /** * 方法执行完执行,不管是否发生异常 * @param joinpoint */ @after(value = pointcut()) public void after(joinpoint joinpoint){ log.info( >> > 执行 after); } /** * 在after与 around 前后执行 * @param pjp * @param jp */ @around(value = pointcut()) public void around(proceedingjoinpoint pjp){ try { log.info( >> > 执行 around starting); pjp.proceed(); log.info( >> > 执行 around finished); } catch (throwable e) { log.info( >> > 执行 around 异常:{}, e.getmessage()); } }}aop代理执行顺序spring 5.2.7之后的执行顺序:
pointcut表达式(aspectj)execution:用于匹配方法执行的连接点
within:用于匹配指定类型内的方法执行
this:用于匹配当前aop代理对象类型的执行方法;注意是aop代理对象的类型匹配,这样就可能包括引入接口也类型匹配
target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配
args:用于匹配当前执行的方法传入的参数为指定类型的执行方法
@within:用于匹配所以持有指定注解类型内的方法
@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解
@args:用于匹配当前执行的方法传入的参数持有指定注解的执行
@annotation:用于匹配当前执行方法持有指定注解的方法
bean:spring aop扩展的,aspectj没有对于指示符,用于匹配特定名称的bean对象的执行方法
基于@configuration通过proxyfactorybean我们可以生成基于目标对象的代理。通过下面几行代码,加上自定义的切面实现(methodinterceptor、advice等接口的实现),就可以实现上面的before、after、around等通知切面。
@configurationpublic class proxyconfiguration { @bean public proxyfactorybean printserviceproxy() { proxyfactorybean proxyfactorybean = new proxyfactorybean(); proxyfactorybean.settarget(new printservice()); proxyfactorybean.setproxytargetclass(true); addinterceptors(proxyfactorybean); return proxyfactorybean; } /** * 定义方法拦截器:(支持通配符) * org.springframework.aop.advisor * org.aopalliance.intercept.interceptor * * methodbeforeadvice * afterreturningadvice * throwsadvice * * org.aopalliance.intercept.methodinterceptor * org.aopalliance.aop.advice * * @param proxyfactorybean */ private void addinterceptors(proxyfactorybean proxyfactorybean) { proxyfactorybean.setinterceptornames(logadvice); } @bean public logadvice logadvice() { return new logadvice(); }}@slf4jpublic class logadvice implements methodinterceptor { @override public object invoke(methodinvocation invocation) throws throwable { try { log.info( >> > before); object result = invocation.proceed(); log.info( >> > afterreturning : {}, result); return result; } catch (throwable e) { log.info( >> > afterthrowing : {}, e.getmessage()); throw e; } finally { log.info( >> > after); } }}实现原理这里说到的是上面第二种通过@aspect的形式实现aop功能,这种模式更适合业务模块中对特定模块内的方法进行业务代理。我们需要依赖注解 enableaspectjautoproxy , 下面来看看具体如何实现?
在enableaspectjautoproxy中可以看到@import(aspectjautoproxyregistrar.class),如果你之前看过之前的spring boot start你就知道,这里是基于importbeandefinitionregistrar在spring容器中注册beandefinition。可以看到aspectjautoproxyregistrar#registerbeandefinitions第一行,aopconfigutils.registeraspectjannotationautoproxycreatorifnecessary(registry), 主要是注册了beanpostprocessor(annotationawareaspectjautoproxycreator)。我们知道在spring bean生命中期中,bean执行初始化前后会执行容器中的npostprocessor,spring aop即通过annotationawareaspectjautoproxycreator这个beanpostprocessor完成bean的代理工作。其父类abstractautoproxycreator中,postprocessafterinitialization -> wrapifnecessary -> createproxy -> proxyfactory -> getproxy 到这里,bean的代理对象也就生成了, 当然省略了各种判断以及加工过程。代理方式cglib和jdk proxy
spring aop主要是通过这两个代理框架来实现代理的。一般情况下,基于接口代理时使用jdk动态代理,否则使用cglib. java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为proxy,java类继承机制不允许多重继承)。而cglib能够代理普通类java动态代理使用java原生的反射api进行操作,在生成类上比较高效;cglib使用asm框架直接对字节码进行操作,在类的执行过程中比较高效aspectj
aspectj是一个功能强大的面相切面编程框架,是对java面向对象的扩展,支持编译时、编译后、加载时为目标对象(不仅仅是类方法)织入代理。在spring aop中引入了aspectjweaver.jar,仅仅使用了其注解。下面是网上spring aop与aspectj的比对
spring aopaspectj
用纯java实现 使用java编程语言的扩展实现
无需单独的编译过程 除非设置了ltw,否则需要aspectj编译器(ajc)
仅需运行时编织 运行时编织不可用。支持编译时,后编译和加载时编织
不足–仅支持方法级编织 更强大–可以编织字段,方法,构造函数,静态初始值设定项,最终类/方法等
只能在spring容器管理的bean上实现 可以在所有领域对象上实施
仅支持方法执行切入点 支持所有切入点
代理是针对目标对象创建的,并且方面已应用于这些代理 在应用程序执行之前(运行时之前)将方面直接编织到代码中
比aspectj慢得多 更好的性能
易于学习和应用 比spring aop复杂得多
结束语spring aop是整个spring框架的核心,里面涉及到的内容不是很多,但是都比较有深度,在spring诸多模块中,比如事务、缓存、鉴权等等方面都有使用到。由于springboot的出现,xml的配置形式使用 得比较少了,但是这种配置的形式更直白地体现了aop需要的配置以及各个组件的依赖关系。而基于@aspect的形式代理业务模块中的方法更简单直观,而基于proxyfactorybean、proxyfactory的方式,在编写类似 cache的模块会更加的灵活。
维信诺发布关于获得政府补助的公告
苹果14promax和13promax哪个更值得入手
电小二户外电源80开箱评测 非常适合于户外工作及娱乐使用
图森未来与Aeva合作 部署4D激光雷达
一文详细了解OpenHarmony新图形框架
Spring AOP如何破解java应用
测试测量热度上升 大咖们的干货以及市场趋势解读
松下全新推出具备可靠IP连接的4K/IP CCU:AK-UCU700系列
安谋科技新任领导层召开员工大会 强调未来发展的“四不变”
电视显示器供应链竞争格局或将发生变化
为什么主流的光学防手震技术是「五轴」防震呢?有没有可能达到「六轴光学防手震」呢?
昕诺飞与NYPA合作推进纽约智能路灯,已超过5万套安装
KUKA机器人高级编程中的"CHANNEL"的应用
工业环网交换机在隧道监控系统中的应用
射频合成器的主要作用
“UT310三维传感微量热仪”成果发布
中国ECT行业发展现状分析
聚焦生成式AI,亚马逊云科技re:Invent推出多项新功能
以穿戴式科技改善工作效率有妙招
MAX5035B开关稳压器的汽车USB电源