# 代理模式(Proxy)
代理模式主要用來控制對其他對象的訪問,代理通常充當中間人的身份。一般可以分為如下4類:
1. 遠程代理(Remote Proxy):控制對遠程對象的訪問,負責將請求及其參數編碼,并向不同地址空間中的對象發送已經編碼的請求。
2. 虛擬代理(Virtual Proxy)
3. 保護代理
4. 智能代理
java中的代理模式:
1. 靜態代理
Java的靜態代理是基于接口的代理模式,可以用于在不修改原來對象的代碼的基礎上,擴充對象的功能。例如:
~~~
package net.smrobot.proxy;
/**
* 靜態代理
*/
public class SimpleProxyDemo {
/**
* 傳遞一個接口參數
* @param iface
*/
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("zhangsan");
}
public static void main(String[] args) {
System.out.println("沒有使用代理模式....");
consumer(new RealObject());
System.out.println("使用了代理模式...");
consumer(new SimpleProxy(new RealObject()));
}
}
interface Interface {
void doSomething();
void somethingElse(String arg);
}
class RealObject implements Interface {
@Override
public void doSomething() {
System.out.println("Real Object:doSomething!");
}
@Override
public void somethingElse(String arg) {
System.out.println("Real Object:somethingElse=" + arg);
}
}
/**
* 創建靜態代理類
*/
class SimpleProxy implements Interface {
private Interface proxied;
public SimpleProxy(Interface proxied) {
this.proxied = proxied;
}
@Override
public void doSomething() {
System.out.println("Proxy Object:doSomething!");
proxied.doSomething();
}
@Override
public void somethingElse(String arg) {
System.out.println("Proxy Object:somethingElse!");
proxied.somethingElse(arg);
}
}
~~~
2. 動態代理
Java提供的動態代理可以動態的創建代理對象,對所代理的方法進行調用,在動態代理類上的所有調用都會被重定向到一個單一的調用處理器(Handler)上。因此使用Java的動態代理技術需要如下元素:
1. 要代理的對象是**基于接口**實現的。
2. 創建調用處理器,并實現InvocationHandler接口中的invoke方法。
3. 通過Proxy.newProxyInstance()來動態的創建一個代理對象。調用該代理對象的方法,就會將方法的調用重定向到處理器的invoke方法中。
例如:
~~~
package net.smrobot.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 動態代理
*/
public class DynamicProxyDemo {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("zhangsan");
}
public static void main(String[] args) {
RealObject realObject = new RealObject();
consumer(realObject);
System.out.println("使用動態代理并再次調用...");
Interface proxyInstance = (Interface)Proxy.newProxyInstance(Interface.class.getClassLoader(),
new Class[]{Interface.class}, new DynamicProxyHandler(realObject));
consumer(proxyInstance);
}
}
class DynamicProxyHandler implements InvocationHandler {
/**
* 要代理的對象
*/
private Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("\n");
System.out.println("proxy:" + proxy.getClass() + "\nmethod:" + method + "\nargs:" + args);
if (args != null) {
System.out.println("方法的參數為....");
for (Object arg : args) {
System.out.println(" " + arg);
}
}
return method.invoke(proxied, args);
}
}
~~~
JDK的動態代理是基于反射技術實現的,還有基于字節碼技術實現ASM或者cglib。
**Cglib的動態代理技術**
JDK的動態代理只能代理基于接口的實現類,否則的話會拋出“ClassCastException異常”。而Cglib的動態代理技術可以實現基于類的動態代理,其主要生成了一個目標類的子類對象來覆蓋掉目標類方法,因此如果目標類是用final修飾,則不可以使用Cglib技術。
1. 引入依賴
~~~
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.9</version>
</dependency>
~~~
2. 實現
~~~
package net.smrobot.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Cglib的動態代理模式
*/
public class CglibProxyDemo {
public static void main(String[] args) {
// CGLIB dynamic proxy call
CglibProxy proxy = new CglibProxy();
Panda panda = (Panda)proxy.getInstance(new Panda());
System.out.println(panda);
panda.eat();
}
}
class Panda {
public void eat() {
System.out.println("The panda is eating...");
}
}
class CglibProxy implements MethodInterceptor {
// 要代理的目標對象
private Object target;
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
// 設置代理對象的父類對象,就是要代理的目標對象
enhancer.setSuperclass(this.target.getClass());
// 設置回調函數
enhancer.setCallback(this);
// 創建一個代理對象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Before calling");
// 執行目標對象的代理方法
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("After calling");
return result;
}
}
~~~
主要組成:
1. 要被代理的目標對象。
2. 實現了MethodInterceptor接口的代理對象,并重寫intercept方法。
JDK動態代理和Cglib的動態代理對比:
1. JDK動態代理更可靠,同時JDK的版本升級的比較快,字節碼包(cglib)也要隨著更新。
2. Cglib有者更高的性能,同時可以用在非接口對象中。
動態代理的應用場景:包裝RPC、面向切面編程(AOP)。
【參考】
1. 《Java編程思想》動態代理。
2. https://www.fatalerrors.org/a/reflection-and-dynamic-proxy-jdk-proxy-and-cglib.html
- 第一章 Java基礎
- ThreadLocal
- Java異常體系
- Java集合框架
- List接口及其實現類
- Queue接口及其實現類
- Set接口及其實現類
- Map接口及其實現類
- JDK1.8新特性
- Lambda表達式
- 常用函數式接口
- stream流
- 面試
- 第二章 Java虛擬機
- 第一節、運行時數據區
- 第二節、垃圾回收
- 第三節、類加載機制
- 第四節、類文件與字節碼指令
- 第五節、語法糖
- 第六節、運行期優化
- 面試常見問題
- 第三章 并發編程
- 第一節、Java中的線程
- 第二節、Java中的鎖
- 第三節、線程池
- 第四節、并發工具類
- AQS
- 第四章 網絡編程
- WebSocket協議
- Netty
- Netty入門
- Netty-自定義協議
- 面試題
- IO
- 網絡IO模型
- 第五章 操作系統
- IO
- 文件系統的相關概念
- Java幾種文件讀寫方式性能對比
- Socket
- 內存管理
- 進程、線程、協程
- IO模型的演化過程
- 第六章 計算機網絡
- 第七章 消息隊列
- RabbitMQ
- 第八章 開發框架
- Spring
- Spring事務
- Spring MVC
- Spring Boot
- Mybatis
- Mybatis-Plus
- Shiro
- 第九章 數據庫
- Mysql
- Mysql中的索引
- Mysql中的鎖
- 面試常見問題
- Mysql中的日志
- InnoDB存儲引擎
- 事務
- Redis
- redis的數據類型
- redis數據結構
- Redis主從復制
- 哨兵模式
- 面試題
- Spring Boot整合Lettuce+Redisson實現布隆過濾器
- 集群
- Redis網絡IO模型
- 第十章 設計模式
- 設計模式-七大原則
- 設計模式-單例模式
- 設計模式-備忘錄模式
- 設計模式-原型模式
- 設計模式-責任鏈模式
- 設計模式-過濾模式
- 設計模式-觀察者模式
- 設計模式-工廠方法模式
- 設計模式-抽象工廠模式
- 設計模式-代理模式
- 第十一章 后端開發常用工具、庫
- Docker
- Docker安裝Mysql
- 第十二章 中間件
- ZooKeeper