真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

spring框架入門(mén)之怎么使用切面編程AOP

本篇內(nèi)容介紹了“spring框架入門(mén)之怎么使用切面編程AOP”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

專(zhuān)注于為中小企業(yè)提供成都網(wǎng)站制作、網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)交口免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了上千企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。

1. 動(dòng)態(tài)代理

特點(diǎn):字節(jié)碼隨用隨創(chuàng)建,隨用隨加載

作用:在不修改源碼的基礎(chǔ)上對(duì)方法進(jìn)行增強(qiáng)

分類(lèi):


基于接口的動(dòng)態(tài)代理基于子類(lèi)的動(dòng)態(tài)代理
涉及的類(lèi)ProxyEnhancer
提供者JDK官方第三方庫(kù)cglib
如何創(chuàng)建代理對(duì)象使用Proxy中的 newProxyInstance 方法使用Enhancer類(lèi)中的create方法
創(chuàng)建代理對(duì)象的要求被代理類(lèi)至少實(shí)現(xiàn)一個(gè)接口,沒(méi)有則不能使用被代理對(duì)象不能是最終類(lèi)
1.1 基于接口的動(dòng)態(tài)代理

newProxyInstance方法的參數(shù):

  • ClassLoader : 類(lèi)加載器。用于加載代理對(duì)象字節(jié)碼,和被代理類(lèi)使用相同的類(lèi)加載器。

    寫(xiě)法: 代理對(duì)象.getClass().getClassLoader()

  • Class[] : 字節(jié)碼數(shù)組。用于讓代理對(duì)象和被代理對(duì)象有相同方法。

    寫(xiě)法: 代理對(duì)象.getClass().getInterfaces()

  • InvocationHandler:用于增強(qiáng)的代碼。書(shū)寫(xiě)對(duì)被代理方法增強(qiáng)的代碼,一般書(shū)寫(xiě)此接口的實(shí)現(xiàn)類(lèi),通常情況下是匿名內(nèi)部類(lèi),但不是必須的,此接口的實(shí)現(xiàn)類(lèi)一般誰(shuí)用到誰(shuí)寫(xiě)。

  • InvocationHandler參數(shù)中的invoke方法,執(zhí)行被代理對(duì)象的任何接口方法都會(huì)經(jīng)過(guò)該方法。方法參數(shù)及其含義:

    • proxy :代理對(duì)象的引用

    • method :當(dāng)前執(zhí)行的方法

    • args:當(dāng)前執(zhí)行方法所需的參數(shù)

    • 返回值:與被代理類(lèi)有相同的返回值

代碼示例:

