最近筆者在深入了解一些開源框架,比如Spring,mybatis這些框架,去閱讀原始碼學習作者的一些好的設計思路和寫法,通過閱讀原始碼你會學到很多大神的編程思想,其中有很多很好的設計模式,看過很多原始碼的話,你會發現很多地方都采用了代理設計模式來使得框架的靈活性開發,今天主要根據JDK的動態代理來介紹下這個代理模式,
首先做個小的鋪墊,很多描述代理模式的時候會用兩個名詞 代理物件 和 目標物件,
代理物件代理的就是目標物件,這倆的關系在程式里不是恒定的,
就比如 一種戀愛關系 A->B->C A暗戀B B是被暗戀的, 但是B暗戀C 相對有C來說C是被暗戀的,就是大家清楚這種關系 是相對的,不是絕對的,
代理模式
- 靜態代理
- 繼承形式實作靜態代理
- 聚合-介面形式實作靜態代理
- 動態代理
- JDK動態代理
- 模仿mybatis的動態代理
- 模仿Spring-aop的動態代理
- 走進Proxy.newProxyInstance方法
- 動態代理物件保存到本地
代理模式分為動態代理和靜態代理;
靜態代理
靜態代理的體現在 繼承和聚合-介面的形式
繼承形式實作靜態代理
/**
* 定義一個基礎類
*/
public class BaseMapper {
//基礎類的查詢方法
public void select(){
}
}
//定義一個類實作這個方法的查詢方法
public class QueryMapper extends BaseMapper {
@Override
public void select() {
System.out.println("查詢方法");
}
}
//定義一個代理類去繼承你想代理的方法
public class ProxyMapper extends QueryMapper{
@Override
public void select() {
System.out.println("代理方法");
super.select();
}
}
我們測驗下
public static void main(String[] args) {
//正常的創建呼叫查詢方法
BaseMapper queryMapper = new QueryMapper();
queryMapper.select();
System.out.println("=====================");
//代理的形式呼叫方法
BaseMapper proxyMapper = new ProxyMapper();
proxyMapper.select();
}
//列印出來
查詢方法
=====================
代理方法
查詢方法
缺點:這種繼承的形式需要寫大量的繼承關系,創建大量的類,代理類過多
聚合-介面形式實作靜態代理
/**
* 基礎介面
*/
public interface BaseMapper {
//定義查詢介面
void select();
}
一個實作類
/**
* 搜索方法
*/
public class SelectMapper implements BaseMapper {
@Override
public void select() {
System.out.println("select * from xxx");
}
}
聚合-介面形式實作靜態代理,創建一個代理物件需要那些條件,
- 代理物件要和目標物件實作同一個介面
- 代理物件存在一個屬性接收目標物件
- 代理物件的構造方法注入目標物件
創建一個代理物件,
/**
* 代理物件
*/
public class ProxyMapper implements BaseMapper {
private BaseMapper baseMapper ;
public ProxyMapper(BaseMapper baseMapper) {
this.baseMapper = baseMapper;
}
@Override
public void select() {
System.out.println("代理方法");
baseMapper.select();
}
}
我們呼叫下
public static void main(String[] args) {
//正常的創建呼叫查詢方法
BaseMapper queryMapper = new SelectMapper();
queryMapper.select();
System.out.println("=====================");
//代理的形式呼叫方法
BaseMapper proxyMapper = new ProxyMapper(queryMapper);
proxyMapper.select();
}
列印:
select * from xxx
=====================
代理方法
select * from xxx
如果說我們要在查詢方法之前先做條數統計count的功能 ,那么我們需要新增代理物件
public class CountMapper implements BaseMapper {
private BaseMapper baseMapper ;
public CountMapper(BaseMapper baseMapper) {
this.baseMapper = baseMapper;
}
@Override
public void select() {
//統計方法
System.out.println("統計方法");
baseMapper.select();
}
public static void main(String[] args) {
//正常的創建呼叫查詢方法
BaseMapper queryMapper = new SelectMapper();
queryMapper.select();
System.out.println("=====================");
//代理的形式呼叫方法
BaseMapper proxyMapper = new ProxyMapper(queryMapper);
proxyMapper.select();
System.out.println("=====================");
//先執行 代理->新增統計方法->查詢
BaseMapper proxy = new ProxyMapper(new CountMapper(new SelectMapper()));
proxy.select();
}
列印:
select * from xxx
=====================
代理方法
select * from xxx
=====================
代理方法
統計方法
select * from xxx
很明顯,我們這種聚合-介面的形式,能夠很靈活去在目標物件的方法上 做各種的擴展,上面這個例子,也可以看出代理物件和目標物件的關系是相對的,
但是還是會存在大量的代碼量,所以人們開始思考如何能夠動態的生成滿足我們需要的代理物件來幫我們實作我們想要的功能,
小總結下:仔細看來這些靜態代理的形式,會發現這是一種編程的多型運用,多型的定義很廣,也很靈活,
動態代理
JDK動態代理
在JDK的java.lang.reflect.Proxy類newProxyInstance方法
/**
* 回傳指定介面的代理類的實體,該實體將方法呼叫分派到指定的呼叫處理程式,
* @param loader 類加載器
* @param interfaces 介面集
* @param h 將方法的呼叫分派給呼叫處理程式
* @return 回傳一個代理物件
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h){
}
模仿mybatis的動態代理
我們試著去呼叫這個方法生成一個代理物件
首先我們先定義一個介面UserDao
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Select {
String value() default "";
}
public interface UserDao {
@Select("select * from xxx")
String select();
}
我們寫一個方法去呼叫Proxy的生成代理類方法
public class MapperFactory {
public static Object getMapper(Class interfaces) {
//interfaces 為目標物件實作的多個介面
Class[] classes = {interfaces};
//獲取介面集的代理物件
Object o = Proxy.newProxyInstance(MapperFactory.class.getClassLoader(),//類加載器
classes,//介面集
new MapperInvocationHandler());//指定的呼叫處理程式
return o;
}
}
//創建一個實作InvocationHandler介面的實作類 MapperInvocationHandler
/**
* 模擬mybatis框架的mapper介面動態代理實作
* */
public class MapperInvocationHandler implements InvocationHandler {
//invoke 在代理物件中呼叫該方法 轉入相應的目標物件方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//我們自定義一些方法的實作 和 前后增強
System.out.println("鏈接資料庫");//實作一個資料庫鏈接的操作呀
Select annotation = method.getAnnotation(Select.class);
System.out.println(annotation.value());//處理一下方法的注解邏輯呀
return method.getReturnType().newInstance();//根據方法的回傳物件 可以自定義
}
}
}
我們呼叫一下
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
UserDao userDao = (UserDao) MapperFactory.getMapper(UserDao.class);
userDao.select();
}
列印:
鏈接資料庫
select * from xxx
在呼叫Proxy.newProxyInstance方法后,記憶體中會注入一個代理物件,這個代理物件實作了你前面傳入的所有interfaces[] 介面方法,
這個代理物件是存在記憶體中的,不是我們能看到的一個.java檔案或者一個.class檔案,
Mybatis框架的設計思路就是通過代理的形式來處理mapper介面,
我們的這種呼叫是模仿mybatis中的參考,是通過代理物件來實作mapper介面;
模仿Spring-aop的動態代理
接下來我們來模仿Spring-aop的動態代理,他是根據目標物件來做代理增強的,
//創建一個實作類 實作UserDao.select方法
public class UserDaoImp implements UserDao {
@Override
public String select() {
System.out.println("實作類方法");
return "實作imp";
}
}
我們要對這個UserDaoImp.class類做代理
首先指定方法呼叫處理程式 InvocationHandler
/**
* 模擬Spring-aop形式的動態代理實作
*/
public class AopInvocationHandler implements InvocationHandler {
private Object target;
public AopInvocationHandler(Object object) {
this.target = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("aop-before method");//@before 前置增強 方法
Object invoke = method.invoke(target, args);//目標物件自身方法邏輯和處理結果
System.out.println("aop-after method");//@after 后置增強 方法
return invoke;
}
}
//模擬的工廠類
public class AopProxyFactory {
public static Object getMapper(Object target) {
//interfaces 為目標物件實作的多個介面
Class[] classes = target.getClass().getInterfaces();//獲取物件所有的介面class集合
//獲取介面集的代理物件
Object o = Proxy.newProxyInstance(AopProxyFactory.class.getClassLoader(),//類加載器
classes,//介面集
new MapperInvocationHandler(target));//目標物件傳入 指定的呼叫處理程式
return o;
}
}
我們來呼叫他
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
UserDao userDao = (UserDao) AopProxyFactory.getMapper(new UserDaoImp());
userDao.select();
}
列印:
aop-before method
實作類方法
aop-after method
這種我們是以目標物件 UserDaoImp 生成的代理物件 $Proxy0
本質上還是呼叫目標物件的select()方法,但是可以在方法的前后做自己的邏輯增強處理,
至于Proxy.newProxyInstance方法中是如何生成代理物件的讓我們走進方法的原始碼,
走進Proxy.newProxyInstance方法
筆者會待大家走一下方法的主線,當然很多細節還需要讀者去自己深入的了解下,看原始碼一定要先弄清方法的主線,
//關鍵類
package java.lang.reflect.Proxy;
/**
* 回傳指定介面的代理類的實體,該實體將方法呼叫分派到指定的呼叫處理程式,
* @param loader 類加載器
* @param interfaces 介面集
* @param h 將方法的呼叫分派給呼叫處理程式
* @return 回傳一個代理物件
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException{
Objects.requireNonNull(h);//InvocationHandler不能傳null
final Class<?>[] intfs = interfaces.clone();
//獲取代理物件class com.sum.proxy.$Proxy0
Class<?> cl = getProxyClass0(loader, intfs);
//獲取代理物件的構造方法 生成一個有參的構造方法 &Proxy0(InvocationHandler h)
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//實體化 代理物件 &Proxy0(InvocationHandler h)
return cons.newInstance(new Object[]{h});
}
方法
Class<?> getProxyClass0(ClassLoader loader,Class<?>… interfaces);
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 有個快取機制,如果存在就回傳代理物件
// 否則通過ProxyClassFactory創建一個代理class
return proxyClassCache.get(loader, interfaces);
}
proxyClassCache 屬性;
//a cache of proxy classes
//代理類的快取容器
WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
WeakCache.get(K key, P parameter) ; 快取容器的get方法
// p interfaces[]
public V get(K key, P parameter) {
Object cacheKey = CacheKey.valueOf(key, refQueue);
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// 上面都不用在意 硬要理解的話 可以理解成 程式在幫我們申請記憶體 初始化容器和資料記錄
//第一個很關鍵的方法
//KeyFactory.apply(ClassLoader classLoader, Class<?>[] interfaces);
//subKey是把interfaces封裝在里面
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
//WeakCache.Factory類
Factory factory = null;
//自旋
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
//型別可能是一個 Factory 或者 CacheValue<V>實體
//第三步 我們呼叫這個 supplier 的get方法,這個時候 supplier被第二步賦值
// 等同于呼叫 Factory.get()方法
V value = supplier.get();
if (value != null) {
return value;
}
}
//第一步 創建一個工廠
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
//第二步 得到上面創建的工廠 這個時候supplier被工廠賦值
supplier = factory;
}
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
supplier = factory;
} else {
supplier = valuesMap.get(subKey);
}
}
}
}
第三步呼叫 方法 WeakCache.Factory類實作了Supplier.get方法
@Override
public synchronized V get() { // serialize access
// 自身檢查
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
//回傳null 重新加入 外層自旋
return null;
}
// create new value
V value = null;
try {
//最關鍵的方法 通過ProxyClassFactory創建一個代理class
//呼叫 Class<?> apply(ClassLoader loader, Class<?>[] interfaces)
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // 創建失敗 清除
valuesMap.remove(subKey, this);
}
}
// 斷言
assert value != null;
return value;
}
}
//在記憶體中動態生成代理物件的類class
Class<?> apply(ClassLoader loader, Class<?>[] interfaces)
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
//回圈介面class集合 主要做一些判斷
for (Class<?> intf : interfaces) {
Class<?> interfaceClass = null;
try {
//拿到這個 介面的class
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
/*
* 判斷這個class是一個介面
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
*判斷這個介面是否重復
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
//*******開始處理一些代理物件相關的屬性 **********
String proxyPkg = null; // 代理類的包路徑 類似 package java.xxx.xxx
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* 記錄非公共代理介面的程式包,以便在同一程式包中定義代理類,
* 驗證所有非公共代理介面都在同一程式包中,
* 這個不在主流成中 一般不會進到回圈里
**/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
//處理代理類的 package 路徑
if (proxyPkg == null) {
// 沒有非公共代理的介面 我們采用 com.sun.proxy 包路徑
// ReflectUtil.PROXY_PACKAGE = com.sun.proxy
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/**
* 處理proxyName代理類名
*/
long num = nextUniqueNumber.getAndIncrement();//獲取編號
// 所有代理類名稱的前綴 String proxyClassNamePrefix = "$Proxy"
// com.sun.proxy. + $Proxy + 0 當前的代理類的名稱
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//******生成指定的 代理類 class ********************
//呼叫 ProxyGenerator.generateProxyClass 生成代理類的位元組碼
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
//呼叫神奇的JVM虛擬機的bean方法 將一個class byte[]位元組 編譯成記憶體物件
//private static native Class<?> defineClass0(ClassLoader loader, String name,byte[] b, int off, int len); 回傳class
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
}
}
呼叫 ProxyGenerator.generateProxyClass 生成代理類的位元組碼動態代理的核心方法
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
//生成構建器
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
//generateClassFile 獲取class位元組
final byte[] var4 = var3.generateClassFile();
//boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction(
"sun.misc.ProxyGenerator.saveGeneratedFiles"));
//這個boolean表示生成的代理物件是否保存到本地,因為這個類只是被加載到記憶體中,默認是不保存
if (saveGeneratedFiles) {
//開始寫入操作通過流將 記憶體中的代理物件生成 .class檔案
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
//回傳代理物件的位元組
return var4;
}
generateClassFile方法處理代理物件位元組
private byte[] generateClassFile() {
//這里的this是 ProxyGenerator 代理物件構造器
//添加 Object的 hashCode equals toString 方法
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
Class[] var1 = this.interfaces;//目標物件的介面集
int var2 = var1.length;
int var3;
Class var4;
//將目標物件介面集的所有方法 添加到代理物件構造器中
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];//獲取到其中一個介面
Method[] var5 = var4.getMethods();//獲得介面中所有的方法集
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];//拿到方法集中的一個方法
this.addProxyMethod(var8, var4);//將方法添加到構造器中 var8=select()方法 var4 = UserDao這個介面物件
}
}
//拿到這個方法集合 這個集合就是上面處理好的所有的方法
Iterator var11 = this.proxyMethods.values().iterator();
List var12;
while(var11.hasNext()) {
var12 = (List)var11.next();
//檢查回傳型別
checkReturnTypes(var12);
}
Iterator var15;
//generateConstructor() 添加代理物件的構造方法
//這個面是寫死的 代理物件的有引數 引數:InvocationHandler var1
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
while(var15.hasNext()) {
//添加 Proxy方法 代理物件 默認繼承Proxy類 包含 屬性 protected InvocationHandler h;
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
this.methods.add(var16.generateMethod());
}
}
this.methods.add(this.generateStaticInitializer());
//判斷方法和屬性的長度
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
} else if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
} else {
//**************從這開始 將所有的方法寫入********
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
var1 = this.interfaces;
var2 = var1.length;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}
this.cp.setReadOnly();
//創建流
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
//寫入
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
//ByteArrayOutputStream 轉成 byte[]位元組
//我們去列印Var13可能會發現 他的內容類似一個編譯后的class檔案
return var13.toByteArray();
}
}
代理物件默認的generateConstructor()
private ProxyGenerator.MethodInfo generateConstructor() throws IOException {
//有參構造方法 唯一引數 InvocationHandler
ProxyGenerator.MethodInfo var1 = new ProxyGenerator.MethodInfo("<init>", "(Ljava/lang/reflect/InvocationHandler;)V", 1);
DataOutputStream var2 = new DataOutputStream(var1.code);
this.code_aload(0, var2);
this.code_aload(1, var2);
var2.writeByte(183);
var2.writeShort(this.cp.getMethodRef("java/lang/reflect/Proxy", "<init>", "(Ljava/lang/reflect/InvocationHandler;)V"));
var2.writeByte(177);
var1.maxStack = 10;
var1.maxLocals = 2;
var1.declaredExceptions = new short[0];
return var1;
}
再回到newProxyInstance方法 這個時候拿到了 cl
Class<?> cl = getProxyClass0(loader, intfs);
//關鍵類
package java.lang.reflect.Proxy;
/**
* 回傳指定介面的代理類的實體,該實體將方法呼叫分派到指定的呼叫處理程式,
* @param loader 類加載器
* @param interfaces 介面集
* @param h 將方法的呼叫分派給呼叫處理程式
* @return 回傳一個代理物件
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException{
Objects.requireNonNull(h);//InvocationHandler不能傳null
final Class<?>[] intfs = interfaces.clone();
//獲取代理物件class com.sum.proxy.$Proxy0
Class<?> cl = getProxyClass0(loader, intfs);
//獲取代理物件的構造方法 生成一個有參的構造方法 &Proxy0(InvocationHandler h)
//我們獲得$Proxy0 的構造方法 generateConstructor()生成的
// constructorParams = InvocationHandler h
final Constructor<?> cons = cl.getConstructor(constructorParams);
//實體化 代理物件 &Proxy0(InvocationHandler h)
return cons.newInstance(new Object[]{h});
}
我們跟過一遍就知道他代理代理的生成邏輯了,但是畢竟代理物件是在記憶體中,我們很難看到他的樣子,如果存在.java或者.class檔案是不是就更好理解了呢?
在原始碼的程序中 有過一個是否將代理物件保存到本地的判斷,大家還記得嗎?
我們可以開啟他,
動態代理物件保存到本地
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
// saveGeneratedFiles = true 保存代理物件到磁盤 地址默認為 com.sum.proxy 目錄下
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//這個時候 userDao 就不是 UserDaoImp 而是他的代理物件 $Proxy0
UserDao userDao = (UserDao) AopProxyFactory.getMapper(new UserDaoImp());
userDao.select();
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.sun.proxy;
import com.example.spring2.proxy.UserDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
/**生成代理物件*/
public final class $Proxy0 extends Proxy implements UserDao {
//代理物件默認繼承Proxy類
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
//父類Proxy屬性 protected InvocationHandler h;
//構造方法
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
//靜態塊 獲取到目標物件的方法
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
//獲取到目標物件 UserDao的select方法
//Class.forName(String className).getMethod(String name, Class<?>... parameterTypes)
m3 = Class.forName("com.example.spring2.proxy.UserDao").getMethod("select");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
//目標物件的方法
public final String select() throws {
try {
//這就是為什么 代理物件可以和目標物件存在一樣的功能,
//呼叫 InvocationHandler.invoke方法 傳入目標物件的方法和入參
return (String)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
小總結下:代理是呼叫相同的方法,得到的處理邏輯可能一樣,也可能不同,就是代理不一定是增強,很多現在人們說是增強方法,但是也可以是改變,改變這個方法,
就像 如果我們不去Object invoke = method.invoke(target, args);呼叫/目標物件自身方法邏輯 ,是不是也可以自己去書寫他新的邏輯,回傳同樣的回傳型別,所以我們出了代理增強,也是一種代理改變,
github: https://github.com/dxt1218/jdk-proxy
當然動態代理的實作有很多種,SpringAop中Cglib也可以實作動態代理,這個后續筆者可以再出一篇Cglib的動態代理的原始碼實作,
希望這篇文章能夠有助于大家,有什么好的建議和疑問也可以留言交流,感謝閱讀~~
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/276172.html
標籤:java
上一篇:整數轉羅馬數字Java版(力扣)
