中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

Spring AOP是什么?你都拿它做什么?

2019-01-17    來(lái)源:importnew

容器云強(qiáng)勢(shì)上線!快速搭建集群,上萬(wàn)Linux鏡像隨意使用

為什么會(huì)有面向切面編程(AOP)?我們知道Java是一個(gè)面向?qū)ο螅∣OP)的語(yǔ)言,但它有一些弊端,比如當(dāng)我們需要為多個(gè)不具有繼承關(guān)系的對(duì)象引入一個(gè)公共行為,例如日志、權(quán)限驗(yàn)證、事務(wù)等功能時(shí),只能在在每個(gè)對(duì)象里引用公共行為。這樣做不便于維護(hù),而且有大量重復(fù)代碼。AOP的出現(xiàn)彌補(bǔ)了OOP的這點(diǎn)不足。

為了闡述清楚Spring AOP,我們從將以下方面進(jìn)行討論:

  1. 代理模式
  2. 靜態(tài)代理原理及實(shí)踐
  3. 動(dòng)態(tài)代理原理及實(shí)踐
  4. Spring AOP原理及實(shí)戰(zhàn)

1. 代理模式

代理模式:為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。這段話(huà)比較官方,但我更傾向于用自己的語(yǔ)言理解:比如A對(duì)象要做一件事情,在沒(méi)有代理前,自己來(lái)做;在對(duì) A 代理后,由 A 的代理類(lèi) B 來(lái)做。代理其實(shí)是在原實(shí)例前后加了一層處理,這也是 AOP 的初級(jí)輪廓。

2. 靜態(tài)代理原理及實(shí)踐

靜態(tài)代理模式:靜態(tài)代理說(shuō)白了,就是在程序運(yùn)行前就已經(jīng)存在代理類(lèi)的字節(jié)碼文件、代理類(lèi)和原始類(lèi)的關(guān)系在運(yùn)行前就已經(jīng)確定。廢話(huà)不多說(shuō),我們看一下代碼。為了方便閱讀,博主把單獨(dú)的 class 文件合并到接口中,讀者可以直接復(fù)制代碼運(yùn)行:

package test.staticProxy;

// 接口
public interface IUserDao {
    void save();
    void find();
}

//目標(biāo)對(duì)象
class UserDao implements IUserDao{
    @Override
    public void save() {
        System.out.println("模擬:保存用戶(hù)!");
    }
    @Override
    public void find() {
        System.out.println("模擬:查詢(xún)用戶(hù)");
    }
}

/**
  * 靜態(tài)代理
  * 特點(diǎn):
  * 2. 目標(biāo)對(duì)象必須要實(shí)現(xiàn)接口
  * 2. 代理對(duì)象,要實(shí)現(xiàn)與目標(biāo)對(duì)象一樣的接口
 */
class UserDaoProxy implements IUserDao{

    // 代理對(duì)象,需要維護(hù)一個(gè)目標(biāo)對(duì)象
    private IUserDao target = new UserDao();

    @Override
    public void save() {
        System.out.println("代理操作: 開(kāi)啟事務(wù)...");
        target.save();   // 執(zhí)行目標(biāo)對(duì)象的方法
        System.out.println("代理操作:提交事務(wù)...");
    }

    @Override
    public void find() {
        target.find();
    }
}

測(cè)試結(jié)果:

靜態(tài)代理雖然保證了業(yè)務(wù)類(lèi)只需關(guān)注邏輯本身,代理對(duì)象的一個(gè)接口只服務(wù)于一種類(lèi)型的對(duì)象。如果要代理的方法很多,勢(shì)必要為每一種方法都進(jìn)行代理。再者,如果增加一個(gè)方法,除了實(shí)現(xiàn)類(lèi)需要實(shí)現(xiàn)這個(gè)方法外,所有的代理類(lèi)也要實(shí)現(xiàn)此方法。增加了代碼的維護(hù)成本。那么要如何解決呢?答案是使用動(dòng)態(tài)代理。

3. 動(dòng)態(tài)代理原理及實(shí)踐

動(dòng)態(tài)代理模式:動(dòng)態(tài)代理類(lèi)的源碼是在程序運(yùn)行期間,通過(guò) JVM 反射等機(jī)制動(dòng)態(tài)生成。代理類(lèi)和委托類(lèi)的關(guān)系是運(yùn)行時(shí)才確定的。實(shí)例如下:

package test.dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 接口
public interface IUserDao {
    void save();
    void find();
}

//目標(biāo)對(duì)象
class UserDao implements IUserDao{

    @Override
    public void save() {
        System.out.println("模擬: 保存用戶(hù)!");
    }

    @Override
    public void find() {
        System.out.println("查詢(xún)");
    }
}

/**
 * 動(dòng)態(tài)代理:
 * 代理工廠,給多個(gè)目標(biāo)對(duì)象生成代理對(duì)象!
 *
 */
