為了賬號安全,請及時綁定郵箱和手機立即綁定

第二篇:從Java基礎學習Spring AOP——JDK動態代理(重點內容)

2016.07.30 10:14 5508瀏覽

在Spring中,嚴格的來說不單單使用JDK的動態代理,還會使用CGLIB動態代理,但是它們大同小異,我們這里講JDK的動態代理,有了它的理解CGLIB也不難掌握,讀者可以閱讀其他的文檔。

這篇文章是揭示Spring AOP原理的基礎,是所有章節中的重點內容之一,初學者需要認真學習和掌握它,否則后面講解會很難理解的,一定多多敲代碼體會它,你的進步就會很大

首先,讓我們先了解什么是動態代理,假設讀者是一個軟件工程師,而你的公司是家軟件公司,我是一個客戶,我需要你公司提供軟件的專業服務。顯然我是找到你們公司的商務,而不是你去討論我的要求。那么商務就等同于你的一個代理,我也會認為商務等于你們公司,而不會去管你這個軟件工程師如何工作的。

代理的實際就是在真實服務對象(軟件工程師)之前加多一個占位或者叫做代理對象(商務),這個占位(商務)可以根據調用者(客戶)的要求去控制真實服務對象的訪問(軟件工程師)。有了這個比喻是不是好理解很多??

那么代理模式的好處在于什么?首先占位(商務)可以在真實服務對象之前之后做一些服務,同時根據需要選擇是否需要啟用真實服務對象,也會加入一些規則,比如商務也可以根據客戶和公司的規則來提供額外的服務,這時可能就連真實服務對象(軟件工程師)都不會啟用。

好,上面的東西貌似很神奇,我們用一張圖來表達代理的含義。

圖片描述

好,這里的代理對象就是我們之前談到的占位,它代理了我們看到的真實對象,可以在真實對象之前之后,甚至是代替真實對象提供服務。

動態代理有好幾種,Spring使用了CGLIG和JDK動態代理。在JDK動態代理中,要求必須提供接口,而CGLIB是不需要的,我們這里只談論JDK動態代理,在大部分的情況下,筆者建議你使用JDK動態代理,因為JDK動態代理的速度要比CGLIB要快,在Spring中一個有切面的Bean如果有接口聲明,Spring就會用JDK動態代理代理它,否者啟用CGLIB。

好了論述講了一大截,我們開始講JDK動態代理,首先我們需要提供一個簡單的接口:

package com.learn.chapter1.proxy;
/**
 *
 * @author ykzhen2015
 */
public interface HelloService {
    public void sayHello(String name);
}

跟著是實現類:

package com.learn.chapter1.proxy;

/**
 *
 * @author ykzhen2015
 */
public class HelloServiceImpl implements HelloService {

    @Override
    public void sayHello(String name) {
        System.err.println("hello " + name);
    }

}

好都很簡單,不需要筆者做任何解釋。我們跟著就是要生成代理對象(proxy),分成兩步:

  • 生成代理對象要建立代理對象(proxy)和真實對象(HelloServiceImpl)的代理關系。
  • 實現代理方法
    在JDK動態代理中需要實現接口:java.lang.reflect.InvocationHandler。讓我們先來看看它:
package com.learn.chapter1.proxy;

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

/**
 *
 * @author ykzhen2015.
 */
public class HelloProxy implements InvocationHandler {

    private Object target;

    /**
     * 生成代理對象,并和真實服務對象綁定.
     * @param target 真實服務對線下 
     * @return 代理對象
     */
    public Object bind(Object target) {
        this.target = target;
        //生成代理對象,并綁定.
        Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), //類的加載器
                target.getClass().getInterfaces(), //對象的接口,明確代理對象掛在哪些接口下
                this);//指明代理類,this代表用當前類對象,那么就要求其實現InvocationHandler接口
        return proxy;
    }

    /**
     * 當生成代理對象時,第三個指定使用HelloProxy進行代理時,代理對象調用的方法就會進入這個方法。
     * @param proxy ——代理對象
     * @param method -- 被調用的方法
     * @param args -- 方法參數
     * @return  代理方法返回。
     * @throws Throwable  -- 異常處理
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.err.println("反射真實對象方法前");
        Object obj = method.invoke(target, args);//相當于sayHello方法調用.
        System.err.println("反射真實對象方法后");
        return obj;
    }

}

好了,有了上面的代碼我們論述一下(實際注釋也很清晰),

  • 首先聲明了一個類的屬性target,它的作用是保存真實服務對象(軟件工程師)。
  • 然后用bind方法綁定代理對象(proxy 商務)和真實對象(軟件工程師),是通過這樣去綁定的:

Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), //類的加載器
target.getClass().getInterfaces(), //對象的接口,明確代理對象掛在哪些接口下
this);//指明代理類,this代表用當前類對象,那么就要求其實現InvocationHandler接口的invoke方法,而代理邏輯就放入invoke方法里。
用之前的比喻就是proxy就是商務,它代理了target這個軟件工程師,而商務代理的邏輯方法放在this這個對象的invoke方法里,只是this這個對象需要實現InvocationHandler接口而已。

  • 這樣聲明就會進入當前類invoke方法,它實現的是代理邏輯(按比喻就是商務邏輯)它有三個參數:

Object proxy ——當前代理對象(商務)
Method method —— 當前調度的方法
Object[] args -- 方法參數

然后我們通過反射調度真實對象的方法,不懂的可以看到第一篇的論述(鏈接描述
Object obj = method.invoke(target, args);//相當于sayHello方法調用.

讓我們測試一下這段代碼:

package com.learn.chapter1.main;

import com.learn.chapter1.pojo.Role;
import com.learn.chapter1.proxy.HelloProxy;
import com.learn.chapter1.proxy.HelloService;
import com.learn.chapter1.proxy.HelloServiceImpl;
import com.learn.chapter1.utils.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;

/**
 *
 * @author ykzhen2015
 */
public class Chapter1Main {

    public static void main(String[] args) {
       HelloProxy helloProxy = new HelloProxy();
       //因為使用了接口HelloService綁定了代理對象,所以可以用HelloService作為代理對象的聲明.
       HelloService proxy = (HelloService) helloProxy.bind(new HelloServiceImpl());
       proxy.sayHello("張三");//此時使用代理對象運行方法進入HelloProxy的invoke方法里
    }
}

這和時候,我們運行一下可以得到下面的打印:

反射真實對象方法前
hello 張三
反射真實對象方法后

好了,我們看到的HelloService實際已經是一個代理對象了,而不是我們普通人看到的HelloServiceImpl這個真實對象,在Spring中不要被它迷糊了哦。

這篇很重要,是我們以后的基礎,所以筆者也論述得比較詳細,希望大家好好學習,一定要多敲代碼,好好體會哦。

點擊查看更多內容

本文原創發布于慕課網 ,轉載請注明出處,謝謝合作

38人點贊

若覺得本文不錯,就分享一下吧!

評論

相關文章推薦

正在加載中
意見反饋 幫助中心 APP下載
官方微信

舉報

0/150
提交
取消
lpl竞猜