1.什么是動(dòng)態(tài)代理肺稀?
代理模式 | 區(qū)別 | 局限 |
---|---|---|
靜態(tài)代理 | 編譯時(shí)生成class文件 | 代理類(lèi)實(shí)現(xiàn)的接口和方法固定 |
動(dòng)態(tài)代理 | 運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建代理類(lèi) | --- |
2.動(dòng)態(tài)代理的兩種實(shí)現(xiàn)
- JDK中的動(dòng)態(tài)代理: 通過(guò)反射類(lèi)Proxy以及InvocationHandler回調(diào)接口實(shí)現(xiàn)的,
JDK動(dòng)態(tài)代理缺點(diǎn): JDK中所要進(jìn)行動(dòng)態(tài)代理的類(lèi)必須要實(shí)現(xiàn)一個(gè)接口霜瘪,也就是說(shuō)只能對(duì)該類(lèi)所實(shí)現(xiàn)接口中定義的方法進(jìn)行代理剪个,這在實(shí)際編程中具有一定的局限性,而且使用反射的效率也并不是很高霎奢。
- CGLIB
CGLIB原理:動(dòng)態(tài)生成一個(gè)要代理類(lèi)的子類(lèi),子類(lèi)重寫(xiě)要代理的類(lèi)的所有不是final的方法饼灿。在子類(lèi)中采用方法攔截的技術(shù)攔截所有父類(lèi)方法的調(diào)用幕侠,順勢(shì)織入橫切邏輯。它比使用java反射的JDK動(dòng)態(tài)代理要快碍彭。
CGLIB底層:使用字節(jié)碼處理框架ASM橙依,來(lái)轉(zhuǎn)換字節(jié)碼并生成新的類(lèi)。不鼓勵(lì)直接使用ASM硕旗,因?yàn)樗竽惚仨殞?duì)JVM內(nèi)部結(jié)構(gòu)包括class文件的格式和指令集都很熟悉窗骑。
CGLIB優(yōu)點(diǎn):它為沒(méi)有實(shí)現(xiàn)接口的類(lèi)提供代理,為JDK的動(dòng)態(tài)代理提供了很好的補(bǔ)充漆枚。通炒匆耄可以使用Java的動(dòng)態(tài)代理創(chuàng)建代理,但當(dāng)要代理的類(lèi)沒(méi)有實(shí)現(xiàn)接口或者為了更好的性能墙基,CGLIB是一個(gè)好的選擇软族。
CGLIB缺點(diǎn):對(duì)于final方法,無(wú)法進(jìn)行代理
2. Java靜態(tài)代理實(shí)例
還是貼一下靜態(tài)代理的代碼實(shí)例作為對(duì)比
代理方法 : 購(gòu)買(mǎi)化妝品
抽象主題角色:商家
public interface Business {
public void sell(); // 賣(mài)東西
}
真實(shí)目標(biāo)類(lèi):香港化妝品商
public class HongKongBusiness implements Business{
public void sell() {
System.out.println("========我出售香港化妝品残制!價(jià)格比大陸便宜得多^-^");
ExecutorService pool = Executors.newSingleThreadExecutor();
Future<Boolean> ft = pool.submit(new Callable<Boolean>() {
public Boolean call() throws Exception {
for (int i = 0; i < 5; i++) {
System.out.print(">>");
Thread.sleep(500);
}
return true;
}
});
try {
if(ft.get()){
System.out.println("=========商品已交貨立砸,祝您購(gòu)物愉快。");
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
pool.shutdown();
}
}
代理類(lèi) : 代購(gòu)服務(wù)商
public class ProxyBusiness implements Business{
HongKongBusiness hongKongBusiness = new HongKongBusiness(); // 維持對(duì)真實(shí)目標(biāo)類(lèi)的引用
public void sell() {
System.out.println("=========我是香港代購(gòu)商家初茶,代跑腿颗祝。");
System.out.println(">>>>>以下將執(zhí)行代購(gòu)方法,我的用戶不需要具體關(guān)心,我一手包辦服務(wù)到位螺戳。");
hongKongBusiness.sell();
}
}
測(cè)試類(lèi)搁宾,用戶類(lèi) :需要代購(gòu)服務(wù)的用戶
public class UserClient {
public static void main(String[] args) {
Business proxy = new ProxyBusiness();
proxy.sell();
}
}
3. Java動(dòng)態(tài)代理實(shí)例
3.1定義接口
通過(guò)JDK實(shí)現(xiàn)的代理對(duì)象必須是一個(gè)接口的實(shí)現(xiàn)。
package com.kang.proxy;
//需要?jiǎng)討B(tài)代理的接口
public interface Subject {
public void hello(String name);
public String bye();
}
3.2倔幼、被代理的類(lèi)(實(shí)際處理業(yè)務(wù)的類(lèi))
這個(gè)類(lèi)實(shí)現(xiàn)上面定義的接口盖腿。
package com.kang.proxy;
//被代理類(lèi)
public class RealSubject implements Subject{
@Override
public void hello(String name) {
System.out.println("hello "+name);
}
@Override
public String bye() {
System.out.println("bye");
return "bye";
}
}
3.3、InvocationHandler實(shí)現(xiàn)類(lèi)
package com.kang.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//每次生成動(dòng)態(tài)代理類(lèi)對(duì)象時(shí)都需要指定一個(gè)實(shí)現(xiàn)了InvocationHandler接口的調(diào)用處理器對(duì)象
public class InvocationHandlerImpl implements InvocationHandler {
private Object subject; // 這個(gè)就是我們要代理的真實(shí)對(duì)象损同,也就是真正執(zhí)行業(yè)務(wù)邏輯的類(lèi)
public InvocationHandlerImpl(Object subject) {// 通過(guò)構(gòu)造方法傳入這個(gè)被代理對(duì)象
this.subject = subject;
}
/**
* 該方法負(fù)責(zé)集中處理動(dòng)態(tài)代理類(lèi)上的所有方法調(diào)用翩腐。 調(diào)用處理器根據(jù)這三個(gè)參數(shù)進(jìn)行預(yù)處理或分派到委托類(lèi)實(shí)例上反射執(zhí)行**/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("可以在調(diào)用實(shí)際方法前做一些事情");
System.out.println("當(dāng)前調(diào)用的方法是" + method.getName());
// 當(dāng)代理對(duì)象調(diào)用真實(shí)對(duì)象的方法時(shí),其會(huì)自動(dòng)的跳轉(zhuǎn)到代理對(duì)象關(guān)聯(lián)的handler對(duì)象的invoke方法來(lái)進(jìn)行調(diào)用
result = method.invoke(subject, args);// 需要指定被代理對(duì)象和傳入?yún)?shù)
System.out.println(method.getName() + "方法的返回值是" + result);
System.out.println("可以在調(diào)用實(shí)際方法后做一些事情");
System.out.println("------------------------");
return result;// 返回method方法執(zhí)行后的返回值
}
}
3.4 測(cè)試類(lèi)
package Proxy.dynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* Created by bruce_shan on 2018/6/13 14:41.
* Description :
*/
public class Test {
public static void main(String[] args) {
// 被代理的對(duì)象
Subject realSubject = new RealSubject();
/**
* 通過(guò)InvocationHandlerImpl的構(gòu)造器生成一個(gè)InvocationHandler對(duì)象,
* 需要傳入被代理對(duì)象作為參數(shù)
*/
InvocationHandler handler = new InvocationHandlerImpl(realSubject);
ClassLoader loader = realSubject.getClass().getClassLoader();
Class[] interfaces = realSubject.getClass().getInterfaces();
// 需要指定類(lèi)裝載器膏燃、一組接口及調(diào)用處理器生成動(dòng)態(tài)代理類(lèi)實(shí)例
Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
System.out.println("動(dòng)態(tài)代理對(duì)象的類(lèi)型:" + subject.getClass().getName());
subject.work();
}
}
4. CGlib動(dòng)態(tài)代理實(shí)例
//商人類(lèi)
public class Boss{
public void work(){
System.out.println("-------->我是香港化妝品店老板,每天開(kāi)店賣(mài)東西");
}
}
//代理類(lèi) 輸出日志
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class BossProxy implements MethodInterceptor {
//通過(guò)Enhancer 創(chuàng)建代理對(duì)象
private Enhancer enhancer = new Enhancer();
//通過(guò)Class對(duì)象獲取代理對(duì)象
public Object getProxy(Class c){
//設(shè)置創(chuàng)建子類(lèi)的類(lèi)
enhancer.setSuperclass(c);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) throws Throwable {
// TODO Auto-generated method stub
System.out.println("日志開(kāi)始...");
//代理類(lèi)調(diào)用父類(lèi)的方法
proxy.invokeSuper(obj, args);
System.out.println("日志結(jié)束...");
return null;
}
}
public class TestCgibl {
public static void main(String[] args) {
//創(chuàng)建我們的代理類(lèi)
BossProxy Proxy = new BossProxy();
Boss boss = (Boss)Proxy.getProxy(Boss.class);
boss.work();
}
}