class ProxyFactory {

    // 接收一個(gè)目標(biāo)對(duì)象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    // 返回對(duì)目標(biāo)對(duì)象(target)代理后的對(duì)象(proxy)
    public Object getProxyInstance() {
        Object proxy = Proxy.newProxyInstance(
            target.getClass().getClassLoader(),  // 目標(biāo)對(duì)象使用的類(lèi)加載器
            target.getClass().getInterfaces(),   // 目標(biāo)對(duì)象實(shí)現(xiàn)的所有接口
            new InvocationHandler() {            // 執(zhí)行代理對(duì)象方法時(shí)候觸發(fā)

                @Override
                public Object invoke(Object proxy, Method method, Object[] args)
                        throws Throwable {

                    // 獲取當(dāng)前執(zhí)行的方法的方法名
                    String methodName = method.getName();
                    // 方法返回值
                    Object result = null;
                    if ("find".equals(methodName)) {
                        // 直接調(diào)用目標(biāo)對(duì)象方法
                        result = method.invoke(target, args);
                    } else {
                        System.out.println("開(kāi)啟事務(wù)...");
                        // 執(zhí)行目標(biāo)對(duì)象方法
                        result = method.invoke(target, args);
                        System.out.println("提交事務(wù)...");
                    }
                    return result;
                }
            }
        );
        return proxy;
    }
}

測(cè)試結(jié)果如下:

IUserDao proxy = (IUserDao)new ProxyFactory(target).getProxyInstance();

其實(shí)是 JDK 動(dòng)態(tài)生成了一個(gè)類(lèi)去實(shí)現(xiàn)接口,隱藏了這個(gè)過(guò)程:

class $jdkProxy implements IUserDao{}

使用 JDK 生成的動(dòng)態(tài)代理的前提是目標(biāo)類(lèi)必須有實(shí)現(xiàn)的接口。但這里又引入一個(gè)問(wèn)題,如果某個(gè)類(lèi)沒(méi)有實(shí)現(xiàn)接口,就不能使用 JDK 動(dòng)態(tài)代理。所以 CGLIB 代理就是解決這個(gè)問(wèn)題的。

CGLIB?是以動(dòng)態(tài)生成的子類(lèi)繼承目標(biāo)的方式實(shí)現(xiàn),在運(yùn)行期動(dòng)態(tài)的在內(nèi)存中構(gòu)建一個(gè)子類(lèi),如下:

public class UserDao{}

// CGLIB 是以動(dòng)態(tài)生成的子類(lèi)繼承目標(biāo)的方式實(shí)現(xiàn),程序執(zhí)行時(shí),隱藏了下面的過(guò)程
public class $Cglib_Proxy_class  extends UserDao{}

CGLIB 使用的前提是目標(biāo)類(lèi)不能為 final 修飾。因?yàn)?final 修飾的類(lèi)不能被繼承。

現(xiàn)在,我們可以看看 AOP 的定義:面向切面編程,核心原理是使用動(dòng)態(tài)代理模式在方法執(zhí)行前后或出現(xiàn)異常時(shí)加入相關(guān)邏輯。

通過(guò)定義和前面代碼我們可以發(fā)現(xiàn)3點(diǎn):

  • AOP 是基于動(dòng)態(tài)代理模式。
  • AOP 是方法級(jí)別的。
  • AOP 可以分離業(yè)務(wù)代碼和關(guān)注點(diǎn)代碼(重復(fù)代碼),在執(zhí)行業(yè)務(wù)代碼時(shí),動(dòng)態(tài)的注入關(guān)注點(diǎn)代碼。切面就是關(guān)注點(diǎn)代碼形成的類(lèi)。

4. Spring AOP

前文提到 JDK 代理和 CGLIB 代理兩種動(dòng)態(tài)代理。優(yōu)秀的 Spring 框架把兩種方式在底層都集成了進(jìn)去,我們無(wú)需擔(dān)心自己去實(shí)現(xiàn)動(dòng)態(tài)生成代理。那么,Spring是如何生成代理對(duì)象的?

  1. 創(chuàng)建容器對(duì)象的時(shí)候,根據(jù)切入點(diǎn)表達(dá)式攔截的類(lèi),生成代理對(duì)象。
  2. 如果目標(biāo)對(duì)象有實(shí)現(xiàn)接口,使用 JDK 代理。如果目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)接口,則使用 CGLIB 代理。然后從容器獲取代理后的對(duì)象,在運(yùn)行期植入“切面”類(lèi)的方法。通過(guò)查看 Spring 源碼,我們?cè)?DefaultAopProxyFactory 類(lèi)中,找到這樣一段話(huà)。

簡(jiǎn)單的從字面意思看出:如果有接口,則使用 JDK 代理,反之使用 CGLIB ,這剛好印證了前文所闡述的內(nèi)容。Spring AOP 綜合兩種代理方式的使用前提有會(huì)如下結(jié)論:如果目標(biāo)類(lèi)沒(méi)有實(shí)現(xiàn)接口,且 class 為 final 修飾的,則不能進(jìn)行 Spring AOP 編程!

