深入解析java中的靜態(tài)代理與動態(tài)代理
java編碼中經(jīng)常用到代理,代理分為靜態(tài)代理和動態(tài)代理。其中動態(tài)代理可以實(shí)現(xiàn)spring中的aop。
一、靜態(tài)代理:程序運(yùn)行之前,程序員就要編寫proxy,然后進(jìn)行編譯,即在程序運(yùn)行之前,代理類的字節(jié)碼文件就已經(jīng)生成了
被代理類的公共父類
package staticproxy;
public abstract class BaseClass {
public abstract void add();
}
被代理類
package staticproxy;
public class A extends BaseClass {
public void add() {
System.out.println("A add !");
}
}
代理類
package staticproxy;
public class Proxy {
BaseClass baseClass;
public void add() {
baseClass.add();
}
public void setBaseClass(BaseClass baseClass) {
this.baseClass = baseClass;
}
public static void main(String[] args) {
BaseClass baseClass = new A();
Proxy proxy = new Proxy();
proxy.setBaseClass(baseClass);
proxy.add();
}
}
二、動態(tài)代理:實(shí)際的代碼在編譯期間并沒有生成,而是在運(yùn)行期間運(yùn)用反射機(jī)制動態(tài)的生成
被代理類接口
package jdkproxy;
public interface Service {
public void add();
public void update();
}
被代理類A
package jdkproxy;
public class AService implements Service {
public void add() {
System.out.println("AService add>>>>>>>>>>>>>>>>>>");
}
public void update() {
System.out.println("AService update>>>>>>>>>>>>>>>");
}
}
被代理類B
package jdkproxy;
public class BService implements Service {
public void add() {
System.out.println("BService add---------------");
}
public void update() {
System.out.println("BService update---------------");
}
}
代理類
package jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
MyInvocationHandler() {
super();
}
MyInvocationHandler(Object target) {
super();
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 程序執(zhí)行前加入邏輯
System.out.println("before-----------------------------");
// 程序執(zhí)行
Object result = method.invoke(target, args);
//程序執(zhí)行后加入邏輯
System.out.println("after------------------------------");
return result;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}
測試類
package jdkproxy;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
Service aService = new AService();
MyInvocationHandler handler = new MyInvocationHandler(aService);
// Proxy為InvocationHandler實(shí)現(xiàn)類動態(tài)創(chuàng)建一個符合某一接口的代理實(shí)例
Service aServiceProxy = (Service) Proxy.newProxyInstance(aService
.getClass().getClassLoader(), aService.getClass()
.getInterfaces(), handler);
//由動態(tài)生成的代理對象來aServiceProxy 代理執(zhí)行程序,其中aServiceProxy 符合Service接口
aServiceProxy.add();
System.out.println();
aServiceProxy.update();
// 以下是對B的代理
// Service bService = new BService();
// MyInvocationHandler handler = new MyInvocationHandler(bService);
// Service bServiceProxy = (Service) Proxy.newProxyInstance(bService
// .getClass().getClassLoader(), bService.getClass()
// .getInterfaces(), handler);
// bServiceProxy.add();
// System.out.println();
// bServiceProxy.update();
}
}
輸出結(jié)果:
before-----------------------------
AService add>>>>>>>>>>>>>>>>>>
after------------------------------
before-----------------------------
AService update>>>>>>>>>>>>>>>
after------------------------------
其中上述標(biāo)紅的語句是產(chǎn)生代理類的關(guān)鍵代碼,可以產(chǎn)生一個符合Service接口的代理對象,newProxyInstance這個方法會做這樣一件事情,他將把你要代理的全部接口,用一個由代碼動態(tài)生成的類來實(shí)現(xiàn),該類中所有的接口中的方法都重寫為調(diào)用InvocationHandler.invoke()方法。
下面詳細(xì)介紹是如何實(shí)現(xiàn)代理對象的生成的
Proxy的newProxyInstance方法,其中,為了看起來方便,已經(jīng)將該方法中的異常處理語句刪減
下下面public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) throws
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
//生成指定的代理類
Class cl = getProxyClass(loader, interfaces);
Constructor cons = cl.getConstructor(constructorParams);
// 生成代理類的實(shí)例,并把MyInvocationHandler的實(shí)例傳給它的構(gòu)造方法,代理類對象實(shí)際執(zhí)行都會調(diào)用MyInvocationHandler的invoke方法,所以代理類對象中維持一個MyInvocationHandler引用
return (Object) cons.newInstance(new Object[] { h });
} 其中g(shù)etProxyClass方法返回代理類的實(shí)例
Proxy的getProxyClass方法
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException
{
//前面省略很多緩存、異常處理、判斷邏輯代碼,為了使程序更加突出
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
proxyClasses.put(proxyClass, null);
return proxyClass;
}
下面看ProxyGenerator的generateProxyClass方法,該方法最終產(chǎn)生代理類的字節(jié)碼文件:
public static byte[] generateProxyClass(final String name, Class[] interfaces)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces);
// 這里動態(tài)生成代理類的字節(jié)碼
final byte[] classFile = gen.generateClassFile();
// 如果saveGeneratedFiles的值為true,則會把所生成的代理類的字節(jié)碼保存到硬盤上
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
try {
FileOutputStream file =
new FileOutputStream(dotToSlash(name) + ".class");
file.write(classFile);
file.close();
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}
// 返回代理類的字節(jié)碼
return classFile;
}
那么最終生成的代理類到底是什么樣子呢,如下(省略了一下equals,hashcode,toString等方法,只展示構(gòu)造函數(shù)和add方法):
public final class $Proxy11 extends Proxy implements Service
{ // 構(gòu)造方法,參數(shù)就是剛才傳過來的MyInvocationHandler類的實(shí)例
public $Proxy11(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
/**
* 繼承的add方法,重寫,調(diào)用MyInvocationHandler中的invoke方法
*/
public final void add()
{
try
{
// 實(shí)際上就是調(diào)用MyInvocationHandler中的invoke方法
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
}
相關(guān)文章
spring整合redis消息監(jiān)聽通知使用的實(shí)現(xiàn)示例
在電商系統(tǒng)中,秒殺,搶購,紅包優(yōu)惠卷等操作,一般都會設(shè)置時間限制,本文主要介紹了spring整合redis消息監(jiān)聽通知使用,具有一定的參考價值,感興趣的可以了解一下2021-12-12Java使用Condition實(shí)現(xiàn)精準(zhǔn)喚醒線程詳解
這篇文章主要為大家詳細(xì)介紹了Java如何使用Condition實(shí)現(xiàn)精準(zhǔn)喚醒線程效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-02-02Mybatis中關(guān)于自定義mapper.xml時,參數(shù)傳遞的方式及寫法
這篇文章主要介紹了Mybatis中關(guān)于自定義mapper.xml時,參數(shù)傳遞的方式及寫法,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12解決mac最新版intellij idea崩潰閃退crash的問題
這篇文章主要介紹了解決mac最新版intellij idea崩潰閃退crash的問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09springboot3整合遠(yuǎn)程調(diào)用的過程解析
遠(yuǎn)程過程調(diào)用主要分為:服務(wù)提供者,服務(wù)消費(fèi)者,通過連接對方服務(wù)器進(jìn)行請求交互,來實(shí)現(xiàn)調(diào)用效果,這篇文章主要介紹了springboot3整合遠(yuǎn)程調(diào)用,需要的朋友可以參考下2023-06-06解決Springboot項(xiàng)目bootstrap.yml不生效問題
Spring Boot 2.4版本開始,配置文件加載方式進(jìn)行了重構(gòu),只會識別application.* 配置文件,并不會自動識別bootstrap.yml,所以本文給大家介紹Springboot項(xiàng)目bootstrap.yml不生效問題的解決方案,需要的朋友可以參考下2023-09-09springboot中spring.profiles.include的妙用分享
這篇文章主要介紹了springboot中spring.profiles.include的妙用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08Java?DelayQueue實(shí)現(xiàn)延時任務(wù)的示例詳解
DelayQueue是一個無界的BlockingQueue的實(shí)現(xiàn)類,用于放置實(shí)現(xiàn)了Delayed接口的對象,其中的對象只能在其到期時才能從隊列中取走。本文就來利用DelayQueue實(shí)現(xiàn)延時任務(wù),感興趣的可以了解一下2022-08-08