0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

AOP知識(shí)詳解

科技綠洲 ? 來(lái)源:了不起 ? 作者:了不起 ? 2023-09-25 11:14 ? 次閱讀

今天我們繼續(xù)看看AOP相關(guān)的知識(shí),前面說(shuō)到了Javassit,Spring AOP,通過(guò)該篇,讓你對(duì)AOP有更完整的認(rèn)識(shí)。

AOP

再看AOP,這是一種面向切面編程思想,相比面向?qū)ο缶幊?,可以說(shuō)是站在更改維度關(guān)注對(duì)象,我們知道,對(duì)象包含由屬性和行為。 基于AOP,我們可以把一段代碼插入到對(duì)象中形成新的對(duì)象,這是織入的過(guò)程,目的是將公共的內(nèi)容寫(xiě)入到業(yè)務(wù)代碼中,通過(guò)配置或簡(jiǎn)單的編碼完成整個(gè)過(guò)程。 這樣一來(lái)不用修改原有的業(yè)務(wù)代碼,同時(shí)又能自由完成目標(biāo)代碼的增強(qiáng),按照代碼的設(shè)計(jì)思想,確實(shí)是降低業(yè)務(wù)與功能的耦合。

大部分框架都是為我們提供切面織入目標(biāo)過(guò)程的封裝。

實(shí)現(xiàn)

圖片

通過(guò)該圖可以看到AOP相關(guān)的實(shí)現(xiàn)主要包括ASM、Cglib、JDK Proxy、AspectJ、Javassit,這些實(shí)現(xiàn)主要都是對(duì)字節(jié)碼直接操作,只不過(guò)對(duì)目標(biāo)對(duì)象的增強(qiáng)可以發(fā)生在編譯時(shí)、編譯后或運(yùn)行時(shí)。

關(guān)于AOP我們說(shuō)的比較多的就是代理,這屬于設(shè)計(jì)模式的一種,但是AOP真正做的不僅僅是對(duì)目標(biāo)的代理,更多的是修改,像我們常用的代理工具Cglib、JDK Proxy,都是基于面向?qū)ο蟮奶匦裕尚碌?目標(biāo)對(duì)象,通過(guò)繼承與代理模式來(lái)實(shí)現(xiàn)最終的增強(qiáng)效果。

在Java中,大部分情況下都是對(duì)方法的增強(qiáng),比如Spring AOP,這樣可以解決幾乎所有的業(yè)務(wù)問(wèn)題;當(dāng)然切點(diǎn)不局限于類方法,還可以包括字段、方法、構(gòu)造函數(shù)、靜態(tài)初始值等,比如AspectJ,只不過(guò)需要特定的 編譯器來(lái)實(shí)現(xiàn)。


下面我們看下剩下的幾項(xiàng)實(shí)現(xiàn)AOP的技術(shù),前面說(shuō)到,Spring AOP主要基于Cglib、JDK Proxy,在運(yùn)行時(shí)實(shí)現(xiàn)目標(biāo)對(duì)象的代理。但是Spring中卻引入了aspectj相關(guān)的依賴,但沒(méi)有用到AspectJ編譯器

JDK Proxy

JDK動(dòng)態(tài)代理,主要是基于目標(biāo)接口,通過(guò)ByteArrayOutputStream直接構(gòu)建字節(jié)數(shù)組,最終生成代理接口的實(shí)現(xiàn)類,基于InvocationHandler實(shí)現(xiàn)代碼的擴(kuò)展與增強(qiáng),通過(guò)反射來(lái)調(diào)用目標(biāo)代碼的調(diào)用。

  1. 目標(biāo)接口
public interface HelloService {

    String hello(String name);

}
  1. 目標(biāo)實(shí)現(xiàn)類
@Slf4j
public class HelloServiceImpl implements HelloService{

    @Override
    public String hello(String name) {
        log.info("+++ 執(zhí)行方法: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); // 執(zhí)行被代理方法
                log.info(" >> > afterReturning : {}", result);
                return result;
            } catch (Throwable e) {
                log.info(" >> > afterThrowing : {}", e.getMessage());
                throw e;
            } finally {
                log.info(" >> > after");
            }
        }
    }

}
  1. 執(zhí)行測(cè)試
public class JdkProxyTests {
    
    @Test
    public void testJdkProxy(){
        HelloService helloService = JdkProxyFactory.create(HelloService.class, new JdkProxyFactory.LogInvocationHandler(new HelloServiceImpl()));
        helloService.hello("JDK Proxy");
    }

}

Cglib

Cglib基于目標(biāo)類來(lái)實(shí)現(xiàn)代理,已目標(biāo)類為參考基于ASM直接操作字節(jié)碼,構(gòu)造目標(biāo)對(duì)象的子類行,基于MethodInterceptor接口實(shí)現(xiàn)目標(biāo)代碼的增強(qiáng),通過(guò)父類調(diào)用來(lái)執(zhí)行原目標(biāo)代碼,因此在執(zhí)行效率上會(huì)高于JDK動(dòng)態(tài)代理。

  1. 添加依賴
