概念
按照代理的創建時期,代理類可以分爲兩種:
靜態:由程序員創建代理類或特定工具自動生成源代碼再對其編譯。在程序運行前代理類的.class文件就已經存在了。
動態:在程序運行時運用反射機制動態創建而成。
靜態代理
先來看看靜態代理的實現吧?
/**
* @ClassName Star
* @Description 公共接口
* @Author lzq
* @Date 2019/8/1 10:39
* @Version 1.0
**/
public interface Star {
public void sell(); //賣商品
}
/**
* @ClassName RealStar
* @Description 真實對象 委託類
* @Author lzq
* @Date 2019/8/1 10:39
* @Version 1.0
**/
public class RealStar implements Star{
@Override
public void sell() {
System.out.println("賣商品");
}
}
/**
* @ClassName StarProxy
* @Description 代理類
* @Author lzq
* @Date 2019/8/1 10:38
* @Version 1.0
**/
public class StarProxy implements Star{
private RealStar realStar = new RealStar();
@Override
public void sell() {
realStar.sell();
}
}
測試代碼,我們只需要和委託類交互即可,不必管它的底層實現:
/**
* @ClassName Test
* @Description 測試
* @Author lzq
* @Date 2019/8/1 11:08
* @Version 1.0
**/
public class Test {
public static void main(String[] args) {
StarProxy proxy = new StarProxy();
proxy.sell();
}
}
運行結果:
賣商品
其實,動態代理也是這麼個原理,只不過它的創建對象的方式是通過反射來完成的,
動態代理
在Java中,實現動態代理的技術有很多,比如JDK自帶的、CGLIB、Javassist、ASM等,其中最常用的就是JDK、CGLIB,下面重點解釋這兩種;
JDK動態代理
上面我們用靜態代理的時候,是定義一個公共接口,然後讓委託類和代理類分別去實現這個接口(相當於後面說的,代理對象和真實對象都是掛在在這個公共接口下面的),最後在代理類裏面定義一個委託類的對象,通過底層去調用委託類的相應的方法去實現代理邏輯的,在動態代理裏面也是一樣,也需要一個公共接口,一個已經實現了的委託類,但是不同的是,它的代理類是根據接口、委託類動態生成的;
首先我們定義一個接口HelloWorld:
/**
* @ClassName HelloWorld
* @Description 公共接口
* @Author lzq
* @Date 2019/8/1 11:26
* @Version 1.0
**/
public interface HelloWorld {
public void sayHelloWorld();
}
定義一個委託類實現這個接口:
/**
* @ClassName HelloWorldImpl
* @Description 委託類 真實對象類
* @Author lzq
* @Date 2019/8/1 11:27
* @Version 1.0
**/
public class HelloWorldImpl implements HelloWorld{
@Override
public void sayHelloWorld() {
System.out.println("Hello World");
}
}
在JDK動態代理中,要實現代理邏輯必須去實現java.lang.reflect.InvocationHandler
接口,它裏面定義了一個invoke方法,並提供接口數組用於掛在代理對象,下面這個類主要實現動態代理綁定和代理邏輯實現:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
1. @ClassName JDKProxyExample
2. @Description 動態代理邏輯代碼
3. @Author lzq
4. @Date 2019/8/1 11:28
5. @Version 1.0
**/
public class JDKProxyExample implements InvocationHandler {
private Object object = null; //真實對象 委託類對象
/**
* 獲取代理對象
* @param o
* @return
*/
public Object bind(Object o) {
this.object = o;
return Proxy.newProxyInstance(object.getClass().getClassLoader(),o.getClass().getInterfaces(),this);
}
/**
* 代理方法邏輯
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("進入代理邏輯前的處理...");
System.out.println("調用真實對象前的服務...");
Object obj = method.invoke(object,args);
System.out.println("調用真實對象之後的服務...");
return obj;
}
}
在這個類裏面,我們需要:
1、建立代理對象和真實對象之間的關係
在這個類裏面,我們是提供bind方法實現的,方法裏面首先用類的屬性object保存了真實對象,然後通過如下代碼建立並生成代理對象:
Proxy.newProxyInstance(object.getClass().getClassLoader(),o.getClass()
.getInterfaces(),this);
這三個參數的意思是:
- 第一個是類加載器,就是要加載這個代理對象用哪個類加載器加載,這裏我們用的是加載真實對象的類的類加載器;
- 第二個是把生成的代理對象下掛在哪些接口下面,這個寫法就是放在object實現的接口下,HelloWorldImpl對象的接口顯然就是HelloWorld;
- 第三個是定義實現方法邏輯的代理類,this表示當前對象,它必須實現InvocationHandler接口中的invoke方法,它就是代理邏輯方法的實現方法;
2、實現代理邏輯方法
invoke方法可以實現代理邏輯,它的三個參數含義如下:
- proxy,代理對象,就是bind方法生成的對象;
- method,當前調度的方法;
- args,調度方法的參數;
當我們使用代理對象調度方法之後,它就會進入到invoke方法裏面:
Object obj = method.invoke(object,args);
//method.invoke(" 要調用的方法的名字所隸屬的對象實體",方法的參數值);
這行代碼相當於調度真實對象的的方法,只不過是通過發射實現而已。
這些各部分的關係就相當於:
- proxy 相當於商務對象,代理對象的意思;
- object 相當於軟件工程師對象,真實對象,委託者;
- bind 這個方法就是建立商務和軟件工程師代理關係的方法;
- invoke 這就是商務邏輯,它將控制軟件工程師的訪問;
測試代碼如下:
/**
* @ClassName Test
* @Description 測試代碼
* @Author lzq
* @Date 2019/8/1 11:32
* @Version 1.0
**/
public class Test {
public static void main(String[] args) {
JDKProxyExample jdk = new JDKProxyExample();
HelloWorld proxy = (HelloWorld)jdk.bind(new HelloWorldImpl());
proxy.sayHelloWorld();
}
}
那麼其實動態代理的好處在哪呢?因爲它的代理對象都是通過反射動態生成的,如果需要新的功能,那麼只需要在公共接口中添加方法,真實對象實現該方法即可,動態代理綁定和代理邏輯根本就不需要動;
測試代碼運行結果:
CGLIB動態代理
JDK動態代理必須提供公共接口才可以使用,在一些不能提供接口的環境中,只能採用其他的第三方技術,比如CGLIB動態代理,它的優勢在於不需要提供公共接口,只要一個非抽象類就能實現動態代理;
CGLIB依賴:
<!--CGlib動態代理-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.2</version>
</dependency>
代碼如下:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @ClassName CglibProxyExample
* @Description
* @Author lzq
* @Date 2019/8/1 12:12
* @Version 1.0
**/
public class CglibProxyExample implements MethodInterceptor {
/**
* 生成CGLIB代理對象
* @param cls
* @return
*/
public Object getProxy(Class cls) {
//CGLIB Enhancer 增強類對象
Enhancer enhancer = new Enhancer();
//設置增強類型
enhancer.setSuperclass(cls);
//帶你一代理邏輯對象爲當前對象,這就要求當前對象實現MethodInterceptor的方法intercept
enhancer.setCallback(this);
//生成並返回代理對象
return enhancer.create();
}
/**
* 代理邏輯方法
* @param o 代理對象
* @param method 方法
* @param objects 方法參數
* @param methodProxy 方法代理
* @return 代理邏輯返回
* @throws Throwable 異常
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("調用真實對象之前的處理邏輯...");
Object result= methodProxy.invokeSuper(o,objects);
System.out.println("調用真實對象之後的處理邏輯");
return result;
}
}
這裏用到了CGLIB的加強者類Enhancer,通過設置超類的方法(setSuperclass),然通過setCallback方法設置哪個類爲它的代理類,其中,參數this就意味着當前對象,那就要求this這個對象實現接口MethodInterceptor的方法——intercept,然後返回代理對象;
那麼此時當前類的intercept方法就是代理邏輯方法,其參數含義見代碼註解;
再寫一個真實對象類;
/**
* @ClassName Test
* @Description 委託類 真實對象類
* @Author lzq
* @Date 2019/8/1 12:28
* @Version 1.0
**/
public class Test {
public void say(String x) {
System.out.println(x);
}
}
測試代碼:
public static void main(String[] args) {
CglibProxyExample cpe = new CglibProxyExample();
Test test = (Test)cpe.getProxy(Test.class);
test.say("你好");
}
運行結果:
動態代理的實現其實都很相似,它們都是用getProxy方法生成代理對象,制定代理的邏輯類,而代理邏輯類要實現一個接口的一個方法,那麼這個接口定義的方法就是代理邏輯方法,它可以控制真實對象的方法;
JDK動態代理和CGLIB動態代理的區別:
在JDK的動態代理裏面,我們可以看到:
那它生成的代理對象和真實對象是同級的,都是公共接口下的實現類,它們是橫向關係;
再來看看CGLIB的:
它的原理是生成一個代理對象來攔截真實對象的方法,但這個代理對象卻是真實對象的子類,繼承關係,所以在CGLIB裏面,代理對象是真實對象的子類,它們是縱向關係;