動態(tài)代理

1、什么是代理宝磨?

代理是一種軟件設計模式疮绷,其是通過訪問代理對象的方法翰舌,而對被代理對象進行操作,相當于加了一個中間環(huán)節(jié)冬骚。這個就好比 商戶---->明星經(jīng)紀人(代理)---->明星這種模式椅贱。我們可以不通過直接與明星對話的情況下懂算,而通過明星經(jīng)紀人(代理)與其產(chǎn)生間接對話。有些時候我們不希望修改已有代碼增加功能庇麦,這個時候就可以使用代理计技。 上網(wǎng)翻墻也是同一個意思。

2山橄、靜態(tài)代理和動態(tài)代理

我們根據(jù)加載被代理類的時機不同垮媒,將代理分為靜態(tài)代理和動態(tài)代理。如果我們在代碼編譯時就確定了被代理的類是哪一個驾胆,那么就可以直接使用靜態(tài)代理涣澡;如果不能確定,那么可以使用類的動態(tài)加載機制丧诺,在代碼運行期間加載被代理的類這就是動態(tài)代理,比如RPC框架和Spring AOP機制奄薇。

3驳阎、靜態(tài)代理

要點:

  • 代理接口
  • 被代理對象
  • 代理對象,與被代理對象實現(xiàn)同一接口馁蒂,持有被代理對象的對象呵晚。
/**
 * 描述:
 *
 * @author bigpeng
 * @create 2019-12-02 23:05
 */
public interface Star {

    void show();

    void movie();


}
/**
 * 描述:
 *
 * @author bigpeng
 * @create 2019-12-02 23:06
 */
public class Loudehua implements Star {

    @Override
    public void show() {
        System.out.println("劉德華開個人演唱會");
    }

    @Override
    public void movie() {
        System.out.println("劉德華出演電影");
    }
}
package proxy;

/**
 * 描述:
 *
 * @author bigpeng
 * @create 2019-12-02 23:09
 */
public class LoudehuaProxy implements Star{
    private Star loudehua;

    public LoudehuaProxy(Star loudehua){
        this.loudehua=loudehua;
    }
    @Override
    public void show() {
        System.out.println("經(jīng)紀人談好演出事宜");
        System.out.println("經(jīng)紀人簽訂合約");
        loudehua.show();
        System.out.println("經(jīng)紀人收款");
    }

    @Override
    public void movie() {
        System.out.println("經(jīng)紀人談好演出事宜");
        System.out.println("經(jīng)紀人簽訂合約");
        loudehua.movie();
        System.out.println("經(jīng)紀人收款");
    }

}
package proxy;

/**
 * 描述:
 *  java 動態(tài)代理
 * @author bigpeng
 * @create 2019-12-02 23:15
 */
public class Test {

    public static void main(String[] args) {
        Star star=new Loudehua();
//        Star star=new LoudehuaProxy();
//        star.show();
//        star.movie();
        DynamicProxy dynamicProxy=new DynamicProxy();
        Star o = (Star) dynamicProxy.newInstance(star);
        o.movie();
        o.show();
    }

}

4、動態(tài)代理

動態(tài)代理在java中有兩種實現(xiàn)方式

  • jdk動態(tài)代理
  • cglib 動態(tài)代理

JDK動態(tài)代理

要點:

  • 動態(tài)代理接口
  • 動態(tài)代理對象
  • 動態(tài)代理實現(xiàn)類沫屡。實現(xiàn)InvocationHandler接口
/**
 * 描述:
 *   動態(tài)代理類饵隙,實現(xiàn)InvocationHandler接口
 *   動態(tài)代理的而必須是接口
 * @author bigpeng
 * @create 2019-12-02 23:28
 */
public class DynamicProxy implements InvocationHandler {

    private Object targetObject;

    public Object newInstance(Object targetObject){
        this.targetObject=targetObject;
        //第一個參數(shù)指定產(chǎn)生代理對象的類加載器,需要將其指定為和目標對象同一個類加載器
        //第二個參數(shù)要實現(xiàn)和目標對象一樣的接口沮脖,所以只需要拿到目標對象的實現(xiàn)接口
        //第三個參數(shù)表明這些被攔截的方法在被攔截時需要執(zhí)行哪個InvocationHandler的invoke方法
        return Proxy.newProxyInstance(
                targetObject.getClass().getClassLoader(),
                targetObject.getClass().getInterfaces(),
                this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("經(jīng)紀人談好演出事宜");
        System.out.println("經(jīng)紀人簽訂合約");
        Object result = method.invoke(targetObject, args);
        System.out.println("經(jīng)紀人收錢");
        return result;
    }
}

cglib 動態(tài)代理

