動態(tài)代理
動態(tài)代理有很多種方式,如jdk代理,cglib脯燃,ASM等
在說動態(tài)代理之前先說一下靜態(tài)代理
靜態(tài)代理
靜態(tài)代理在使用時蒙保,需要定義接口或者父類,被代理對象和代理對象一起實(shí)現(xiàn)相同的接口或者繼承相同的父類
靜態(tài)代理使用的是組合模式邓厕,在代理類中包含有被代理類的對象
public class TestStaticProxy {
public static void main(String[] args) {
NikeClothFactory nikeClothFactory = new NikeClothFactory();
ProxyFactory proxyFactory = new ProxyFactory(nikeClothFactory);
proxyFactory.productCloth();
}
}
// 接口
interface ClothFactory{
void productCloth();
}
// 被代理類
class NikeClothFactory implements ClothFactory{
@Override
public void productCloth() {
System.out.println("Nike工廠生產(chǎn)了一件Nike");
}
}
// 代理類
class ProxyFactory implements ClothFactory{
private ClothFactory clothFactory;
public ProxyFactory(ClothFactory clothFactory){
this.clothFactory = clothFactory;
}
@Override
public void productCloth() {
System.out.println("代理類開始執(zhí)行逝嚎,準(zhǔn)備調(diào)用被代理類");
clothFactory.productCloth();
}
}
靜態(tài)代理雖然可以在不修改目標(biāo)對象功能的前提下對目標(biāo)功能進(jìn)行擴(kuò)展,但是一旦接口增加方法详恼,目標(biāo)對象和代理類都要同時修改补君,而且代理對象和被代理對象要實(shí)現(xiàn)一樣的接口,導(dǎo)致有很多的代理類昧互,不便于維護(hù)
jdk動態(tài)代理
jdk動態(tài)代理的底層是用的是java的反射挽铁,但是jdk代理的前提是目標(biāo)類必須實(shí)現(xiàn)接口
使用步驟
- 首先實(shí)現(xiàn)一個InvocationHandler,方法調(diào)用會被轉(zhuǎn)發(fā)到該類的invoke()方法
- 調(diào)用時通過動態(tài)代理獲取代理對象
核心方法為Proxy.newProxyInstance(ClassLoader,Class[],InvocationHandler)
三個參數(shù)分別表示
- ClassLoader 當(dāng)前目標(biāo)對象使用的類加載器
- Class[] 目標(biāo)對象實(shí)現(xiàn)的接口類型
- InvocationHandler 事件處理硅堆,執(zhí)行目標(biāo)對象的方法時屿储,會觸發(fā)事件處理器的方法,會把當(dāng)前執(zhí)行目標(biāo)對象的方法作為參數(shù)傳入
public class TestDynamicProxy {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
MyInvationHandler myInvationHandler = new MyInvationHandler();
Object obj = myInvationHandler.blind(realSubject);
Subject sub = (Subject) obj;
sub.action();
}
}
// 接口
interface Subject{
void action();
}
// 真正執(zhí)行的方法渐逃,被代理類
class RealSubject implements Subject{
@Override
public void action() {
System.out.println("被代理類開始執(zhí)行");
}
}
class MyInvationHandler implements InvocationHandler{
// 實(shí)現(xiàn)了接口的被代理類的對象的聲明
Object obj;
// 被代理類的實(shí)例
// 返回一個代理類的對象
public Object blind(Object obj){
this.obj = obj;
// ①使用被代理類的類加載器②被代理類的接口③代理類的實(shí)例
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
}
/**
* 當(dāng)通過代理類的對象被重寫的方法調(diào)用時,都會轉(zhuǎn)換為對invoke方法的調(diào)用
* @param proxy 正在返回的代理對象民褂,一般情況下茄菊,在invoke方法中不使用該對象
* @param method 正在被調(diào)用的方法
* @param args 調(diào)用方法時,傳入的參數(shù)
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(obj,args);
}
}
cglib代理
靜態(tài)代理和jdk動態(tài)代理都妖氣目標(biāo)對象一定要實(shí)現(xiàn)接口赊堪,但是有時候目標(biāo)對象只是一個單獨(dú)的對象面殖,并沒有實(shí)現(xiàn)任何接口,這個時候采用以目標(biāo)對象子類的方式實(shí)現(xiàn)代理哭廉,該方法稱為cglib代理
Cglib包的底層是通過使用一個字節(jié)碼處理框架ASM來轉(zhuǎn)換字節(jié)碼并生成新的類脊僚,由于要生成子類,所以要被代理的類不可以被final修飾
需要引入cglib的包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
使用步驟
- 首先實(shí)現(xiàn)MethodIntercepor,方法調(diào)用會被轉(zhuǎn)發(fā)到intercept()方法
- 使用CGLIB來獲取代理對象
public class CglibProxy implements MethodInterceptor {
public Object getProxy(Class clazz){
Enhancer enhancer = new Enhancer();
// 指定代理類的父類
enhancer.setSuperclass(clazz);
// 設(shè)置Callback對象
enhancer.setCallback(this);
// 通過字節(jié)碼技術(shù)動態(tài)創(chuàng)建子類實(shí)例
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置處理");
// 調(diào)用MethodProxy.invokeSuper方法將調(diào)用轉(zhuǎn)發(fā)給原始對象
Object result = methodProxy.invokeSuper(o,objects);
System.out.println("后置處理");
return result;
}
}
public class Test {
public void print(){
System.out.println("方法執(zhí)行");
}
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
Test proxyImp = (Test) proxy.getProxy(Test.class);
proxyImp.print();
}
}
由于本身的博客百度沒有收錄辽幌,博客地址http://zhhll.icu