AOP知识详解

今天我们继续看看aop相关的知识,前面说到了javassit,spring aop,通过该篇,让你对aop有更完整的认识。
aop再看aop,这是一种面向切面编程思想,相比面向对象编程,可以说是站在更改维度关注对象,我们知道,对象包含由属性和行为。 基于aop,我们可以把一段代码插入到对象中形成新的对象,这是织入的过程,目的是将公共的内容写入到业务代码中,通过配置或简单的编码完成整个过程。 这样一来不用修改原有的业务代码,同时又能自由完成目标代码的增强,按照代码的设计思想,确实是降低业务与功能的耦合。
大部分框架都是为我们提供切面织入目标过程的封装。
实现
通过该图可以看到aop相关的实现主要包括asm、cglib、jdk proxy、aspectj、javassit,这些实现主要都是对字节码直接操作,只不过对目标对象的增强可以发生在编译时、编译后或运行时。
关于aop我们说的比较多的就是代理,这属于设计模式的一种,但是aop真正做的不仅仅是对目标的代理,更多的是修改,像我们常用的代理工具cglib、jdk proxy,都是基于面向对象的特性,生成新的 目标对象,通过继承与代理模式来实现最终的增强效果。
在java中,大部分情况下都是对方法的增强,比如spring aop,这样可以解决几乎所有的业务问题;当然切点不局限于类方法,还可以包括字段、方法、构造函数、静态初始值等,比如aspectj,只不过需要特定的 编译器来实现。
下面我们看下剩下的几项实现aop的技术,前面说到,spring aop主要基于cglib、jdk proxy,在运行时实现目标对象的代理。但是spring中却引入了aspectj相关的依赖,但没有用到aspectj编译器
jdk proxyjdk动态代理,主要是基于目标接口,通过bytearrayoutputstream直接构建字节数组,最终生成代理接口的实现类,基于invocationhandler实现代码的扩展与增强,通过反射来调用目标代码的调用。
目标接口public interface helloservice { string hello(string name);}目标实现类@slf4jpublic class helloserviceimpl implements helloservice{ @override public string hello(string name) { log.info(+++ 执行方法:hello); return string.format(hello, %s, name); }}代理工厂public class jdkproxyfactory { public static t create(class targetclass, invocationhandler invocationhandler){ return (t) proxy.newproxyinstance(thread.currentthread().getcontextclassloader(), new class[]{targetclass}, invocationhandler); } @slf4j public static class loginvocationhandler implements invocationhandler{ private object target; public loginvocationhandler(object target) { this.target = target; } /** * * @param proxy * @param method * @param args * * @return * @throws throwable */ @override public object invoke(object proxy, method method, object[] args) throws throwable { try { log.info( >> > before); object result = method.invoke(target, args); // 执行被代理方法 log.info( >> > afterreturning : {}, result); return result; } catch (throwable e) { log.info( >> > afterthrowing : {}, e.getmessage()); throw e; } finally { log.info( >> > after); } } }}执行测试public class jdkproxytests { @test public void testjdkproxy(){ helloservice helloservice = jdkproxyfactory.create(helloservice.class, new jdkproxyfactory.loginvocationhandler(new helloserviceimpl())); helloservice.hello(jdk proxy); }}cglibcglib基于目标类来实现代理,已目标类为参考基于asm直接操作字节码,构造目标对象的子类行,基于methodinterceptor接口实现目标代码的增强,通过父类调用来执行原目标代码,因此在执行效率上会高于jdk动态代理。
添加依赖 cglib cglib 3.3.0目标类@slf4jpublic class hiservice { public string hi(string name){ log.info(+++ 执行方法:hi); return string.format(hi, %s, name); }}代理工厂public class cglibfactory{ /** * * @param targetclass * @param methodinterceptor * @return * @param */ public static t create(class targetclass, methodinterceptor methodinterceptor){ enhancer enhancer = new enhancer(); enhancer.setsuperclass(targetclass); enhancer.setcallback(methodinterceptor); return (t) enhancer.create(); } @slf4j public static class logmethodinterceptor implements methodinterceptor { /** * * @param target 目标对象 * @param method 目标方法 * @param args 参数 * @param methodproxy 代理方法,注意执行方式 methodproxy.invokesuper * @return * @throws throwable */ @override public object intercept(object target, method method, object[] args, methodproxy methodproxy) throws throwable { try { log.info( >> > before); object result = methodproxy.invokesuper(target, args); // 执行被代理方法 log.info( >> > afterreturning : {}, result); return result; } catch (throwable e) { log.info( >> > afterthrowing : {}, e.getmessage()); throw e; } finally { log.info( >> > after); } } }}执行测试public class cglibtests { /** * */ @test public void testcglib(){ hiservice hiservice = cglibfactory.create(hiservice.class, new cglibfactory.logmethodinterceptor()); hiservice.hi(cglib); }}aspectjaspectj是一个功能强大的面向切面编程框架,是对java面向对象的扩展,支持编译时、编译后、加载时为目标对象(不仅仅是类方法)织入代理。
切面织入时机:
编译期织入(compiler-time weaving):在类进行编译的时候就将相应的代码织入到元类文件的.class文件中编译后织入(post-compiler weaving):在类编译后,再将相关的代码织入到.class文件中加载时织入(load-time weaving):在jvm加载.class 文件的时候将代码织入我们可以通过aspectj编译器或者maven插件aspectj-maven-plugin来实现。
aspectj编译器下载aspectj
安装java -jar aspectj-1.9.6.jar 配置环境变量path与系统变量classpath
使用通过下面的命令可实现编译时织入的效果:
# ajc [options] [file... | @file... | -argfile file...]ajc -1.8 -sourceroots .srcmainjava -cp %class_path% -outjar main.jar通过ajc编译后并打包成main.jar,即是编译时实现了目标对象的代理,通过反编译工具可以查看到编译后的目标对象已经被修改。
aspectj使用编译时织入(compile-time weaving)
编译时织入目标对象:
public class ctwobject { public void run() { system.out.println(-- compile-time weaving --); }}aspect:
public aspect ctwaspect { pointcut pc(): execution(* com.sucl.blog.aspectj.target.ctwobject.*()); before(): pc(){ system.out.println( > > before ctw < > around before ctw < > around before ctw < > after ctw < < ); }}配置maven插件 aspectj-maven-plugin org.codehaus.mojo aspectj-maven-plugin 1.14.0 1.8 1.8 1.8 true true ignore utf-8 compile test-compile 执行测试public class aspectjctwtests { @test public void call() { ctwobject ctwobject = new ctwobject(); ctwobject.run(); }}编译后织入(post-compile weaving)
针对编译好的文件,比如jar中的class文件编写测试的目标对象,并打包成jar文件
public class pcwobject { public void run() { system.out.println(-- post-compile weaving --); }}引入上面的目标jar com.sucls.blog pcw-target ${project.version}配置maven插件 aspectj-maven-plugin org.codehaus.mojo aspectj-maven-plugin 1.11 1.8 1.8 1.8 utf-8 com.sucls.blog pcw-target compile test-compile 编译mvn clean compile执行测试public class aspectjpcwtests { @test public void call(){ pcwobject pcwobject = new pcwobject(); pcwobject.run(); }}运行时织入(load-time weaving)
配置vm参数 -javaagent:${project.basedir}/lib/aspectjweaver-1.9.7.jar或者配置maven-surefire-plugin插件
org.apache.maven.plugins maven-surefire-plugin 2.10 -javaagent:${project.basedir}/lib/aspectjweaver-1.9.7.jar true always 配置aop.xml/src/main/resources/meta-inf/aop.xml
启动测试public class aspectjltwtests { @test public void call(){ ltwobject ltwobject = new ltwobject(); ltwobject.run(); }}结束语不管是javassit,还是jdk proxy或者cglib来实现aop,都是通过对字节码的修改,只不过对字节码操作方式不一样。通过上面的例子我们可以认识到各种aop框架的使用方式。在究其原理时, 能够能够知道这些工具到底为我们做了什么。

tb-gateway网关的设备接入实例
苹果手机陌陌删除的消息怎么恢复?详细教程演示
数模混合芯片scan chain问题解析
国产电动牙刷哪个牌子好,介绍两大国产实力派品牌
基于STM32F103R8T6的数字量度继电器的设计
AOP知识详解
苹果承认iPhone15存在烧屏问题 OLED显示屏特性不可避免
士兰会议系统方案 让你的会议清晰无噪音
“裸眼3D”来袭:新卖点还是新方向
Andes导入Ansys medini来实现安全分析流程的自动化与模块化
微雪电子SOP28转DIP28测试座简介
简谈数字电路设计中的抖动
华硕家用Wi-Fi6新品RT-AX56U开售 原价899元
怎么给D寄存器输入数值 三菱plc寄存器D怎么读取
手机面板价格有望止跌回涨
奥迪“探路”EV电池梯次利用
时间数字转换器TMIS7701/7702介绍
快来认识一下,采用崭新结构的升压充电泵~
数字音频处理器的正确使用方法及步骤
矢量自适应光学技术解析