当前位置: 首页 > 后端技术 > Java

AOP概念篇

时间:2023-04-01 21:09:27 Java

SpringAOP系列第一篇首先介绍AOP相关的一些概念。现实中,我们经常需要记录重要操作的流程,并将相关日志打印到日志文件中//微信公众号:CoderLipublicclassBizService01{publicvoiddealBiz(BizDtobizDto){//脱敏打印+统计上报给操作系统记录(bizDto);//业务操作}privatevoidrecord(BizDtobizDto){//.....}}当有n个这样的服务时,我们进一步的操作可能是:提取到公共场所进行记录维护//微信公众号:CoderLipublicclassBizService01{publicvoiddealBiz(BizDtobizDto){//脱敏打印+向操作系统统计报告RecordUtils.record(bizDto);//业务操作}}更进一步,我们可以使用模板方法来设计。子类继承基础服务//WeChat公众号:CoderLipublicabstractclassBaseBizService{publicvoiddealBiz(BizDtobizDto){//脱敏打印+向操作系统统计上报if(isRecord()){RecordUtils.record(bizDto);}readDealBiz(bizDto);}protectedabstractvoidreadDealBiz(BizDtobizDto);protectedbooleanisRecord(){返回真;}}这样貌似可以解决非核心业务的统一处理,但是如果我们需要做一些其他核心业务处理的时候,比如权限检查,性能监控等,以及这些非核心业务的顺序-核心业务在每个业务场景中可能不一样,是否执行这些非核心业务是不确定的,这么简单如果用模板的方法解决,会很费力。甚至可以说,这种变化的过程并不适合模板法。这时候AOP就出现了。第一个流行的JavaAOP框架是AspectJ。AspectJ使用Idea来演示一个HelloWorld程序。确保AspectJ插件已安装并启用使用Ajc编译器并配置AspectJToolsjar的路径项目pom依赖org.aspectjaspectjrt1.9.7publicclassMain{//微信公众号:CoderLivoidsayHi(){System.out.println("helloworld");}publicstaticvoidmain(String[]args){Mainmain=newMain();main.sayHi();}}//方面。微信公众号:CoderLipublicaspectMainAspect{//切入点pointcutsayHiPointcut():call(*Main.sayHi());//预先通知before():sayHiPointcut(){System.out.println("sayHiBefore");}//post-adviceafter():sayHiPointcut(){System.out.println("sayHiAfter");}}我们使用关键字aspect来创建方面。在这方面,我们使用pointcut来定义切入点。所谓切点就是切面需要应用的方法,这些方法称为目标方法。在这方面,我们还定义了两种通知,一种是目标方法执行前的通知,一种是执行后的通知。方面是切入点和建议的组合。我们看Main类的反编译内容publicstaticvoidmain(String[]args){Mainmain=newMain();主要var10000=主要;try{//预通知MainAspect.aspectOf().ajc$before$com_demo_aspectj_MainAspect$1$acd0869f();var10000.sayHi();//目标方法}catch(Throwablevar3){//发布通知MainAspect.aspectOf().ajc$after$com_demo_aspectj_MainAspect$2$acd0869f();抛出var3;}//Post-notificationMainAspect.aspectOf().ajc$after$com_demo_aspectj_MainAspect$2$acd0869f();}Post-notification在catch代码块中,保证异常发生时也能得到通知。通知类型:前置通知、后置通知、异常通知、返回后通知、环绕通知、相关概念连接点:程序执行中的一个特定位置,如调用方法之前或之后、方法抛出之后异常处理程序块的类成员访问和执行等。一个类或一段程序代码有一些具有边界属性的特定点,代码中的这些特定点就是连接点。它还可以自己嵌套其他连接点。AOP中的Joinpoint可以有多种类型:构造函数调用、字段设置和获取、方法调用、方法执行、异常处理执行、类初始化。Spring只支持方法执行类型的连接点。切入点:如果连接点相当于数据中的一条记录,那么切入点就相当于查询条件,一个切入点可以匹配多个连接点。所以pointcuts代表了一组Joinpoints,它们要么通过逻辑关系组合,要么通过通配符、正则表达式等聚集在一起,定义了对应的Advice会出现在哪里。Advice:是一段程序代码,编织到目标类的连接点中。Spring提供的通知接口都是带有位置名的,比如:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。我们通过AOP在原有的业务逻辑中加入横切关注点功能,是对原有业务逻辑的增强,可以前置,后置,返回,抛出异常时等等。其实将Advice翻译成“增强”更合理,更能准确表达其本质。由于大部分文献都叫notice,我们这里也叫notice。编织:编织是将Advice通知添加到目标类的特定连接点的过程。编译编织,动态编织。Aspect切面:Pointcut(切点)和Advice(通知)简介:简介是一种特殊的通知,它给类增加了一些属性和方法。这样,即使一个业务类没有实现某个接口,也可以通过引入函数,将接口的实现逻辑动态地添加到业务类中,使业务类成为这个接口的实现类。在织入之前,我们也看到了编译后的Main类,可以看到它其实改变了我们原来的class文件。这称为静态编织。主要原因是ajc编译器在编译时将切面类编译成类字节码,然后织入到目标类中。还有一种比较常见的编织方法——动态编织。动态编织是在运行时将要增强的代码编织到目标类中,这通常是通过动态代理完成的。比如JavaJDK的动态代理或者CGLib的动态代理。SpringAOP使用动态编织。切入点表达式切入点表达式。SpringAop只支持其中的9种,一共10种表达式执行是SpringAop自己扩展的:一般用于指定方法的执行,用的最多。within:指定某些类型的所有方法执行,也可以用来指定一个包。this:SpringAop是基于代理的,生成的bean也是代理对象。这是代理对象。当这个对象可以转换为指定类型时,就是对应的入口点,SpringAop才会生效。target:当被代理的对象可以转换为指定类型时,对应的入口点就是它,SpringAop才会生效。args:当执行的方法的参数为指定类型时生效。@target:当代理的目标对象有指定注解时生效。@args:当执行的方法参数类型有指定注解时生效。@within:类似于@target,根据官方文档和网上的说法,@within只需要在目标对象的类或者父类上指定注解,@within就会生效,而@target必须是targetobject类上有指定的注解。经笔者测试,两者只要在目标类或父类上有指定的注解即可。@annotation:当指定的注解在执行的方法上时生效。Bean:当调用的方法是指定bean的方法时,才会生效。这篇后续文章会详细介绍。