< dependency >
    < groupId >cglib< /groupId >
    < artifactId >cglib< /artifactId >
    < version >3.3.0< /version >
< /dependency >
  1. 目標(biāo)類
@Slf4j
public class HiService {

    public String hi(String name){
        log.info("+++ 執(zhí)行方法: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 目標(biāo)對(duì)象
         * @param method 目標(biāo)方法
         * @param args 參數(shù)
         * @param methodProxy 代理方法,注意執(zhí)行方式  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); // 執(zhí)行被代理方法
                log.info(" >> > afterReturning : {}", result);
                return result;
            } catch (Throwable e) {
                log.info(" >> > afterThrowing : {}", e.getMessage());
                throw e;
            } finally {
                log.info(" >> > after");
            }
        }
    }
}
  1. 執(zhí)行測(cè)試
public class CglibTests {

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

}

AspectJ

AspectJ是一個(gè)功能強(qiáng)大的面向切面編程框架,是對(duì)Java面向?qū)ο蟮臄U(kuò)展,支持編譯時(shí)、編譯后、加載時(shí)為目標(biāo)對(duì)象(不僅僅是類方法)織入代理。

切面織入時(shí)機(jī):

  • 編譯期織入(compiler-time weaving):在類進(jìn)行編譯的時(shí)候就將相應(yīng)的代碼織入到元類文件的.class文件中
  • 編譯后織入(post-compiler weaving):在類編譯后,再將相關(guān)的代碼織入到.class文件中
  • 加載時(shí)織入(load-time weaving):在JVM加載.class 文件的時(shí)候?qū)⒋a織入

我們可以通過(guò)AspectJ編譯器或者maven插件aspectj-maven-plugin來(lái)實(shí)現(xiàn)。

AspectJ編譯器

aspectj

  • 安裝

java -jar aspectj-1.9.6.jar 配置環(huán)境變量PATH與系統(tǒng)變量CLASSPATH

  • 使用

通過(guò)下面的命令可實(shí)現(xiàn)編譯時(shí)織入的效果:

#  ajc [Options] [file... | @file... | -argfile file...]
ajc -1.8 -sourceroots .srcmainjava -cp %CLASS_PATH% -outjar main.jar

通過(guò)ajc編譯后并打包成main.jar,即是編譯時(shí)實(shí)現(xiàn)了目標(biāo)對(duì)象的代理,通過(guò)反編譯工具可以查看到編譯后的目標(biāo)對(duì)象已經(jīng)被修改。

AspectJ使用

編譯時(shí)織入(Compile-Time Weaving)

  • 編譯時(shí)織入

目標(biāo)對(duì)象:

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 >
  • 執(zhí)行測(cè)試
public class AspectJCTWTests {

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

編譯后織入(Post-Compile Weaving)

  • 針對(duì)編譯好的文件,比如jar中的class文件

編寫(xiě)測(cè)試的目標(biāo)對(duì)象,并打包成jar文件

public class PCWObject {

    public void run() {
        System.out.println("-- Post-Compile Weaving --");
    }

}
  • 引入上面的目標(biāo)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
  • 執(zhí)行測(cè)試
public class AspectJPCWTests {
    
    @Test
    public void call(){
        PCWObject pcwObject = new PCWObject();
        pcwObject.run();
    }
}

運(yùn)行時(shí)織入(Load-Time Weaving)

  • 配置VM參數(shù)
    -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形式編寫(xiě)切面(aj需要對(duì)應(yīng)編譯器編譯)-- >
        < aspect name="com.sucl.blog.aspectj.aspect.LogAspect"/ >
    < /aspects >
< /aspectj >
  • 啟動(dòng)測(cè)試
public class AspectJLTWTests {

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

結(jié)束語(yǔ)

不管是javassit,還是jdk proxy或者cglib來(lái)實(shí)現(xiàn)AOP,都是通過(guò)對(duì)字節(jié)碼的修改,只不過(guò)對(duì)字節(jié)碼操作方式不一樣。通過(guò)上面的例子我們可以認(rèn)識(shí)到各種AOP框架的使用方式。在究其原理時(shí), 能夠能夠知道這些工具到底為我們做了什么。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8354

    瀏覽量

    150509
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3541

    瀏覽量

    93462
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4694

    瀏覽量

    68075
  • AOP
    AOP
    +關(guān)注

    關(guān)注

    0

    文章

    40

    瀏覽量

    11083
  • JDK
    JDK
    +關(guān)注

    關(guān)注

    0

    文章

    80

    瀏覽量

    16563
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Spring AOP如何破解java應(yīng)用

    前面我們看過(guò)javaassit是如何破解java應(yīng)用,核心都是AOP相關(guān)的知識(shí),今天我們看下Spring AOP是怎么回事! Spring-AOP spring 5.x版本
    的頭像 發(fā)表于 09-25 11:16 ?818次閱讀
    Spring <b class='flag-5'>AOP</b>如何破解java應(yīng)用