知道了原理,現(xiàn)在我們將自己手動(dòng)實(shí)現(xiàn) Spring 的 AOP:

package test.spring_aop_anno;

import org.aspectj.lang.ProceedingJoinPoint;

public interface IUserDao {
    void save();
}

// 用于測(cè)試 CGLIB 動(dòng)態(tài)代理
class OrderDao {
    public void save() {
        //int i =1/0; 用于測(cè)試異常通知
        System.out.println("保存訂單...");
    }
}

//用于測(cè)試 JDK 動(dòng)態(tài)代理
class UserDao implements IUserDao {
    public void save() {
        //int i =1/0; 用于測(cè)試異常通知
        System.out.println("保存用戶(hù)...");
    }
}

//切面類(lèi)
class TransactionAop {

    public void beginTransaction() {
        System.out.println("[前置通知]  開(kāi)啟事務(wù)..");
    }

    public void commit() {
        System.out.println("[后置通知] 提交事務(wù)..");
    }

    public void afterReturing() {
        System.out.println("[返回后通知]");
    }

    public void afterThrowing() {
        System.out.println("[異常通知]");
    }

    public void arroud(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("[環(huán)繞前:]");
        pjp.proceed(); // 執(zhí)行目標(biāo)方法
        System.out.println("[環(huán)繞后:]");
    }
}

Spring 的 XML 配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="

http://www.springframework.org/schema/beans


http://www.springframework.org/schema/beans/spring-beans.xsd


http://www.springframework.org/schema/context


http://www.springframework.org/schema/context/spring-context.xsd


http://www.springframework.org/schema/aop


http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- dao實(shí)例加入容器 -->
    <bean id="userDao" class="test.spring_aop_anno.UserDao"></bean>

    <!-- dao實(shí)例加入容器 -->
    <bean id="orderDao" class="test.spring_aop_anno.OrderDao"></bean>

    <!-- 實(shí)例化切面類(lèi) -->
    <bean id="transactionAop" class="test.spring_aop_anno.TransactionAop"></bean>

    <!-- Aop相關(guān)配置 -->
    <aop:config>
        <!-- 切入點(diǎn)表達(dá)式定義 -->
        <aop:pointcut expression="execution(* test.spring_aop_anno.*Dao.*(..))" id="transactionPointcut"/>
        <!-- 切面配置 -->
        <aop:aspect ref="transactionAop">
            <!-- 【環(huán)繞通知】 -->
            <aop:around method="arroud" pointcut-ref="transactionPointcut"/>
            <!-- 【前置通知】 在目標(biāo)方法之前執(zhí)行 -->
            <aop:before method="beginTransaction" pointcut-ref="transactionPointcut" />
            <!-- 【后置通知】 -->
            <aop:after method="commit" pointcut-ref="transactionPointcut"/>
            <!-- 【返回后通知】 -->
            <aop:after-returning method="afterReturing" pointcut-ref="transactionPointcut"/>
            <!-- 異常通知 -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="transactionPointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

切入點(diǎn)表達(dá)式不在這里介紹。參考?Spring AOP 切入點(diǎn)表達(dá)式

代碼的測(cè)試結(jié)果如下:

到這里,我們已經(jīng)全部介紹完Spring AOP;氐介_(kāi)篇的問(wèn)題,我們拿它做什么?

  1. Spring聲明式事務(wù)管理配置:請(qǐng)參考博主的另一篇文章:分布式系統(tǒng)架構(gòu)實(shí)戰(zhàn) demo:SSM+Dubbo
  2. Controller層的參數(shù)校驗(yàn):參考 Spring AOP攔截Controller做參數(shù)校驗(yàn)
  3. 使用 Spring AOP 實(shí)現(xiàn) MySQL 數(shù)據(jù)庫(kù)讀寫(xiě)分離案例分析
  4. 在執(zhí)行方法前,判斷是否具有權(quán)限
  5. 對(duì)部分函數(shù)的調(diào)用進(jìn)行日志記錄:監(jiān)控部分重要函數(shù),若拋出指定的異常,可以以短信或郵件方式通知相關(guān)人員。
  6. 信息過(guò)濾,頁(yè)面轉(zhuǎn)發(fā)等等功能

博主一個(gè)人的力量有限,只能列舉這么多,歡迎評(píng)論區(qū)對(duì)文章做補(bǔ)充。

Spring AOP還能做什么,實(shí)現(xiàn)什么魔幻功能,就在于我們每一個(gè)平凡而又睿智的程序猿!

標(biāo)簽: Mysql ssl 代碼 權(quán)限 數(shù)據(jù)庫(kù)

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:一文搞清Gradle依賴(lài)

下一篇:php 操作mysql