原文鏈接http://zhhll.icu/2020/11/12/java%E5%9F%BA%E7%A1%80/%E5%8F%8D%E5%B0%84/%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86/
動態代理
動態代理有很多種方式,如jdk代理,cglib,ASM等
在說動態代理之前先說一下靜態代理
靜態代理
靜態代理在使用時,需要定義介面或者父類,被代理物件和代理物件一起實作相同的介面或者繼承相同的父類
靜態代理使用的是組合模式,在代理類中包含有被代理類的物件
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工廠生產了一件Nike");
}
}
// 代理類
class ProxyFactory implements ClothFactory{
private ClothFactory clothFactory;
public ProxyFactory(ClothFactory clothFactory){
this.clothFactory = clothFactory;
}
@Override
public void productCloth() {
System.out.println("代理類開始執行,準備呼叫被代理類");
clothFactory.productCloth();
}
}
靜態代理雖然可以在不修改目標物件功能的前提下對目標功能進行擴展,但是一旦介面增加方法,目標物件和代理類都要同時修改,而且代理物件和被代理物件要實作一樣的介面,導致有很多的代理類,不便于維護
jdk動態代理
jdk動態代理的底層是用的是java的反射,但是jdk代理的前提是目標類必須實作介面
使用步驟
- 首先實作一個InvocationHandler,方法呼叫會被轉發到該類的invoke()方法
- 呼叫時通過動態代理獲取代理物件
核心方法為Proxy.newProxyInstance(ClassLoader,Class[],InvocationHandler)
三個引數分別表示
- ClassLoader 當前目標物件使用的類加載器
- Class[] 目標物件實作的介面型別
- InvocationHandler 事件處理,執行目標物件的方法時,會觸發事件處理器的方法,會把當前執行目標物件的方法作為引數傳入
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();
}
// 真正執行的方法,被代理類
class RealSubject implements Subject{
@Override
public void action() {
System.out.println("被代理類開始執行");
}
}
class MyInvationHandler implements InvocationHandler{
// 實作了介面的被代理類的物件的宣告
Object obj;
// 被代理類的實體
// 回傳一個代理類的物件
public Object blind(Object obj){
this.obj = obj;
// ①使用被代理類的類加載器②被代理類的介面③代理類的實體
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
}
/**
* 當通過代理類的物件被重寫的方法呼叫時,都會轉換為對invoke方法的呼叫
* @param proxy 正在回傳的代理物件,一般情況下,在invoke方法中不使用該物件
* @param method 正在被呼叫的方法
* @param args 呼叫方法時,傳入的引數
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(obj,args);
}
}
cglib代理
靜態代理和jdk動態代理都妖氣目標物件一定要實作介面,但是有時候目標物件只是一個單獨的物件,并沒有實作任何介面,這個時候采用以目標物件子類的方式實作代理,該方法稱為cglib代理
Cglib包的底層是通過使用一個位元組碼處理框架ASM來轉換位元組碼并生成新的類,由于要生成子類,所以要被代理的類不可以被final修飾
需要引入cglib的包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
使用步驟
- 首先實作MethodIntercepor,方法呼叫會被轉發到intercept()方法
- 使用CGLIB來獲取代理物件
public class CglibProxy implements MethodInterceptor {
public Object getProxy(Class clazz){
Enhancer enhancer = new Enhancer();
// 指定代理類的父類
enhancer.setSuperclass(clazz);
// 設定Callback物件
enhancer.setCallback(this);
// 通過位元組碼技術動態創建子類實體
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置處理");
// 呼叫MethodProxy.invokeSuper方法將呼叫轉發給原始物件
Object result = methodProxy.invokeSuper(o,objects);
System.out.println("后置處理");
return result;
}
}
public class Test {
public void print(){
System.out.println("方法執行");
}
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
Test proxyImp = (Test) proxy.getProxy(Test.class);
proxyImp.print();
}
}
由于本身的博客百度沒有收錄,博客地址http://zhhll.icu
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/248859.html
標籤:Java
