0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

AOP知识详解

科技绿洲 来源:了不起 作者:了不起 2023-09-25 11:14 次阅读

今天我们继续看看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 Proxy

JDK动态代理,主要是基于目标接口,通过ByteArrayOutputStream直接构建字节数组,最终生成代理接口的实现类,基于InvocationHandler实现代码的扩展与增强,通过反射来调用目标代码的调用。

  1. 目标接口
public interface HelloService {

    String hello(String name);

}
  1. 目标实现类
@Slf4j
public class HelloServiceImpl implements HelloService{

    @Override
    public String hello(String name) {
        log.info("+++ 执行方法:hello");
        return String.format("hello, %s", name);
    }
}
  1. 代理工厂
public class JdkProxyFactory {

    public static < T > T create(Class< T > 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");
            }
        }
    }

}
  1. 执行测试
public class JdkProxyTests {
    
    @Test
    public void testJdkProxy(){
        HelloService helloService = JdkProxyFactory.create(HelloService.class, new JdkProxyFactory.LogInvocationHandler(new HelloServiceImpl()));
        helloService.hello("JDK Proxy");
    }

}

Cglib

Cglib基于目标类来实现代理,已目标类为参考基于ASM直接操作字节码,构造目标对象的子类行,基于MethodInterceptor接口实现目标代码的增强,通过父类调用来执行原目标代码,因此在执行效率上会高于JDK动态代理。

  1. 添加依赖
< dependency >
    < groupId >cglib< /groupId >
    < artifactId >cglib< /artifactId >
    < version >3.3.0< /version >
< /dependency >
  1. 目标类
@Slf4j
public class HiService {

    public String hi(String name){
        log.info("+++ 执行方法:hi");
        return String.format("hi, %s", name);
    }
}
  1. 代理工厂
public class CglibFactory{

    /**
     *
     * @param targetClass
     * @param methodInterceptor
     * @return
     * @param < T >
     */
    public static  < T > T create(Class< T > 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");
            }
        }
    }
}
  1. 执行测试
public class CglibTests {

    /**
     *
     */
    @Test
    public void testCglib(){
        HiService hiService = CglibFactory.create(HiService.class, new CglibFactory.LogMethodInterceptor());
        hiService.hi("Cglib");
    }

}

AspectJ

AspectJ是一个功能强大的面向切面编程框架,是对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 < < ");
    }

    void around(): pc(){
        System.out.println(" > > around before CTW < < ");
        proceed();
        System.out.println(" > > around before CTW < < ");
    }

    after(): pc(){
        System.out.println(" > > after CTW < < ");
    }
}
  • 配置maven插件 aspectj-maven-plugin
< !-- 编译期织入 -- >
< plugin >
    < groupId >org.codehaus.mojo< /groupId >
    < artifactId >aspectj-maven-plugin< /artifactId >
    < version >1.14.0< /version >
    < configuration >
        < complianceLevel >1.8< /complianceLevel >
        < source >1.8< /source >
        < target >1.8< /target >
        < showWeaveInfo >true< /showWeaveInfo >
        < verbose >true< /verbose >
        < Xlint >ignore< /Xlint >
        < encoding >UTF-8< /encoding >
    < /configuration >
    < executions >
        < execution >
            < goals >
                < goal >compile< /goal >
                < goal >test-compile< /goal >
            < /goals >
        < /execution >
    < /executions >
< /plugin >
  • 执行测试
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
< dependency >
    < groupId >com.sucls.blog< /groupId >
    < artifactId >PCW-target< /artifactId >
    < version >${project.version}< /version >
< /dependency >
  • 配置maven插件 aspectj-maven-plugin
< !-- 编译后织入 -- >
< plugin >
    < groupId >org.codehaus.mojo< /groupId >
    < artifactId >aspectj-maven-plugin< /artifactId >
    < !--                < version >1.14.0< /version >-- >
    < version >1.11< /version >
    < configuration >
        < complianceLevel >1.8< /complianceLevel >
        < source >1.8< /source >
        < target >1.8< /target >
        < encoding >UTF-8< /encoding >
        < weaveDependencies >
            < weaveDependency >
                < groupId >com.sucls.blog< /groupId >
                < artifactId >PCW-target< /artifactId >
            < /weaveDependency >
        < /weaveDependencies >
    < /configuration >
    < executions >
        < execution >
            < goals >
                < goal >compile< /goal >
                < goal >test-compile< /goal >
            < /goals >
        < /execution >
    < /executions >
< /plugin >
  • 编译
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插件

< plugin >
   < groupId >org.apache.maven.plugins< /groupId >
   < artifactId >maven-surefire-plugin< /artifactId >
   < version >2.10< /version >
   < configuration >
       < argLine >
           -javaagent:${project.basedir}/lib/aspectjweaver-1.9.7.jar
       < /argLine >
       < useSystemClassLoader >true< /useSystemClassLoader >
       < forkMode >always< /forkMode >
   < /configuration >
