JDK動態(tài)代理,代理接口沒有實現(xiàn)類,實現(xiàn)動態(tài)代理方式
JDK動態(tài)代理,代理接口沒有實現(xiàn)類,實現(xiàn)動態(tài)代理
JDK代理,代理的是接口,那么筆者想一想,既然代理的是接口,那如果沒有實現(xiàn)類怎么辦,能不能代理。答案是可以的,Mybatis就是這樣的。
Mybatis使用JDK動態(tài)代理來實現(xiàn)Mapper接口,事先保存好Mapper接口,和接口聲明的方法,返回值,參數(shù)類型,然后代理類的方法調(diào)用的時候使用MapperMethod這個事先放入方法緩存里的對象來真實調(diào)用功能。
筆者極度簡化了一下代碼:
被代理的接口:
public interface Subject2 {
String selectById();
}
這個接口可以看成是Mapper接口
代理對象:
public class SubjectProxy2<T> implements InvocationHandler {
private Class<T> proxyInterface;
//這里可以維護(hù)一個緩存,存這個接口的方法抽象的對象
SubjectProxy2(Class<T> proxyInterface){
this.proxyInterface = proxyInterface;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("selectById")){
//String result = (String) method.invoke(proxyInterface,args);
//這里可以得到方法抽象對象來調(diào)用真的的查詢方法
System.out.println("selectById調(diào)用成功");
}
return null;
}
public T getProxy(){
return (T) Proxy.newProxyInstance(proxyInterface.getClassLoader(),new Class[]{proxyInterface},this);
}
}
這個代理類使用了泛型,說明這個代理類可以代理所有的mapper接口。
那么接下來測試一下:
public class ProxyTest2 {
public static void main(String[] args) {
SubjectProxy2<Subject2> subjectProxy2 = new SubjectProxy2(Subject2.class);
Subject2 subject2 = subjectProxy2.getProxy();
subject2.selectById();
}
}
結(jié)果不言而喻??隙〞邢鄳?yīng)的輸出
沒有看mybatis源碼的時候,我以為動態(tài)代理一定要要有實現(xiàn)類才能代理,但是看了優(yōu)秀的頂級大牛的源碼之后,我才發(fā)現(xiàn),原來還可以這樣。
jdk動態(tài)代理為什么要接口
jdk的動態(tài)代理為什么用接口,內(nèi)部是什么原理呢?看了幾篇文章貌似都沒講的清楚明白,因此來解釋一下。
先通過一個簡單例子實現(xiàn)功能:
//接口
public interface SayService {
void say(String name);
}
//實現(xiàn)類
public class SayServiceImpl implements SayService{
@Override
public void say(String name) {
System.out.println(name);
}
}
然后再自定義一個增強(qiáng)類, 實現(xiàn)InvocationHandler接口:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class WavingInvocationHandler implements InvocationHandler{
private Object target;
public void setTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法執(zhí)行之前!");
Object obj = method.invoke(target, args);
System.out.println("方法執(zhí)行之后!");
return obj;
}
}
編寫測試方法:
import sun.misc.ProxyGenerator;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
//接口
SayService sayService = new SayServiceImpl();
//織入類
WavingInvocationHandler handler = new WavingInvocationHandler();
handler.setTarget(sayService);
//代理類--增強(qiáng)的對象
SayService s = (SayService) Proxy.newProxyInstance(
sayService.getClass().getClassLoader(),
sayService.getClass().getInterfaces(), handler);
s.say("say()");//執(zhí)行代理對象完成業(yè)務(wù)
/**
方法執(zhí)行之前!
say()
方法執(zhí)行之后!
*/
//將jdk中生成代理類輸出到本地.Class文件,之后可以通過反編譯軟件打開查看
createProxyClassFile("test12345",sayService.getClass().getInterfaces());
}
private static void createProxyClassFile(String name,Class<?> [] interfaces){
byte[] data = ProxyGenerator.generateProxyClass(name,interfaces);//該方法為jdk中生成代理類的核心方法
FileOutputStream out =null;
try {
out = new FileOutputStream(name+".class");
System.out.println((new File(name)).getAbsolutePath());
out.write(data);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null!=out) try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
好奇心強(qiáng)的小伙伴一定看過newProxyInstance方法看過了,
里面的getProxyClass方法創(chuàng)建代理類:
/* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs);
具體是里面的哪個方法生成的,小伙伴們不用找半天了,慢慢debug后會發(fā)現(xiàn),就是上面提到的
ProxyGenerator.generateProxyClass()
通過該方法生成的代理類如下:
通過反編譯查看源碼,一看便知接口的作用
JDK的動態(tài)代理是靠多態(tài)和反射來實現(xiàn)的,它生成的代理類需要實現(xiàn)你傳入的接口,并通過反射來得到接口的方法對象(下文中的m3),并將此方法對象傳參給增強(qiáng)類(上文中的WavingInvocationHandler類)的invoke方法去執(zhí)行,從而實現(xiàn)了代理功能,故接口是jdk動態(tài)代理的核心實現(xiàn)方式,沒有它就無法通過反射找到方法,所以這也是必須有接口的原因。不知道大家明白否
public final class test12345 extends Proxy implements SayService {
//1、該類實現(xiàn)你傳入的接口并實現(xiàn)方法
// 2、通過構(gòu)造方法傳入你定義的增強(qiáng)類對象
// 3、通過反射該接口,得到接口里的Method對象并傳參給增強(qiáng)類,然后執(zhí)行Invoke實現(xiàn)功能
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public test12345(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);//2.此處的super指向Proxy中的構(gòu)造方法并賦值(下文的h就是此處的增強(qiáng)類對象),
}
//略去無關(guān)的hashcode和equals方法
public final void say(String paramString) {
try {
this.h.invoke(this, m3, new Object[]{paramString});//3.此處的h就是InvocationHandler對象,invoke就是你增強(qiáng)類里的方法,傳入接口的方法和參數(shù)并執(zhí)行你增強(qiáng)類里的invoke方法,即完成了整個操作!
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
static {//1.靜態(tài)代碼塊給屬性賦值,初始化接口中的方法對象(主要是下面的m3)
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.chenrui.core.jdk.SayService").getMethod("say", new Class[]{Class.forName("java.lang.String")});
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
} catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring?Cloud?Gateway服務(wù)網(wǎng)關(guān)限流問題及解決
這篇文章主要介紹了Spring?Cloud?Gateway服務(wù)網(wǎng)關(guān)限流問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-04-04
spring mvc中的@ModelAttribute注解示例介紹
在Spring mvc中,注解@ModelAttribute是一個非常常用的注解,下面這篇文章主要給大家介紹了關(guān)于spring mvc中@ModelAttribute注解的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。2017-09-09
spring中的特殊注解@RequiredArgsConstructor詳解
這篇文章主要介紹了spring中的特殊注解@RequiredArgsConstructor,包括注解注入,構(gòu)造器注入及setter注入,結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-04-04
Java編程實現(xiàn)深度優(yōu)先遍歷與連通分量代碼示例
這篇文章主要介紹了Java編程實現(xiàn)深度優(yōu)先遍歷與連通分量代碼示例,2017-11-11

