Java 代理學習筆記
阿新 • • 發佈:2022-04-19
java代理
特徵: 代理類和委託類有同樣的介面, 代理類主要為委託類預處理訊息, 過濾訊息, 吧訊息轉發給委託類, 以及處理事後訊息, 代理類的物件並不真正實現服務, 而是通過呼叫委託類的相關方法, 來提供特定的服務.
簡單的說就是,我們在訪問實際物件時,是通過代理物件來訪問的,代理模式就是在訪問實際物件時引入一定程度的間接性, 因為這種間接性我們可以附加多種用途.
代理模式
- 代理模式: 為其他物件為提供一種代理以控制對這個物件的訪問.
Super類定義了SubObject和Proxy的公用介面, 這樣在需要使用SubObject的任何地方都可以使用Proxy.
應用場景:
- 遠端代理: 遠端物件的本地代表, 本地方法呼叫這個物件會被轉發到遠端中.
- 虛擬代理: 虛擬化開銷大的物件
- 安全代理: 控制訪問許可權
- 智慧指引: 呼叫真實的物件時, 處理另外一些事
靜態代理
在編譯時期就已經將介面, 被代理類等確定下來. 在程式執行之前代理類的.class檔案就已經生成.
例子:
classDiagram class Interface{ <<interface>> void doSomthing() void doSomthingElse() } class InterfaceImplFirst{ void doSomthing() void doSomthingElse() } class InterfaceImplTwo{ void doSomthing() void doSomthingElse() } Interface <|.. InterfaceImplFirst : 實現 Interface <|.. InterfaceImplTwo : 實現 InterfaceImplFirst <-- InterfaceImplTwo : 代理Interface.class
public interface Interface {
void doSomething();
void doSomethingElse(String arg);
}
InterfaceImplFirst.class
public class InterfaceImplFirst implements Interface{ public void doSomething() { System.out.println("InterfaceImplFirst: doSomething"); } public void doSomethingElse(String arg) { System.out.println("InterfaceImplFirst: doSomethingElse " + arg); } }
InterfaceImplTwo.class
public class InterfaceImplTwo implements Interface {
private final Interface interfaceImplFirst;
public InterfaceImplTwo(Interface interfaceImplFirst){
this.interfaceImplFirst = interfaceImplFirst;
}
public void doSomething() {
interfaceImplFirst.doSomething();
System.out.println("InterfaceImplTwo: doSomething");
}
public void doSomethingElse(String arg) {
interfaceImplFirst.doSomethingElse(arg);
System.out.println("doSomethingElse: doSomethingElse " + arg);
}
}
反射
基本使用:
//獲取Class物件的三種方法:
Class<User> clazz = User.class;
Class<User> clazz = user.getClass();
Class<User> clazz = Class.forName(User.class);
//Class獲取屬性
public Field getField(String name) {...} //獲取指定名稱欄位
public Method getMethod(String name, Class<?>... parameterTypes) {...} //獲取指定名稱和簽名的方法
public Constructor<T> getConstructor(Class<?>... parameterTypes){...} //獲取指定引數列表公有構造器
public Constructor<?>[] getConstructors() throws SecurityException {...} //獲取所有公有構造器
//記載和類位於同一目錄下的資源, 通過類載入器獲取
public java.net.URL getResource(String name) {...} //返回URL形式的資源
public InputStream getResourceAsStream(String name) {...} //返回輸入流形式的資源
//Constructor構造器的操作
public void setAccessible(boolean flag) {...} //給予私有化構造方法,成員方法,成員變數操作許可權
public T newInstance(Object ... initargs) {...} //通過這個構造器宣告一個類的新例項
public int getModifiers() {...} //獲取構造器的訪問修飾符
//Method方法呼叫
public Object invoke(Object obj, Object... args) {...} //obj為操作物件靜態方法為null, args為方法引數
詳情可以參考部落格
例子:
Class<?> clazz = Class.forName("java.util.Date");
Date instance = (Date)clazz.newInstance();
使用反射覆制陣列:
使用Object類實現:
public static Object copy(Object array, int newLength){
Objects.requireNonNull(array);
Class<?> clazz = array.getClass();
System.out.println(clazz.getName());
if (!clazz.isArray()) {
return null;
}
Class<?> componentType = clazz.getComponentType();
Object newArray = Array.newInstance(componentType, newLength);
System.arraycopy(array, 0, newArray, 0, newLength);
return newArray;
}
泛型方式實現:
public static <T> T copy(T array, int newLength){
Objects.requireNonNull(array);
Class<?> clazz = array.getClass();
if (!clazz.isArray()){
return null;
}
Class<?> componentType = clazz.getComponentType();
Object o = Array.newInstance(componentType, newLength);
System.arraycopy(array, 0, o, 0, newLength);
return (T)o;
}
使用泛型和反射實現模型轉換:
public class Converter {
public static <T, S> T convert(S s, Class<T> clazz){
try {
T t = clazz.newInstance();
if (s == null){
return t;
}
BeanUtils.copyProperties(s, t);
return t;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
動態代理
在程式執行中建立物件, 不需要程式設計師去建立檔案
功能:
- 控制訪問, 在代理中控制是否可以呼叫目標的方法
- 功能增強, 在完成目標呼叫時, 附加額外的一些功能, 這些額外的功能叫做功能增強
優點:
-
不用建立代理類
-
可以給不同的目標隨時建立代理
流程:
JDK實現動態代理:
- 建立一個處理程式, 經過這個處理程式處理的類的方法執行都會將耗時列印在控制檯上
class MyInvokeHandler<T> implements InvocationHandler {
private T proxied = null;
public MyInvokeHandler(T proxied){
this.proxied = proxied;
}
/**
*
* @param proxy
* @param method 代理物件的動作
* @param args 動作的引數列表
* @return 返回
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long before = System.currentTimeMillis();
Object invoke = method.invoke(proxied, args); //使用反射呼叫類中的方法
long after = System.currentTimeMillis();
System.out.println(method.getName() + " used time: " + (after - before));
return invoke;
}
}
- 下面是被代理物件和其實現介面
interface Super{
void printHello(String param);
}
class Proxied implements Super{
public void printHello(String param){
System.out.println("Hello Proxied: " + param);
}
}
- 實現動態代理
//1. 建立一個被代理類, 就是被處理程式處理的類
Proxied proxied = new Proxied();
//2. 建立程式處理類處理這個被代理類
InvocationHandler handler = new MyInvokeHandler<>(proxied);
//3. 根據程式處理類動態生成代理類
Class<?> proxyClass = Proxy.getProxyClass(Super.class.getClassLoader(), Super.class);
//4. 生成代理類的構造器
Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
//5. 使用代理類的構造器生成代理物件
Super s = (Super)constructor.newInstance(handler);
//6. 檢測代理是否有效
s.printHello("axianibiru");
或者:
//1. 建立被代理類
Proxied proxied = new Proxied();
//2. 生成代理處理程式
InvocationHandler handler = new MyInvokeHandler<Super>(proxied);
//3. 根據代理處理程式動態生成代理類
Super s = (Super)Proxy.newProxyInstance(Super.class.getClassLoader(), new Class<?>[]{Super.class}, handler);
//4. 呼叫被代理後的方法
s.printHello("axianibiru");
//5. 檢視代理類行資訊
Class<? extends Super> clazz = s.getClass();
//6. 檢視繼承和實現關係
Class<?> superclass = clazz.getSuperclass();
System.out.print(clazz.getName() + " extends " + superclass);
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.print( " implements " + anInterface.getName() + " ");
}
輸出:
Hello Proxied: axianibiru
printHello used time: 0
com.proxyTest.dynamicProxy.thridDemo.$Proxy0 extends class java.lang.reflect.Proxy implements com.proxyTest.dynamicProxy.thridDemo.Super