1. 前情內(nèi)容
2. 目錄
3. 概念
百度百科對(duì)它的定義是:對(duì)其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。
代理模式更多的是一種安全層面的考慮,保護(hù)被訪問(wèn)對(duì)象的安全,在訪問(wèn)者和被訪問(wèn)者之間起一種中介作用。在我們具體應(yīng)用中都會(huì)遇到需要記錄掌控方法的執(zhí)行前后的動(dòng)作。
從生成方式的不通又在代理中分為靜態(tài)代理、動(dòng)態(tài)代理以及Cglib代理。
4. 靜態(tài)代理
代理類(lèi)在程序運(yùn)行前就已經(jīng)定義好,其與目標(biāo)類(lèi)(被代理類(lèi))的關(guān)系在程序運(yùn)行前就已經(jīng)確立,屬于事前約定的范疇。這一點(diǎn)非常類(lèi)似于企業(yè)與企業(yè)法律顧問(wèn)間的關(guān)系,他們之間的代理關(guān)系,并不是在“官司“發(fā)生后才建立的,而是之前就確立好的一種關(guān)系。在這一點(diǎn)上動(dòng)態(tài)代理可以理解為官司已經(jīng)開(kāi)始,才臨時(shí)聘請(qǐng)熟諳法律的律師。
package xyz.wongs.weathertop.design.proxy;
/**
* @ClassName Loginable
* @Description 定義接口
* @author WCNGS@QQ.COM
* @Github <a>https://github.com/rothschil</a>
* @date 2019/12/27 15:31
* @Version 1.0.0
*/
public interface Loginable {
void login();
}
package xyz.wongs.weathertop.design.proxy.stat;
import lombok.extern.slf4j.Slf4j;
import xyz.wongs.weathertop.design.proxy.Loginable;
import java.util.Random;
/**
* @ClassName LoginService
* @Description 登陸實(shí)現(xiàn)類(lèi)
* @author WCNGS@QQ.COM
* @Github <a>https://github.com/rothschil</a>
* @date 2019/12/27 15:32
* @Version 1.0.0
*/
@Slf4j
public class LoginService implements Loginable {
@Override
public void login(){
try {
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
log.error("登陸成功");
}
}
package xyz.wongs.weathertop.design.proxy.stat;
import lombok.extern.slf4j.Slf4j;
import xyz.wongs.weathertop.design.proxy.Loginable;
/**
* @ClassName LoginServiceProxy
* @Description 實(shí)際代理類(lèi)
* @author WCNGS@QQ.COM
* @Github <a>https://github.com/rothschil</a>
* @date 2019/12/27 15:32
* @Version 1.0.0
*/
@Slf4j
public class LoginServiceProxy implements Loginable {
private Loginable loginable;
public LoginServiceProxy() {
}
public LoginServiceProxy(Loginable loginable) {
this.loginable = loginable;
}
@Override
public void login() {
long start = System.currentTimeMillis();
log.error("start proxy : " + start);
loginable.login();
long end = System.currentTimeMillis();
log.error("end proxy : " + end);
log.error("spend all time : " + (end - start) + " ms.");
}
}

上面的一個(gè)登陸例子中,我們通過(guò)代理對(duì)象來(lái)獲取我們想要的目標(biāo)(LoginService),外界并不知道目標(biāo)是如何實(shí)現(xiàn)的,無(wú)形之中保護(hù)了真正的意圖。
小結(jié)一下:
靜態(tài)代理對(duì)象(LoginServiceProxy)有兩個(gè)特征:
它內(nèi)部包含著被代理對(duì)象(Loginable)實(shí)現(xiàn)接口的引用。
它又實(shí)現(xiàn)了被代理對(duì)象(Loginable)實(shí)現(xiàn)的接口。
這兩個(gè)特征既是特點(diǎn)同時(shí)也帶有劣勢(shì),就是代理的對(duì)象必須提前知曉,當(dāng)被代理對(duì)象發(fā)生變更,相應(yīng)的代理對(duì)象也要跟著變更,還是有點(diǎn)不便捷。那有沒(méi)有一種手段可在運(yùn)行過(guò)程中動(dòng)態(tài)地產(chǎn)生一個(gè)代理對(duì)象,非但再也不用維護(hù)代理類(lèi),更提高整體的效率。答案是有的,這種手段就是下來(lái)的動(dòng)態(tài)代理。
5. 動(dòng)態(tài)代理
JAVA中的AOP切面就是基于動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)的,但是它用到了兩種代理模式分別為jdk動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理,當(dāng)然這兩種代理各有優(yōu)劣,這一點(diǎn)我們?cè)谧詈髸?huì)統(tǒng)一總結(jié)。
5.1. JDK動(dòng)態(tài)代理
在靜態(tài)代理的模塊上做一下改造,沿用接口和接口實(shí)現(xiàn)部分,新增一個(gè)
5.1.1. 實(shí)現(xiàn)
package xyz.wongs.weathertop.design.proxy.dyc.jdk;
import lombok.extern.slf4j.Slf4j;
import xyz.wongs.weathertop.design.proxy.Loginable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@Slf4j
public class LoginServiceDycProxy implements InvocationHandler {
private Loginable loginable;
public Object getInstall(Loginable loginable){
this.loginable = loginable;
return Proxy.newProxyInstance(loginable.getClass().getClassLoader(),loginable.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.error("Begin 執(zhí)行方法前的操作");
if(method.getName().equalsIgnoreCase("login")){
loginable.login();
}
log.error("End 執(zhí)行方法后的操作");
return null;
}
}
5.1.2. 演示結(jié)果
@Test
public void testDycProxy(){
LoginServiceDycProxy lsdp = new LoginServiceDycProxy();
Loginable loginable = (Loginable)lsdp.getInstall(new LoginService());
loginable.login();
}
17:48:57.793 [main] ERROR xyz.wongs.weathertop.design.proxy.dyc.jdk.LoginServiceDycProxy - Begin 執(zhí)行方法前的操作
17:49:00.594 [main] ERROR xyz.wongs.weathertop.design.proxy.LoginService - 登陸成功
17:49:00.594 [main] ERROR xyz.wongs.weathertop.design.proxy.dyc.jdk.LoginServiceDycProxy - End 執(zhí)行方法后的操作
5.2. cglib代理
5.2.1. 引入依賴(lài)包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
5.2.2. 代理類(lèi)
package xyz.wongs.weathertop.design.proxy.dyc.cglib;
import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
@Slf4j
public class LoginServiceDycProxyCglib implements MethodInterceptor {
public Object getInstall(Object object){
return Enhancer.create(object.getClass(), this);
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
log.error("Begin 執(zhí)行方法前的操作");
methodProxy.invokeSuper(o,objects);
log.error("End 執(zhí)行方法后的操作");
return null;
}
}
5.2.3. 測(cè)試
@Test
public void testDycProxyCglib(){
LoginServiceDycProxyCglib lsdp = new LoginServiceDycProxyCglib();
Loginable loginable = (Loginable)lsdp.getInstall(new LoginService());
loginable.login();
}
5.2.4. 演示結(jié)果
18:00:53.993 [main] ERROR xyz.wongs.weathertop.design.proxy.dyc.cglib.LoginServiceDycProxyCglib - Begin 執(zhí)行方法前的操作
18:00:57.787 [main] ERROR xyz.wongs.weathertop.design.proxy.LoginService - 登陸成功
18:00:57.787 [main] ERROR xyz.wongs.weathertop.design.proxy.dyc.cglib.LoginServiceDycProxyCglib - End 執(zhí)行方法后的操作
5.3. jdk代理與cglib代理比較
實(shí)現(xiàn)機(jī)制:jdk動(dòng)態(tài)代理是由java內(nèi)部的反射機(jī)制來(lái)實(shí)現(xiàn)的,cglib動(dòng)態(tài)代理底層則是借助asm來(lái)實(shí)現(xiàn)的;
效率上:反射機(jī)制在類(lèi)的生成過(guò)程中比較高效,而asm機(jī)制在類(lèi)生成之后的執(zhí)行過(guò)程中比較高效,當(dāng)然也有可以通過(guò)將asm生成的類(lèi)接入緩存,這樣也可以解決asm生成類(lèi)過(guò)程低效問(wèn)題;
應(yīng)用上: jdk動(dòng)態(tài)代理的應(yīng)用需要依賴(lài)目標(biāo)類(lèi)均基于統(tǒng)一的接口,而cglib則無(wú)限制;
綜上所述,我們?cè)趯?shí)際過(guò)程中基于第三方庫(kù)實(shí)現(xiàn)的動(dòng)態(tài)代理應(yīng)用在綜合效率上更有優(yōu)勢(shì)。