java學習-動態代理-JDK動態代理
阿新 • • 發佈:2020-11-29
JDK 原生動態代理
對於 JDK 動態代理 實際是 利用了java.lang.reflect.Proxy#newProxyInstance 來獲得動態代理物件
對於得到的動態代理物件結果 分析
getClass ->com.sun.proxy.$Proxy0
getSuperClass ->java.lang.reflect.Proxy
getinterfaces -> 就是呼叫Proxy#newProxyInstance API 傳遞的介面型別引數
通過對動態代理物件結果分析可以看出,動態代理和普通java類物件的區別在於 動態代理結果類實際是沒有具體的型別,且其繼承於 Proxy類,並實現API指定的介面類
其缺陷也很明顯:JDK動態代理是針對介面的增強,雖然從API角度支援傳遞多個介面型別,但在使用時需要對得到的代理物件例項進行強制型別轉換為指定的介面型別,方可使用介面對應的方法
/* * Copyright (c) 2020, guoxing, Co,. Ltd. All Rights Reserved */ package com.xingguo.bytecode.beans; import com.xingguo.bytecode.dynamicproxy.NameableProxyService; import java.io.Serializable; /** * ProxyPojo * 測試動態代理 的pojo * *@author guoxing * @date 2020/11/28 4:24 PM * @since */ public class ProxyPojo implements Serializable, NameableProxyService { private String name; @Override public void setName(String name){ this.name = name; } public String getName() { return name; } }
/* * Copyright (c) 2020, guoxing, Co,. Ltd. All Rights Reserved*/ package com.xingguo.bytecode.dynamicproxy; /** * NameableProxyService * 為了支援jdk 基於介面的動態代理實現, {@link com.xingguo.bytecode.beans.ProxyPojo} * 通過 jdk 動態代理 實現 {@link java.beans.PropertyChangeEvent} 相關操作 * * @author guoxing * @date 2020/11/28 4:25 PM * @since */ public interface NameableProxyService { /** * 設定 {@link com.xingguo.bytecode.beans.ProxyPojo#name} 操作 * * @param name 名稱 */ void setName(String name); }
/* * Copyright (c) 2020, guoxing, Co,. Ltd. All Rights Reserved */ package com.xingguo.bytecode.dynamicproxy; import com.xingguo.bytecode.beans.ProxyPojo; import lombok.extern.slf4j.Slf4j; import java.beans.*; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Objects; import static com.xingguo.java.beans.properties.Person.isNumeric; /** * PersonEventDynamicProxyDemo * 對於 {@link NameableProxyService} 的動態代理提升 * * @author guoxing * @date 2020/11/28 4:22 PM * @since */ @Slf4j public class PersonEventDynamicProxyDemo { public static void main(String[] args) { /** * 對於 不使用動態代理的操作如下 */ ProxyPojo proxyPojo = new ProxyPojo(); NameablePropertyEventInvocationHandler nameablePropertyEventInvocationHandler = new NameablePropertyEventInvocationHandler(proxyPojo); // 增加 PropertyChange listener nameablePropertyEventInvocationHandler.addPropertyChangeListener(event -> { log.info("強迫屬性事件"); log.info("source:{}中的屬性:{}發生變化,oldVal為{},newVal為{}", event.getSource(), event.getPropertyName(), event.getOldValue(), event.getNewValue()); }); // 增加 VetoableChange listener nameablePropertyEventInvocationHandler.addVetoableChangeListener(event -> { log.info("勉強屬性事件"); log.info("source:{}中的屬性:{}發生變化,oldVal為{},newVal為{}", event.getSource(), event.getPropertyName(), event.getOldValue(), event.getNewValue()); }); // 獲取當前執行緒上下文的 類載入器 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); // 建立動態代理類 // 對於 動態代理的建立實際支援傳遞多個介面, 但也可以指定任意一個介面進行強制轉換 /** * 對於JDK動態代理的缺點也在於其基於介面模式,當一個實現類實現多個介面時,就會需要編寫過多的重複程式碼去增強多個介面 */ NameableProxyService nameableProxyService = (NameableProxyService) Proxy.newProxyInstance(contextClassLoader, new Class[]{NameableProxyService.class}, nameablePropertyEventInvocationHandler); /** * 對於 Proxy.newInstance 得到的結果進行分析 * 型別:.getClass -> com.sun.proxy.$Proxy0 * superclass -> java.lang.reflect.Proxy * interfaces -> Proxy.newProxyInstance# interfaces 引數 * 對於 JDK 動態代理 得到的代理類實際並沒有具體的型別定義,動態代理和普通類 的區別就在於動態代理是屬於執行時動態生成的 類(例項), 生成的結果實際是通過組裝而成的,首先是繼承了 java.lang.reflect.Proxy類,其次是 實現的傳遞的 interfaces 引數中的全部介面 * * 其缺陷: 對於JDK動態代理而言,其實現主要是對介面的增強,因此在使用動態代理類時必須要指定單獨的介面,當實現多個介面時,就需要對代理類進行多次強制型別轉換為指定的介面型別 */ nameableProxyService.setName("guoxing"); nameableProxyService.setName("xingguo"); nameableProxyService.setName("1234"); } } /** * 對於 {@link com.xingguo.bytecode.beans.ProxyPojo#setName(String)} 的 {@link java.beans.PropertyChangeEvent} 的動態代理實現 */ class NameablePropertyEventInvocationHandler implements InvocationHandler { private ProxyPojo proxyPojo; // 強迫屬性工具類 private transient final PropertyChangeSupport propertyChangeSupport; // 勉強屬性工具類 private transient final VetoableChangeSupport vetoableChangeSupport; public NameablePropertyEventInvocationHandler(ProxyPojo proxyPojo) { this.proxyPojo = proxyPojo; propertyChangeSupport = new PropertyChangeSupport(proxyPojo); vetoableChangeSupport = new VetoableChangeSupport(proxyPojo); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String name = method.getName(); // 對於 void setName(String) 判斷 if ("setName".equals(name) // 判斷方法名稱是否為 "setName" && void.class.equals(method.getReturnType()) // 判斷返回值型別是否為 void && Objects.nonNull(args) // 判斷 引數是否為空 && args.length == 1 // 判斷長度是否為1 && args[0] instanceof String // 是否為 String 型別 ) { String oldVal = proxyPojo.getName(); String newVal = (String) args[0]; // 建立屬性變更事件 PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(this, "name", oldVal, newVal); // 勉強屬性 fireVetoableChange(propertyChangeEvent); proxyPojo.setName(newVal); // 強迫事件變更機制 // 強迫事件變更發生在 屬性成功變更之後 // 屬性變更後釋出事件 // 釋出事件 firePropertyChange(propertyChangeEvent); } return null; } /** * 強迫屬性相關 * * @param event */ public void firePropertyChange(PropertyChangeEvent event) { propertyChangeSupport.firePropertyChange(event); } public void addPropertyChangeListener(PropertyChangeListener listener) { propertyChangeSupport.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { propertyChangeSupport.removePropertyChangeListener(listener); } /** * 勉強屬性相關 * * @param event */ public void fireVetoableChange(PropertyChangeEvent event) { try { // 校驗要求name 不能為純粹的數字 String propertyName = event.getPropertyName(); if ("name".equals(propertyName) && isNumeric(String.valueOf(event.getNewValue()))) { throw new PropertyVetoException("name屬性要求不能為純數字", event); } vetoableChangeSupport.fireVetoableChange(event); } catch (PropertyVetoException propertyVetoException) { throw new RuntimeException(propertyVetoException); } } public void addVetoableChangeListener(VetoableChangeListener listener) { vetoableChangeSupport.addVetoableChangeListener(listener); } public void removeVetoableChangeListener(VetoableChangeListener listener) { vetoableChangeSupport.removeVetoableChangeListener(listener); } }