    汽車(chē)結(jié)構(gòu)基本知識(shí)詳解(圖文)

    汽車(chē)結(jié)構(gòu)基本知識(shí)詳解(圖文)
    發(fā)表于 10-09 14:17

    詳解射頻和微波開(kāi)關(guān)的基本知識(shí)

    詳解射頻和微波開(kāi)關(guān)的基本知識(shí)
    發(fā)表于 05-20 06:06

    具有AoP技術(shù)的雷達(dá)傳感器

    傳感器封裝天線 (AoP) 技術(shù)消除了對(duì)高頻基板材料的需求,并降低了成本、制造復(fù)雜性和大概30%的布板空間。TI的AoP技術(shù)利用倒裝芯片封裝技術(shù)將天線放置在無(wú)塑封基板上,防止因天線穿過(guò)塑封材料時(shí)產(chǎn)生損耗
    發(fā)表于 11-04 06:32

    電子元器件基礎(chǔ)知識(shí)詳解

    電子元器件基礎(chǔ)知識(shí)詳解
    發(fā)表于 10-08 20:13 ?371次下載

    硅控制開(kāi)關(guān)(SCS)必需知識(shí)點(diǎn)詳解

    硅控制開(kāi)關(guān)(SCS)必需知識(shí)點(diǎn)詳解
    發(fā)表于 07-25 11:24 ?1119次閱讀
    硅控制開(kāi)關(guān)(SCS)必需<b class='flag-5'>知識(shí)</b>點(diǎn)<b class='flag-5'>詳解</b>

    基于反射機(jī)制的AOP模型的研究_張波

    基于反射機(jī)制的AOP模型的研究_張波
    發(fā)表于 03-17 15:47 ?0次下載

    FreeRTOS基礎(chǔ)知識(shí)詳解pdf下載

    FreeRTOS基礎(chǔ)知識(shí)詳解
    發(fā)表于 03-29 14:36 ?45次下載

    電磁閥知識(shí)大全,原理、維護(hù)、選型一一詳解

    電磁閥知識(shí)大全,原理、維護(hù)、選型一一詳解
    發(fā)表于 04-19 10:56 ?70次下載

    機(jī)器視覺(jué)基礎(chǔ)知識(shí)詳解模板

    機(jī)器視覺(jué)基礎(chǔ)知識(shí)詳解模板下載。
    發(fā)表于 05-28 14:48 ?13次下載

    詳解射頻微波基礎(chǔ)知識(shí)點(diǎn)

    詳解射頻微波基礎(chǔ)知識(shí)點(diǎn)
    的頭像 發(fā)表于 01-29 10:28 ?2223次閱讀

    無(wú)功補(bǔ)償原理基礎(chǔ)知識(shí)詳解

    無(wú)功補(bǔ)償原理基礎(chǔ)知識(shí)詳解
    的頭像 發(fā)表于 08-11 09:48 ?772次閱讀
    無(wú)功補(bǔ)償原理基礎(chǔ)<b class='flag-5'>知識(shí)</b><b class='flag-5'>詳解</b>

    AOP要怎么使用

    AOP(Aspect-Oriented Programming)經(jīng)常會(huì)出現(xiàn)在面試過(guò)程中,AOP到底有沒(méi)有用,要怎么使用呢。本篇來(lái)一起撥開(kāi)迷霧! 1 第一個(gè)AOP示例 我們會(huì)一次將所有的通知類型都覆蓋
    的頭像 發(fā)表于 10-09 16:18 ?590次閱讀
    <b class='flag-5'>AOP</b>要怎么使用

    AWR1843AOP單芯片77GHz和79GHz FMCW毫米波傳感器天線封裝(AOP)數(shù)據(jù)表

    電子發(fā)燒友網(wǎng)站提供《AWR1843AOP單芯片77GHz和79GHz FMCW毫米波傳感器天線封裝(AOP)數(shù)據(jù)表.pdf》資料免費(fèi)下載
    發(fā)表于 08-15 10:42 ?0次下載
    AWR1843<b class='flag-5'>AOP</b>單芯片77GHz和79GHz FMCW毫米波傳感器天線封裝(<b class='flag-5'>AOP</b>)數(shù)據(jù)表

    IWR1843AOP單芯片77GHz和79GHz FMCW毫米波傳感器天線封裝(AOP)數(shù)據(jù)表

    電子發(fā)燒友網(wǎng)站提供《IWR1843AOP單芯片77GHz和79GHz FMCW毫米波傳感器天線封裝(AOP)數(shù)據(jù)表.pdf》資料免費(fèi)下載
    發(fā)表于 08-15 11:20 ?0次下載
    IWR1843<b class='flag-5'>AOP</b>單芯片77GHz和79GHz FMCW毫米波傳感器天線封裝(<b class='flag-5'>AOP</b>)數(shù)據(jù)表