代理
靜態(tài)代理
- 編寫需要目標(biāo)對象實現(xiàn)的接口
/**
* 接口
*/
public interface Action {
/**
* dosomething
*/
void doSomething();
}
- 編寫目標(biāo)類恩掷,實現(xiàn)接口
/**
* @author earthchen
* @date 2018/8/10
**/
public class RealObject implements Action {
@Override
public void doSomething() {
System.out.println(this.getClass().getName()+"dosomething....");
}
}
- 編寫靜態(tài)代理類寞酿,也需要實現(xiàn)該接口
/**
* 代理類
*
* @author earthchen
* @date 2018/8/10
**/
public class Proxy implements Action {
private Action realObject;
public Proxy(Action realObject) {
this.realObject = realObject;
}
@Override
public void doSomething() {
System.out.println("proxy do");
realObject.doSomething();
}
}
需要維護(hù)一個接口的對象
- 編寫main方法進(jìn)行測試
public class Main {
public static void main(String[] args) {
Action action = new RealObject();
Proxy proxy = new Proxy(action);
proxy.doSomething();
}
}
優(yōu)缺點(diǎn)
這種代理方式需要代理對象和目標(biāo)對象實現(xiàn)一樣的接口。
優(yōu)點(diǎn):可以在不修改目標(biāo)對象的前提下擴(kuò)展目標(biāo)對象的功能雌澄。
缺點(diǎn):
- 冗余伊者。由于代理對象要實現(xiàn)與目標(biāo)對象一致的接口,會產(chǎn)生過多的代理類胁赢。
- 不易維護(hù)企蹭。一旦接口增加方法,目標(biāo)對象與代理對象都要進(jìn)行修改智末。
jdk動態(tài)代理
- 和靜態(tài)代理一樣谅摄,編寫一個接口
/**
* @author earthchen
* @date 2018/8/10
**/
public interface Action {
void doSomeThing();
}
- 編寫多個目標(biāo)類,實現(xiàn)接口
/**
* @author earthchen
* @date 2018/8/10
**/
public class RealObject1 implements Action {
@Override
public void doSomeThing() {
System.out.println(this.getClass().getName()+"do someThing...");
}
}
/**
* @author earthchen
* @date 2018/8/10
**/
public class RealObject2 implements Action {
@Override
public void doSomeThing() {
System.out.println(this.getClass().getName()+"do someThing...");
}
}
- 編寫動態(tài)代理類系馆,
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author earthchen
* @date 2018/8/10
**/
public class DynamicProxyHandler implements InvocationHandler {
private Action testAction;
public DynamicProxyHandler(Action testAction) {
this.testAction = testAction;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理擴(kuò)展邏輯
System.out.println("proxy do");
return method.invoke(testAction, args);
}
}
- 編寫main方法進(jìn)行測試
public class Main {
public static void main(String[] args) {
RealObject1 realObject1 = new RealObject1();
Action proxy1 = (Action) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{Action.class}, new DynamicProxyHandler(realObject1));
proxy1.doSomeThing();
RealObject2 realObject2 = new RealObject2();
Action proxy2 = (Action) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{Action.class}, new DynamicProxyHandler(realObject2));
proxy2.doSomeThing();
}
}
靜態(tài)代理與動態(tài)代理的區(qū)別
- 靜態(tài)代理在編譯時就已經(jīng)實現(xiàn)送漠,編譯完成后代理類是一個實際的class文件
- 動態(tài)代理是在運(yùn)行時動態(tài)生成的,即編譯完成后沒有實際的class文件由蘑,而是在運(yùn)行時動態(tài)生成類字節(jié)碼闽寡,并加載到JVM中
特點(diǎn):
動態(tài)代理對象不需要實現(xiàn)接口,但是要求目標(biāo)對象必須實現(xiàn)接口尼酿,否則不能使用動態(tài)代理爷狈。
cglib代理
- 在pom.xml中引入cglib的依賴
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
- 直接編寫需要代理的目標(biāo)類
public class RealObject {
public void doSomeThing(){
System.out.println(this.getClass().getName()+"do someThing...");
}
}
- 編寫cglib代理類
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* @author earthchen
* @date 2018/8/10
**/
public class CglibProxy implements MethodInterceptor {
private Object target;
public CglibProxy(Object object) {
this.target = object;
}
/**
* 為目標(biāo)對象生成代理對象
*
* @return
*/
public Object getProxyInstance() {
//工具類
Enhancer en = new Enhancer();
//設(shè)置父類
en.setSuperclass(target.getClass());
//設(shè)置回調(diào)函數(shù)
en.setCallback(this);
//創(chuàng)建子類對象代理
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before do someThing.....");
// 執(zhí)行目標(biāo)對象的方法
Object returnValue = method.invoke(target, objects);
System.out.println("after do someThing....");
return returnValue;
}
}
cglib與動態(tài)代理的區(qū)別
- 使用動態(tài)代理的對象必須實現(xiàn)一個或多個接口
- 使用cglib代理的對象則無需實現(xiàn)接口,達(dá)到代理類無侵入裳擎。
cglib特點(diǎn)
- JDK的動態(tài)代理有一個限制涎永,就是使用動態(tài)代理的對象必須實現(xiàn)一個或多個接口。
如果想代理沒有實現(xiàn)接口的類鹿响,就可以使用CGLIB實現(xiàn) - CGLIB是一個強(qiáng)大的高性能的代碼生成包羡微,它可以在運(yùn)行期擴(kuò)展Java類與實現(xiàn)Java接口。它廣泛的被許多AOP的框架使用惶我,例如Spring AOP和dynaop妈倔,為他們提供方法的interception(攔截)
- CGLIB包的底層是通過使用一個小而快的字節(jié)碼處理框架ASM,來轉(zhuǎn)換字節(jié)碼并生成新的類绸贡。不鼓勵直接使用ASM盯蝴,因為它需要你對JVM內(nèi)部結(jié)構(gòu)包括class文件的格式和指令集都很熟悉
總結(jié)
- 靜態(tài)代理實現(xiàn)較簡單毅哗,只要代理對象對目標(biāo)對象進(jìn)行包裝,即可實現(xiàn)增強(qiáng)功能结洼,但靜態(tài)代理只能為一個目標(biāo)對象服務(wù)黎做,如果目標(biāo)對象過多叉跛,則會產(chǎn)生很多代理類松忍。
- JDK動態(tài)代理需要目標(biāo)對象實現(xiàn)業(yè)務(wù)接口,代理類只需實現(xiàn)InvocationHandler接口筷厘。
- 靜態(tài)代理在編譯時產(chǎn)生class字節(jié)碼文件鸣峭,可以直接使用,效率高酥艳。
- 動態(tài)代理必須實現(xiàn)InvocationHandler接口摊溶,通過反射代理方法,比較消耗系統(tǒng)性能充石,但可以減少代理類的數(shù)量莫换,使用更靈活。
- cglib代理無需實現(xiàn)接口骤铃,通過生成類字節(jié)碼實現(xiàn)代理拉岁,比反射稍快,不存在性能問題惰爬,但cglib會繼承目標(biāo)對象喊暖,需要重寫方法,所以目標(biāo)對象不能為final類撕瞧。