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;
//這里可以維護一個緩存,存這個接口的方法抽象的對象
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);
}
}
然后再自定義一個增強類, 實現(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);
//代理類--增強的對象
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();
}
}
}
}
好奇心強的小伙伴一定看過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),并將此方法對象傳參給增強類(上文中的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)造方法傳入你定義的增強類對象
// 3、通過反射該接口,得到接口里的Method對象并傳參給增強類,然后執(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就是此處的增強類對象),
}
//略去無關(guān)的hashcode和equals方法
public final void say(String paramString) {
try {
this.h.invoke(this, m3, new Object[]{paramString});//3.此處的h就是InvocationHandler對象,invoke就是你增強類里的方法,傳入接口的方法和參數(shù)并執(zhí)行你增強類里的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)文章
JSch教程使用sftp協(xié)議實現(xiàn)服務(wù)器文件載操作
這篇文章主要為大家介紹了JSch如何使用sftp協(xié)議實現(xiàn)服務(wù)器文件上傳下載操作,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2022-03-03
使用Thrift實現(xiàn)跨語言RPC的調(diào)用
Thrift最大的優(yōu)勢就是可以實現(xiàn)跨語言RPC調(diào)用,尤其在一些大廠,微服務(wù)各模塊之間使用不同的語言是很常見的,本文就將使用java作為服務(wù)端,用python作為客戶端,實現(xiàn)不同語言之間的RPC調(diào)用,需要的可以參考下2023-10-10
SpringBoot 異步線程間數(shù)據(jù)傳遞的實現(xiàn)
本文主要介紹了SpringBoot 異步線程間數(shù)據(jù)傳遞的實現(xiàn),包括異步線程的基本概念、數(shù)據(jù)傳遞的方式、具體實現(xiàn)方式等,具有一定的參考價值,感興趣的可以了解一下2024-03-03
Spring Cloud Gateway組件的三種使用方式實例詳解
Spring Cloud Gateway是 Spring 官方基于 Spring5.0 、 SpringBoot2.0 和 Project Reactor 等技術(shù)開發(fā)的網(wǎng)關(guān)旨在為微服務(wù)框架提供一種簡單而有效的統(tǒng)一的API 路由管理方式,統(tǒng)一訪問接口,這篇文章主要介紹了Spring Cloud Gateway組件的三種使用方式,需要的朋友可以參考下2024-01-01
解析springboot集成AOP實現(xiàn)日志輸出的方法
如果這需要在每一個controller層去寫的話代碼過于重復(fù),于是就使用AOP定義切面 對其接口調(diào)用前后進行攔截日志輸出。接下來通過本文給大家介紹springboot集成AOP實現(xiàn)日志輸出,需要的朋友可以參考下2021-11-11
SpringBoot使用阿里OSS實現(xiàn)文件云存儲的方法
這篇文章主要介紹了SpringBoot使用阿里OSS實現(xiàn)文件云存儲,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10