  • 導入cglibjar包
<dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.2.12</version>
    </dependency>
  • 如果使用spring,則配置使用cglib
    <aop:aspectj-autoproxy proxy-target-class="true"/>

被代理類

/**
 * 描述:
 *
 * @author bigpeng
 * @create 2019-12-03 13:32
 */
public class Star {

    public void move(){
        System.out.println("參演電影");
    }

}

代理類

/**
 * 描述:
 *
 * @author bigpeng
 * @create 2019-12-03 13:32
 */
public class StarProxy  implements MethodInterceptor {

    /**
     * 提供代理對象
     * @param object
     * @return
     */
    public Object newInstance(Object object){
        return Enhancer.create(object.getClass(), this);
    }
    /**
     *
     * @param o  被代理的對象
     * @param method 被代理的方法
     * @param objects 方法參數(shù)
     * @param methodProxy  代理方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println(method.getName());
        System.out.println("簽合同");
        Object invoke = methodProxy.invokeSuper(o, objects);
        System.out.println("收錢");
        return invoke;
    }
}

測試類

/**
 * 描述:
 *
 * @author bigpeng
 * @create 2019-12-03 13:35
 */
public class Test {

    public static void main(String[] args) {
        StarProxy starProxy=new StarProxy();
        Star o = (Star) starProxy.newInstance(new Star());
        o.move();

    }

}

spring AOP源碼:

  • jdk動態(tài)代理
    JdkDynamicAopProxy
  • cglib動態(tài)代理
    CglibAopProxy

區(qū)別
JDK動態(tài)代理:利用反射機制生成一個實現(xiàn)代理接口的匿名類金矛,在調用具體方法前調用InvokeHandler來處理。
CGlib動態(tài)代理:利用ASM(開源的Java字節(jié)碼編輯庫勺届,操作字節(jié)碼)開源包驶俊,將代理對象類的class文件加載進來,通過修改其字節(jié)碼生成子類來處理免姿。

區(qū)別:JDK代理只能對實現(xiàn)接口的類生成代理饼酿;CGlib是針對類實現(xiàn)代理,對指定的類生成一個子類胚膊,并覆蓋其中的方法故俐,這種通過繼承類的實現(xiàn)方式,不能代理final修飾的類

spring 注解式AOP

## 注解方式配置AOP

<context:component-scan base-package="spring.aop"></context:component-scan>

<aop:aspectj-autoproxy/>

package spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**

  • 描述:

  • @author bigpeng

  • @create 2019-10-25 13:25
    */
    @Component
    @Aspect
    public class AopAspect {

    /*

    • 定義一個方法紊婉,用于聲明切入點的表達式药版,一般地,該方法中不需要添加其他代碼
    • 使用@Pointcut來聲明切入點表達式
    • 后面的其他通知直接使用方法名來引用切入點表達式
      /
      @Pointcut("execution(
      spring.aop.Impl.(..))")
      public void declareJoinPointExpression(){}
      /*
      • 在top.cheungchingyin.spring.aop.
      • ArithmeticCalculator接口的每一個實現(xiàn)類的每一個方法開始之前執(zhí)行一段代碼
        */
        @Before("declareJoinPointExpression()")
        public void beforeMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("The Method " + methodName + " Begins with " + Arrays.asList(args));
        }

    /*

    • 返回通知:在方法執(zhí)行后執(zhí)行的代碼肩榕,無論方法是否發(fā)生異常
      */
      @After("declareJoinPointExpression()")
      public void afterMethod(JoinPoint joinPoint) {
      String methodName = joinPoint.getSignature().getName();
      System.out.println("The Method " + methodName + " ends with");

    }

    /**

    • 返回通知:在方法正常結束后執(zhí)行代碼 返回通知是可以訪問到方法的返回值
    • @param joinPoint
      */
      @AfterReturning(value = "declareJoinPointExpression()", returning = "result")
      public void afterReturningMethod(JoinPoint joinPoint, Object result) {
      String methodName = joinPoint.getSignature().getName();
      System.out.println("The Method " + methodName + " ends with " + result);

    }

    /**

    • 在方法出現(xiàn)異常的時候會執(zhí)行代碼 可以訪問到的異常對象刚陡,且可以指定出現(xiàn)特定異常的時候才執(zhí)行通知代碼
    • @param joinPoint
    • @param ex
      */
      @AfterThrowing(value = "declareJoinPointExpression()", throwing = "ex")
      public void afterThrowing(JoinPoint joinPoint, Exception ex) {
      String methodName = joinPoint.getSignature().getName();
      System.out.println("The Method " + methodName + " occurs with " + ex);

    }