public class Client {
    public static void main(String[] args) {
        final ProducerImpl producer = new ProducerImpl();

        producer.saleProduct(1000f);// 銷(xiāo)售產(chǎn)品,拿到錢(qián)1000.0
        System.out.println("對(duì)方法進(jìn)行增強(qiáng)后。。。。。");
        Producer proxyProduct = (Producer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                producer.getClass().getInterfaces(),
                new InvocationHandler() {

                    /**
                     * 執(zhí)行被代理對(duì)象的任何接口方法都會(huì)經(jīng)過(guò)該方法
                     * 方法的參數(shù)含義
                     * @param proxy  代理對(duì)象的引用
                     * @param method 當(dāng)前執(zhí)行方法
                     * @param args   當(dāng)前執(zhí)行方法所需的參數(shù)
                     * @return       和被代理對(duì)象有相同的返回值
                     * @throws Throwable
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 提供增強(qiáng)的代碼
                        Object returnValue = null;
                        // 1.獲取方法的執(zhí)行參數(shù)
                        Float money = (Float) args[0];

                        // 2.判斷當(dāng)前方法是不是銷(xiāo)售方法
                        if ("saleProduct".equals(method.getName())){
                            returnValue = method.invoke(producer, money * 0.8f);
                        }
                        return returnValue;
                    }
                });
        proxyProduct.saleProduct(1000f);// 銷(xiāo)售產(chǎn)品,拿到錢(qián)800.0
    }
}
1.2 基于子類(lèi)的動(dòng)態(tài)代理

create方法的參數(shù):

  • Class:字節(jié)碼。用于指定被代理對(duì)象的字節(jié)碼。

  • Callback:用于提供增強(qiáng)的代碼,類(lèi)似于基于接口的動(dòng)態(tài)代理的invoke方法。一般寫(xiě)的是該接口的子接口實(shí)現(xiàn)類(lèi) MethodInterceptor

  • create參數(shù)中 MethodInterceptor 的方法參數(shù):

    • o :代理對(duì)象的引用

    • method :當(dāng)前執(zhí)行的方法

    • objects:當(dāng)前執(zhí)行方法所需的參數(shù)

    • methodProxy:當(dāng)前執(zhí)行方法的代理對(duì)象

代碼示例:

public class Client {

    final Producer producer = new Producer();

    public static void main(String[] args) {

        final Producer producer = new Producer();

        producer.saleProduct(1000f);// 售賣(mài)商品,得到錢(qián)1000.0
        System.out.println("對(duì)方法進(jìn)行增強(qiáng)后。。。。。");
        Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
            /**
             * 執(zhí)行任何被處理對(duì)象的任何方法都會(huì)經(jīng)過(guò)該方法
             * @param o           代理對(duì)象的引用
             * @param method      當(dāng)前的執(zhí)行方法
             * @param objects     當(dāng)前執(zhí)行方法所需的參數(shù)
             * @param methodProxy 當(dāng)前執(zhí)行方法的代理對(duì)象
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                // 提供增強(qiáng)的方法
                Object returnValue = null;

                // 1.獲取當(dāng)前方法的執(zhí)行參數(shù)
                Float money = (Float) objects[0];
                // 2.判斷當(dāng)前的方法是不是銷(xiāo)售動(dòng)作
                if ("saleProduct".equals(method.getName())){
                    returnValue = method.invoke(producer, money * 0.8f);
                }
                return returnValue;
            }
        });
        cglibProducer.saleProduct(1000f);// 售賣(mài)商品,得到錢(qián)800.0
    }
}
1.3 動(dòng)態(tài)代理總結(jié)

動(dòng)態(tài)代理的一般使用方式:

  • 獲取被代理類(lèi)對(duì)象(被代理對(duì)象的字節(jié)碼、被代理類(lèi)對(duì)象的類(lèi)加載器等信息)

  • 在代理類(lèi)提供的方法中對(duì)被代理類(lèi)中的方法進(jìn)行增強(qiáng)

2. spring中的AOP

spring中的AOP是通過(guò)配置的方式實(shí)現(xiàn)動(dòng)態(tài)代理

2.1 spring中的相關(guān)術(shù)語(yǔ):

**Joinpoint(連接點(diǎn)):**指被攔截到的點(diǎn)。在spring中這些點(diǎn)指的是方法,因?yàn)閟pring只支持方法類(lèi)型的連接點(diǎn)。可以理解為業(yè)務(wù)層中所有的方法。

**Pointcut(切入點(diǎn)):**指需要對(duì)那些Joinpoint進(jìn)行攔截的定義??梢岳斫鉃楸辉鰪?qiáng)的方法。

**Advice(通知/增強(qiáng)):**指攔截到Joinpoint后需要做的事情。通知類(lèi)型:前置通知,后置通知,異常通知,最終通知,環(huán)繞通知。

  • 前置通知:在執(zhí)行業(yè)務(wù)層方法前的通知;

  • 后置通知:在執(zhí)行業(yè)務(wù)層方法后的通知;

  • 異常通知:catch中的通知;

  • 最終通知:在finally中的通知;

  • 環(huán)繞通知:整個(gè)invoke方法執(zhí)行就是環(huán)繞通知;

spring框架入門(mén)之怎么使用切面編程AOP

**Introduction(引介):**一種特殊的通知在不修改類(lèi)代碼的前提下,Introduction可以在運(yùn)行期為類(lèi)動(dòng)態(tài)的添加一些方法或Field。

Target(目標(biāo)對(duì)象):代理的目標(biāo)對(duì)象。

**Weaving(織入):**指把增強(qiáng)應(yīng)用到目標(biāo)對(duì)象來(lái)創(chuàng)建代理對(duì)象的過(guò)程。spring是動(dòng)態(tài)代理織入的,而AspectJ采用編譯期織入和類(lèi)裝載期織入。

**Proxy(代理):**一個(gè)類(lèi)被AOP織入增強(qiáng)后,就產(chǎn)生一個(gè)結(jié)果代理類(lèi)。

**Aspect(切面):**是切入點(diǎn)和通知(引介)的結(jié)合。

2.2 spring中AOP

開(kāi)發(fā)階段:

編寫(xiě)核心業(yè)務(wù)代碼(主線(xiàn)開(kāi)發(fā),熟悉業(yè)務(wù)代碼即可進(jìn)行開(kāi)發(fā))

把公共代碼提取出來(lái),制作成通知。(開(kāi)發(fā)最后階段)

在配置文件中聲明切入點(diǎn)與通知之間的關(guān)系,即切面。

運(yùn)行階段:

spring框架監(jiān)控切入點(diǎn)的方法執(zhí)行。一旦監(jiān)控到切入點(diǎn)方法被運(yùn)行,使用代理機(jī)制,動(dòng)態(tài)創(chuàng)建目標(biāo)對(duì)象的代理對(duì)象,通知類(lèi)別,在代理對(duì)象的對(duì)應(yīng)位置,將通知對(duì)應(yīng)的功能織入,完成完整的代碼邏輯運(yùn)行。

spring中的AOP會(huì)根據(jù)目標(biāo)是否實(shí)現(xiàn)了接口來(lái)決定采用哪種動(dòng)態(tài)代理的方式

3.基于XML的AOP配置

3.1 將通知類(lèi)交由IoC容器管理

將通知類(lèi)注冊(cè)到spring的IoC容器中


	

3.2 使用 標(biāo)簽進(jìn)行AOP配置

用于聲明aop配置


	

3.3 使用 配置切面

用于配置切面

屬性:

① id屬性:是給切面提供一個(gè)唯一標(biāo)識(shí)

② ref屬性:是指定通知類(lèi)bean的Id。


    
  • :用于配置前置通知

  • :用于配置后置通知

  • :用于配置異常通知

  • :用于配置最終通知

  • :用于配置環(huán)繞通知

    ① method屬性:用于指定Logger類(lèi)中哪個(gè)方法是前置通知

    ② pointcut屬性:用于指定切入點(diǎn)表達(dá)式,該表達(dá)式的含義指的是對(duì)業(yè)務(wù)層中哪些方法增強(qiáng)

    ③ pointcut-ref屬性:用于指定切入點(diǎn)表達(dá)式的id

3.4 使用 配置切入點(diǎn)表達(dá)式

用于配置切入點(diǎn)表達(dá)式,就是指定對(duì)那些類(lèi)進(jìn)行的那些方法進(jìn)行增強(qiáng)

屬性:

① id屬性:用于指定切入點(diǎn)的唯一標(biāo)識(shí)

② expression屬性:用于配置切入點(diǎn)表達(dá)式

代碼示例:











    
    
        
        
        
    

4. 切入點(diǎn)表達(dá)式

關(guān)鍵字:execution("表達(dá)式")

表達(dá)式寫(xiě)法:訪問(wèn)修飾符 返回值 包名.***.包名.類(lèi)名.方法名(參數(shù)列表)

標(biāo)準(zhǔn)寫(xiě)法:public void cn.bruce.service.impl.AccountServiceImpl.saveAccount()

  • 訪問(wèn)修飾符可以省略(訪問(wèn)權(quán)限不能寫(xiě) *),表示匹配任意類(lèi)型的訪問(wèn)權(quán)限,但Spring現(xiàn)在只支持public權(quán)限;

    void cn.bruce.service.impl.AccountServiceImpl.saveAccount()

  • 返回值可以使用通配符,表示任意返回值;

    * cn.bruce.service.impl.AccountServiceImpl.saveAccount()

  • 包名可以使用通配符,表示任意包,有幾級(jí)包就要寫(xiě)幾個(gè) *;

    * *.*.*.*.AccountServiceImpl.saveAccount()

  • 包名可以使用 .. 表示當(dāng)前包及其子包

    * cn..AccountServiceImpl.saveAccount()

  • 類(lèi)名和方法名都可以使用通配符代替

    * *..*.*()

**參數(shù)列表:**直接寫(xiě)數(shù)據(jù)類(lèi)型

  • 基本數(shù)據(jù)類(lèi)型直接寫(xiě)名稱(chēng),如:int long double boolean

  • 引用數(shù)據(jù)類(lèi)型要寫(xiě)全類(lèi)名,如:cn.bruce.domain.Accout

  • 可以使用通配符 * 表示任意類(lèi)型,但是必須有參數(shù)

  • 可以使用通配符 * 進(jìn)行占位,如:* *..*.*(*, int)

  • 可以使用 .. 表示有無(wú)參數(shù)均可,有參數(shù)可以是任意類(lèi)型 * *..*.*(..)

全通配寫(xiě)法:* *..*.*(..)

開(kāi)發(fā)中切入點(diǎn)表達(dá)式的通常寫(xiě)法:如:切到業(yè)務(wù)層實(shí)現(xiàn)類(lèi)下的所有方法 * cn.bruce.service.impl.*.*(..)

5. 常用通知類(lèi)型

前置通知 :在切入點(diǎn)方法執(zhí)行之前執(zhí)行

后置通知 :在切入點(diǎn)方法執(zhí)行之后執(zhí)行。后置通知和異常通知永遠(yuǎn)只能執(zhí)行一個(gè)

異常通知 :在切入點(diǎn)方法執(zhí)行產(chǎn)生異常后執(zhí)行。異常通知和后置通知永遠(yuǎn)只能執(zhí)行一個(gè)

最終通知 :無(wú)論切入點(diǎn)方法是否正常執(zhí)行,它都會(huì)在其后面執(zhí)行

環(huán)繞通知 :是spring框架為我們提供的一種可以在代碼中手動(dòng)控制增強(qiáng)方法何時(shí)執(zhí)行的方式。

代碼示例:



    
    

    
    
        
        

        
        

        
        

        
        

        
        
    

6. 基于注解的AOP配置

配置步驟:

①導(dǎo)入maven坐標(biāo)


    
        org.springframework
        spring-context
        5.2.8.RELEASE
    

    
        org.aspectj
        aspectjweaver
        1.9.6
    

② 書(shū)寫(xiě)spring配置類(lèi),開(kāi)啟包掃描和注解支持

@configuration
@ComponentScan("cn.bruce") // 開(kāi)啟包掃描,配置需要掃描的包
@EnableAspectJAutoProxy(proxyTargetClass = true) // 開(kāi)啟注解驅(qū)動(dòng)
public class SpringConfiguration {
}

③ 將業(yè)務(wù)層實(shí)體類(lèi)交由IoC容器管理

@Service("testService")
public class TestServiceImpl implements TestService {

    @Override
    public void testOfVoid() {
        System.out.println("testOfVoid is running......");
    }

    @Override
    public void testOfInt(int i) {
        System.out.println("testOfInt is running......number is" + i);
    }

    @Override
    public void testOfInteger(Integer i) {
//        i = 1/0;
        System.out.println("testOfInteger is running......number is" + i);

    }

    @Override
    public void testOfAccount(Account account) {
        int i = 1/0;
        System.out.println("testOfInt is running......number is" + account);

    }
}

④ 書(shū)寫(xiě)切面類(lèi),聲明為切面類(lèi)并設(shè)置切入點(diǎn)和通知類(lèi)型

@Component("logger")
@Aspect // 表示此類(lèi)為切面類(lèi)
public class Logger {

    @Pointcut("execution(* cn..impl.*.*(..))") // 指定切入點(diǎn)表達(dá)式
    private void pointcut(){}

    /**
     * 前置通知
     */
    @Before("execution(* cn..impl.*.*(int))")
    public  void beforePrintLog(){
        System.out.println("前置通知Logger類(lèi)中的beforePrintLog方法開(kāi)始記錄日志了。。。");
    }

    /**
     * 后置通知
     */
    @AfterReturning("execution(* cn..impl.*.*(Integer))")
    public  void afterReturningPrintLog(){
        System.out.println("后置通知Logger類(lèi)中的afterReturningPrintLog方法開(kāi)始記錄日志了。。。");
    }
    /**
     * 異常通知
     */
    @AfterThrowing("pointcut()")
    public  void afterThrowingPrintLog(){
        System.out.println("異常通知Logger類(lèi)中的afterThrowingPrintLog方法開(kāi)始記錄日志了。。。");
    }

    /**
     * 最終通知
     */
    @After("execution(* cn..impl.*.*())")
    public  void afterPrintLog(){
        System.out.println("最終通知Logger類(lèi)中的afterPrintLog方法開(kāi)始記錄日志了。。。");
    }

    /**
     * 環(huán)繞通知
     */
    @Around("execution(* cn..impl.*.*(cn.bruce.domain.Account))")
    public Object aroundPringLog(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try{
            //得到方法執(zhí)行所需的參數(shù)
            Object[] args = pjp.getArgs();

            System.out.println("Logger類(lèi)中的aroundPringLog方法開(kāi)始記錄日志了。。。前置");

            //明確調(diào)用業(yè)務(wù)層方法(切入點(diǎn)方法)
            rtValue = pjp.proceed(args);

            System.out.println("Logger類(lèi)中的aroundPringLog方法開(kāi)始記錄日志了。。。后置");

            return rtValue;
        }catch (Throwable t){
            System.out.println("Logger類(lèi)中的aroundPringLog方法開(kāi)始記錄日志了。。。異常");
            throw new RuntimeException(t);
        }finally {
            System.out.println("Logger類(lèi)中的aroundPringLog方法開(kāi)始記錄日志了。。。最終");
        }
    }

}

⑤ 書(shū)寫(xiě)測(cè)試類(lèi)進(jìn)行測(cè)試

public class TestAOP {
    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        TestService testService = (TestService) ac.getBean("testService");
        testService.testOfInt(133);
        System.out.println("-----------");
        testService.testOfInteger(112);
        System.out.println("-----------");
        testService.testOfVoid();
        System.out.println("-----------");

        Account account = (Account) ac.getBean("account");
        account.setName("Bruce");
        account.setAge(112);
        testService.testOfAccount(account);
    }
}

“spring框架入門(mén)之怎么使用切面編程AOP”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!


網(wǎng)站欄目:spring框架入門(mén)之怎么使用切面編程AOP
本文地址:http://weahome.cn/article/jdciee.html

其他資訊

在線(xiàn)咨詢(xún)

微信咨詢(xún)

電話(huà)咨詢(xún)

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部