< /plugin >
  • 配置aop.xml

/src/main/resources/META-INF/aop.xml

< aspectj >
    < aspects >
        < !-- 以@Aspect形式编写切面(aj需要对应编译器编译)-- >
        < aspect name="com.sucl.blog.aspectj.aspect.LogAspect"/ >
    < /aspects >
< /aspectj >
  • 启动测试
public class AspectJLTWTests {

    @Test
    public void call(){
        LTWObject LTWObject = new LTWObject();
        LTWObject.run();
    }
}

结束语

不管是javassit,还是jdk proxy或者cglib来实现AOP,都是通过对字节码的修改,只不过对字节码操作方式不一样。通过上面的例子我们可以认识到各种AOP框架的使用方式。在究其原理时, 能够能够知道这些工具到底为我们做了什么。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接口
    +关注

    关注

    33

    文章

    7639

    浏览量

    148485
  • 编程
    +关注

    关注

    88

    文章

    3440

    浏览量

    92388
  • 代码
    +关注

    关注

    30

    文章

    4555

    浏览量

    66766
  • AOP
    AOP
    +关注

    关注

    0

    文章

    37

    浏览量

    11043
  • JDK
    JDK
    +关注

    关注

    0

    文章

    77

    浏览量

    16489
收藏 人收藏

    评论

    相关推荐

    Spring AOP如何破解java应用

    前面我们看过javaassit是如何破解java应用,核心都是AOP相关的知识,今天我们看下Spring AOP是怎么回事! Spring-AOP spring 5.x版本
    的头像 发表于 09-25 11:16 597次阅读
    Spring <b class='flag-5'>AOP</b>如何破解java应用

    汽车结构基本知识详解(图文)

    汽车结构基本知识详解(图文)
    发表于 10-09 14:17

    详解射频和微波开关的基本知识

    详解射频和微波开关的基本知识
    发表于 05-20 06:06

    Synopsy的Host和DPHY的知识详解,错过后悔

    Synopsy的Host和DPHY的知识详解,错过后悔
    发表于 03-08 08:25

    具有AoP技术的雷达传感器

    传感器封装天线 (AoP) 技术消除了对高频基板材料的需求,并降低了成本、制造复杂性和大概30%的布板空间。TI的AoP技术利用倒装芯片封装技术将天线放置在无塑封基板上,防止因天线穿过塑封材料时产生损耗
    发表于 11-04 06:32

    电子元器件基础知识详解

    电子元器件基础知识详解
    发表于 10-08 20:13 366次下载

    个体与群体思维状态下的AOP语言

    引入群体思维状态对GOAL进行改进,建立了个体与群体思维状态下的AOP语言IG-AOP,给出其语法和操作语义。举例证明该语言的表达力比GOAL强,可以较好地满足多Agent合作求解过程的刻
    发表于 04-16 10:17 11次下载

    AOP中使用标注改进日志功能的实现

    面向方面编程(AOP)可避免横切关注点对核心代码的不良影响,但AOP 中的方法签名匹配模式难以精确表达系统中的横切点,使得在大中型系统中直接使用连接点匹配方式捕获某些横
    发表于 04-17 09:01 25次下载

    基于反射机制的AOP模型的研究_张波

    基于反射机制的AOP模型的研究_张波
    发表于 03-17 15:47 0次下载

    FreeRTOS基础知识详解pdf下载

    FreeRTOS基础知识详解
    发表于 03-29 14:36 45次下载

    机器视觉基础知识详解模板

    机器视觉基础知识详解模板下载。
    发表于 05-28 14:48 12次下载

    详解射频微波基础知识

    详解射频微波基础知识
    的头像 发表于 01-29 10:28 1833次阅读

    无功补偿原理基础知识详解

    无功补偿原理基础知识详解
    的头像 发表于 08-11 09:48 527次阅读
    无功补偿原理基础<b class='flag-5'>知识</b><b class='flag-5'>详解</b>

    太阳能光伏相关知识详解

    电子发烧友网站提供《太阳能光伏相关知识详解.pdf》资料免费下载
    发表于 10-08 09:32 0次下载
    太阳能光伏相关<b class='flag-5'>知识</b><b class='flag-5'>详解</b>

    AOP要怎么使用

    AOP(Aspect-Oriented Programming)经常会出现在面试过程中,AOP到底有没有用,要怎么使用呢。本篇来一起拨开迷雾! 1 第一个AOP示例 我们会一次将所有的通知类型都覆盖
    的头像 发表于 10-09 16:18 331次阅读
    <b class='flag-5'>AOP</b>要怎么使用