    /**

    • 環(huán)繞通知需要攜帶ProceedingJoinPoint類型參數(shù)
    • 環(huán)繞通知類似于動態(tài)代理的全過程惩妇,ProceedingJoinPoint類型的參數(shù)可以決定是否執(zhí)行目標方法
    • 且環(huán)繞通知必須要有返回值,返回值即為目標方法的返回值
    • @param pjd
    • @return
      */
      @Around("declareJoinPointExpression()")
      public Object arroundMethod(ProceedingJoinPoint pjd) {
      Object result = null;
      String methodName = pjd.getSignature().getName();
      // 執(zhí)行目標方法
      try {
      // 前置通知
      System.out.println("【Around】The method 【" + methodName + "】 begins with" + Arrays.asList(pjd.getArgs()));
      result = pjd.proceed();
      // 后置通知
      System.out.println("【Around】The Method " + " ends with 【" + result + "】");
      } catch (Throwable e) {
      // 異常通知
      System.out.println("The method occurs exception" + e);
      }
      // 后置通知
      System.out.println("【Around】The Method 【" + methodName + "】 ends ");
      return result;
      }
      }
/**
 * @author bigpeng
 * @create 2020-06-28-19:58
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Delete {
    String value();
}
package proxy;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

/**
 * @author bigpeng
 * @create 2020-06-28-20:02
 */
public class MapperProxy implements InvocationHandler {

    public Object getMapper(Class mapper){
        return Proxy.newProxyInstance(mapper.getClassLoader(),new Class[]{mapper},this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Delete delete = method.getAnnotation(Delete.class);
        if(delete!=null){
            String sql = delete.value();
            sql = sql.replaceAll("#\\{\\w+\\}", "?");
            Class.forName("oracle.jdbc.OracleDriver");
            Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "blog",
                    "123456");
            PreparedStatement pstm = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                pstm.setObject(i+1,args[i]);
            }
            int i = pstm.executeUpdate();
            return i;
        }
        return null;
    }
}

public interface UserDao {

    @Delete("delete from t_admin where id=#{id}")
    int delete(int id);
}

public class Test {

    public static void main(String[] args) {
        String sql = "delete from t_admin where  name=#{name} and id=#{id}";
        String s = sql.replaceAll("#\\{\\w+\\}", "?");
        System.out.println(s);
        MapperProxy mapperProxy=new MapperProxy();
        UserDao mapper = (UserDao) mapperProxy.getMapper(UserDao.class);
        int delete = mapper.delete(21);
    }
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末筐乳,一起剝皮案震驚了整個濱河市歌殃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蝙云,老刑警劉巖氓皱,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異勃刨,居然都是意外死亡波材,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門身隐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來廷区,“玉大人,你說我怎么就攤上這事贾铝∠肚幔” “怎么了?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵垢揩,是天一觀的道長玖绿。 經(jīng)常有香客問我,道長叁巨,這世上最難降的妖魔是什么斑匪? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮锋勺,結果婚禮上蚀瘸,老公的妹妹穿的比我還像新娘。我一直安慰自己宙刘,他們只是感情好苍姜,可當我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著悬包,像睡著了一般衙猪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上布近,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天垫释,我揣著相機與錄音,去河邊找鬼撑瞧。 笑死棵譬,一個胖子當著我的面吹牛,可吹牛的內容都是我干的预伺。 我是一名探鬼主播订咸,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼曼尊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了脏嚷?” 一聲冷哼從身側響起骆撇,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎父叙,沒想到半個月后神郊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡趾唱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年涌乳,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甜癞。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡夕晓,死狀恐怖,靈堂內的尸體忽然破棺而出带欢,到底是詐尸還是另有隱情运授,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布乔煞,位于F島的核電站,受9級特大地震影響柒室,放射性物質發(fā)生泄漏渡贾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一雄右、第九天 我趴在偏房一處隱蔽的房頂上張望空骚。 院中可真熱鬧,春花似錦擂仍、人聲如沸囤屹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肋坚。三九已至,卻和暖如春肃廓,著一層夾襖步出監(jiān)牢的瞬間智厌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工盲赊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留铣鹏,地道東北人。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓哀蘑,卻偏偏與公主長得像诚卸,于是被迫代替她去往敵國和親葵第。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內容