java兩種動態代理demo(java中的動態代理)

本文目錄一覽:

JAVA動態代理設計原理及如何實現

ava動態代理機制的出現,使得Java開發人員不用手工編寫代理類,只要簡單地制定一組接口及委託類對象,便能動態地獲得代理類。代理類會負責將所有的方法調用分配到委託對象上反射執行,配置執行過程中,開發人員還可以進行修改

代理設計模式

代理是一種常用的設計模式,其目的就是為其他對象提供一個代理以控制對某個對象的訪問。代理類負責為委託類預處理消息、過濾消息並轉發消息,以及進行消息被委託類執行後的後續處理。

1.

為了保持行為的一致性,代理類和委託類通常會實現相同的接口

2.

引入代理能夠控制對委託對象的直接訪問,可以很好的隱藏和保護委託對象,也更加具有靈活性

相關的類和接口

要了解

Java

動態代理的機制,首先需要了解以下相關的類或接口:

1.

java.lang.reflect.Proxy:這是

Java

動態代理機制的主類,它提供了一組靜態方法來為一組接口動態地生成代理類及其對象

2.

java.lang.reflect.InvocationHandler:這是調用處理器接口,它自定義了一個invoke方法,用於幾種處理在動態代理類對象上的方法調用。通常在該方法中實現對委託類的代理訪問。

3.

java.lang.ClassLoader:Proxy

靜態方法生成動態代理類同樣需要通過類裝載器來進行裝載才能使用,它與普通類的唯一區別就是其字節碼是由

JVM

在運行時動態生成的而非預存在於任何一個.class

文件中。

代理機制及其特點

首先讓我們來了解一下如何使用

Java

動態代理。具體有如下四步驟:

1.

通過實現

InvocationHandler

接口創建自己的調用處理器;

2.

通過為

Proxy

類指定

ClassLoader

對象和一組

interface

來創建動態代理類;

3.

通過反射機制獲得動態代理類的構造函數,其唯一參數類型是調用處理器接口類型;

4.

通過構造函數創建動態代理類實例,構造時調用處理器對象作為參數被傳入。

java靜態代理與動態代理的區別

JAVA的靜態代理與動態代理比較

1.靜態代理類:

由程序員創建或由特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了。動態代理類:在程序運行時,運用反射機制動態創建而成。

由此可見,代理類可以為委託類預處理消息、把消息轉發給委託類和事後處理消息等。

例程1 HelloService.java

package proxy;

import java.util.Date;

public interface HelloService{

public String echo(String msg);

public Date getTime();

}

2.動態代理類

與靜態代理類對照的是動態代理類,動態代理類的字節碼在程序運行時由Java反射機制動態生成,無需程序員手工編寫它的源代碼。動態代理類不僅簡化了編程工作,而且提高了軟件系統的可擴展性,因為Java 反射機制可以生成任意類型的動態代理類。java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動態代理類的能力。

Proxy類提供了創建動態代理類及其實例的靜態方法。

(1)getProxyClass()靜態方法負責創建動態代理類,它的完整定義如下:

public static Class? getProxyClass(ClassLoader loader, Class?[] interfaces) throws IllegalArgumentException

參數loader 指定動態代理類的類加載器,參數interfaces 指定動態代理類需要實現的所有接口。

(2)newProxyInstance()靜態方法負責創建動態代理類的實例,它的完整定義如下:

public static Object newProxyInstance(ClassLoader loader, Class?[] interfaces, InvocationHandler handler) throws

IllegalArgumentException

參數loader 指定動態代理類的類加載器,參數interfaces 指定動態代理類需要實現的所有接口,參數handler 指定與動態代理類關聯的 InvocationHandler 對象。

以下兩種方式都創建了實現Foo接口的動態代理類的實例:

/**** 方式一 ****/

//創建InvocationHandler對象

InvocationHandler handler = new MyInvocationHandler(…);

//創建動態代理類

Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class });

//創建動態代理類的實例

Foo foo = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).

newInstance(new Object[] { handler });

/**** 方式二 ****/

//創建InvocationHandler對象

InvocationHandler handler = new MyInvocationHandler(…);

//直接創建動態代理類的實例

Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class[] { Foo.class }, handler);

由Proxy類的靜態方法創建的動態代理類具有以下特點:

動態代理類是public、final和非抽象類型的;

動態代理類繼承了java.lang.reflect.Proxy類;

動態代理類的名字以“$Proxy”開頭;

動態代理類實現getProxyClass()和newProxyInstance()方法中參數interfaces指定的所有接口;

java 動態代理怎麼理解

JAVA的靜態代理與動態代理比較

一、概念

代理模式是常用的Java 設計模式,它的特徵是代理類與委託類有同樣的接口,代理類主要負責為委託類預處理消息、過濾消息、把消息轉發給委託類,以及事後處理消息等。代理類與委託類之間通常會存在關聯關係,一個代理類的對象與一個委託類的對象關聯,代理類的對象本身並不真正實現服務,而是通過調用委託類的對象的相關方法,來提供特定的服務。按照代理類的創建時期,代理類可分為兩種。

靜態代理類:

由程序員創建或由特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了。動態代理類:在程序運行時,運用反射機制動態創建而成。

二、靜態代理類

如下, HelloServiceProxy 類是代理類,HelloServiceImpl類是委託類,這兩個類都實現了HelloService接口。其中HelloServiceImpl類是HelloService接口的真正實現者,而HelloServiceProxy類是通過調用HelloServiceImpl 類的相關方法來提供特定服務的。HelloServiceProxy類的echo()方法和getTime()方法會分別調用被代理的HelloServiceImpl 對象的echo()方法和getTime()方法,並且在方法調用前後都會執行一些簡單的打印操作。

由此可見,代理類可以為委託類預處理消息、把消息轉發給委託類和事後處理消息等。

例程1 HelloService.java

package proxy;

import java.util.Date;

public interface HelloService{

public String echo(String msg);

public Date getTime();

}

例程2 HelloServiceImpl.java

package proxy;

import java.util.Date;

public class HelloServiceImpl implements HelloService{

public String echo(String msg){

return “echo:”+msg;

}

public Date getTime(){

return new Date();

}

}

例程3 HelloServiceProxy.java

package proxy;

import java.util.Date;

public class HelloServiceProxy implements HelloService{

private HelloService helloService; //表示被代理的HelloService 實例

public HelloServiceProxy(HelloService helloService){

this.helloService=helloService;

}

public void setHelloServiceProxy(HelloService helloService){

this.helloService=helloService;

}

public String echo(String msg){

System.out.println(“before calling echo()”); //預處理

String result=helloService.echo(msg); //調用被代理的HelloService 實例的echo()方法

System.out.println(“after calling echo()”); //事後處理

return result;

}

public Date getTime(){

System.out.println(“before calling getTime()”); //預處理

Date date=helloService.getTime(); //調用被代理的HelloService 實例的getTime()方法

System.out.println(“after calling getTime()”); //事後處理

return date;

}

}

在Client1 類的main()方法中,先創建了一個HelloServiceImpl對象,又創建了一個HelloServiceProxy對象,最後調用HelloServiceProxy對象的echo()方法。

例程4 Client1.java

package proxy;

public class Client1{

public static void main(String args[]){

HelloService helloService=new HelloServiceImpl();

HelloService helloServiceProxy=new HelloServiceProxy(helloService);

System.out.println(helloServiceProxy.echo(“hello”));

}

}

運行Client1 類,打印結果如下:

before calling echo()

after calling echo()

echo:hello

例程3 的HelloServiceProxy 類的源代碼是由程序員編寫的,在程序運行前,它的.class文件就已經存在了,這種代理類稱為靜態代理類。

三、動態代理類

與靜態代理類對照的是動態代理類,動態代理類的字節碼在程序運行時由Java反射機制動態生成,無需程序員手工編寫它的源代碼。動態代理類不僅簡化了編程工作,而且提高了軟件系統的可擴展性,因為Java 反射機制可以生成任意類型的動態代理類。java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動態代理類的能力。

Proxy類提供了創建動態代理類及其實例的靜態方法。

(1)getProxyClass()靜態方法負責創建動態代理類,它的完整定義如下:

public static Class getProxyClass(ClassLoader loader, Class[] interfaces) throws IllegalArgumentException

參數loader 指定動態代理類的類加載器,參數interfaces 指定動態代理類需要實現的所有接口。

(2)newProxyInstance()靜態方法負責創建動態代理類的實例,它的完整定義如下:

public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler handler) throws

IllegalArgumentException

參數loader 指定動態代理類的類加載器,參數interfaces 指定動態代理類需要實現的所有接口,參數handler 指定與動態代理類關聯的 InvocationHandler 對象。

以下兩種方式都創建了實現Foo接口的動態代理類的實例:

/**** 方式一 ****/

//創建InvocationHandler對象

InvocationHandler handler = new MyInvocationHandler(…);

//創建動態代理類

Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class });

//創建動態代理類的實例

Foo foo = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).

newInstance(new Object[] { handler });

/**** 方式二 ****/

//創建InvocationHandler對象

InvocationHandler handler = new MyInvocationHandler(…);

//直接創建動態代理類的實例

Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class[] { Foo.class }, handler);

由Proxy類的靜態方法創建的動態代理類具有以下特點:

動態代理類是public、final和非抽象類型的;

動態代理類繼承了java.lang.reflect.Proxy類;

動態代理類的名字以“$Proxy”開頭;

動態代理類實現getProxyClass()和newProxyInstance()方法中參數interfaces指定的所有接口;

Proxy 類的isProxyClass(Class cl)靜態方法可用來判斷參數指定的類是否為動態代理類。只有通過Proxy類創建的類才是動態代理類;

動態代理類都具有一個public 類型的構造方法,該構造方法有一個InvocationHandler 類型的參數。

由Proxy類的靜態方法創建的動態代理類的實例具有以下特點:

1. 假定變量foo 是一個動態代理類的實例,並且這個動態代理類實現了Foo 接口,那麼“foo instanceof Foo”的值為true。把變量foo強制轉換為Foo類型是合法的:

(Foo) foo //合法

2.每個動態代理類實例都和一個InvocationHandler 實例關聯。Proxy 類的getInvocationHandler(Object proxy)靜態方法返回與參數proxy指定的代理類實例所關聯的InvocationHandler 對象。

3.假定Foo接口有一個amethod()方法,那麼當程序調用動態代理類實例foo的amethod()方法時,該方法會調用與它關聯的InvocationHandler 對象的invoke()方法。

InvocationHandler 接口為方法調用接口,它聲明了負責調用任意一個方法的invoke()方法:

Object invoke(Object proxy,Method method,Object[] args) throws Throwable

參數proxy指定動態代理類實例,參數method指定被調用的方法,參數args 指定向被調用方法傳遞的參數,invoke()方法的返回值表示被調用方法的返回值。

四、最後看一個實例:

HelloServiceProxyFactory 類的getHelloServiceProxy()靜態方法負責創建實現了HelloService接口的動態代理類的實例。

例程5 HelloServiceProxyFactory.java

package proxy;

import java.lang.reflect.*;

public class HelloServiceProxyFactory {

/** 創建一個實現了HelloService 接口的動態代理類的實例

* 參數helloService 引用被代理的HelloService 實例

*/

public static HelloService getHelloServiceProxy(final HelloService helloService){

//創建一個實現了InvocationHandler接口的匿名類的實例

InvocationHandler handler=new InvocationHandler(){

public Object invoke(Object proxy,Method method,Object args[])throws Exception{

System.out.println(“before calling “+method); //預處理

Object result=method.invoke(helloService,args);

//調用被代理的HelloService 實例的方法

System.out.println(“after calling “+method); //事後處理

return result;

}

};

Class classType=HelloService.class;

return (HelloService)Proxy.newProxyInstance(classType.getClassLoader(),

new Class[]{classType},

handler);

}

}

如下所示的Client2 類先創建了一個HelloServiceImpl 實例,然後創建了一個動態代理類實例helloServiceProxy,最後調用動態代理類實例的echo()方法。

例程6 Client2.java

package proxy;

public class Client2{

public static void main(String args[]){

HelloService helloService=new HelloServiceImpl();

HelloService helloServiceProxy=HelloServiceProxyFactory.getHelloServiceProxy(helloService);

System.out.println(“動態代理類的名字為”+helloServiceProxy.getClass().getName());

System.out.println(helloServiceProxy.echo(“Hello”));

}

}

運行Client2,打印結果如下:

動態代理類的名字為$Proxy0

before calling public abstract java.lang.String proxy.HelloService.echo(java.lang.String)

after calling public abstract java.lang.String proxy.HelloService.echo(java.lang.String)

echo:Hello

從結果看出,動態代理類的名字為$Proxy0。

PostScript

java動態代理有什麼應用,舉幾個例子看看,可以的話解釋一下

import java.lang.reflect.Proxy;

A. 創建一個實現接口InvocationHandler的類,他必須實現invoke方法

B. 創建被代理的類以及接口。

C. 通過Proxy的靜態方法newProxyInstance(ClassLoader loader,Class【】interfaces,InvocationHandler handler)創建一個代理

D. 通過代理調用方法。

java動態代理:是在運行是生成的class對象,在生成時必須提供一組或一個interface給它,然後該class就宣稱它實現了這些interface。你當然可以把該class的實例當做這些interface中的任何一個來用,當然,這個DynamicProxy其實就是一個Proxy,他不會替你做實質性的工作,在生成它的實例時你必須提供一個handler,由它接管實際的工作。因此,DynamicProxy必須實現InvocationHandler接口。

5) 一個動態代理了和一個InvocationHandler 實現關聯的。每一個動態代理實例的調用都要通過InvocationHandler接口的handler(調用處理器)來調用,動態代理不做任何執行操作,只是在創建動態代理時,把要實現的接口和handler關聯,動態代理要幫助被代理執行的任務,要轉交給handler來執行。其實就是調用invoke方法。

如何獲得Java動態代理的代理類

AOP的攔截功能是由java中的動態代理來實現的。說白了,就是在目標類的基礎上增加切面邏輯,生成增強的目標類(該切面邏輯或者在目標類函數執行之前,或者目標類函數執行之後,或者在目標類函數拋出異常時候執行。不同的切入時機對應不同的Interceptor的種類,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。

那麼動態代理是如何實現將切面邏輯(advise)織入到目標類方法中去的呢?下面我們就來詳細介紹並實現AOP中用到的兩種動態代理。

AOP的源碼中用到了兩種動態代理來實現攔截切入功能:jdk動態代理和cglib動態代理。兩種方法同時存在,各有優劣。jdk動態代理是由Java內部的反射機制來實現的,cglib動態代理底層則是藉助asm來實現的。總的來說,反射機制在生成類的過程中比較高效,而asm在生成類之後的相關執行過程中比較高效(可以通過將asm生成的類進行緩存,這樣解決asm生成類過程低效問題)。還有一點必須注意:jdk動態代理的應用前提,必須是目標類基於統一的接口。如果沒有上述前提,jdk動態代理不能應用。由此可以看出,jdk動態代理有一定的局限性,cglib這種第三方類庫實現的動態代理應用更加廣泛,且在效率上更有優勢。。

1、定義接口和實現

[java] view plain copy print?

package com.meituan.hyt.test3.service;

public interface UserService {

public String getName(int id);

public Integer getAge(int id);

}

[java] view plain copy print?

package com.meituan.hyt.test3.service.impl;

import com.meituan.hyt.test3.service.UserService;

public class UserServiceImpl implements UserService {

@Override

public String getName(int id) {

System.out.println(“——getName——“);

return “Tom”;

}

@Override

public Integer getAge(int id) {

System.out.println(“——getAge——“);

return 10;

}

}

2、jdk動態代理實現

[java] view plain copy print?

package com.meituan.hyt.test3.jdk;

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;

}

@Override

public Object invoke(Object o, Method method, Object[] args) throws Throwable {

if(“getName”.equals(method.getName())){

System.out.println(“++++++before ” + method.getName() + “++++++”);

Object result = method.invoke(target, args);

System.out.println(“++++++after ” + method.getName() + “++++++”);

return result;

}else{

Object result = method.invoke(target, args);

return result;

}

}

}

[java] view plain copy print?

package com.meituan.hyt.test3.jdk;

import com.meituan.hyt.test3.service.UserService;

import com.meituan.hyt.test3.service.impl.UserServiceImpl;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Proxy;

public class Main1 {

public static void main(String[] args) {

UserService userService = new UserServiceImpl();

InvocationHandler invocationHandler = new MyInvocationHandler(userService);

UserService userServiceProxy = (UserService)Proxy.newProxyInstance(userService.getClass().getClassLoader(),

userService.getClass().getInterfaces(), invocationHandler);

System.out.println(userServiceProxy.getName(1));

System.out.println(userServiceProxy.getAge(1));

}

}

運行結果

++++++before getName++++++

——getName——

++++++after getName++++++

Tom

——getAge——

10

3、cglib動態代理實現

Cglib是一個優秀的動態代理框架,它的底層使用ASM在內存中動態的生成被代理類的子類,使用CGLIB即使代理類沒有實現任何接口也可以實現動態代理功能。CGLIB具有簡單易用,它的運行速度要遠遠快於JDK的Proxy動態代理:

CGLIB的核心類:

net.sf.cglib.proxy.Enhancer – 主要的增強類

net.sf.cglib.proxy.MethodInterceptor – 主要的方法攔截類,它是Callback接口的子接口,需要用戶實現

net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method類的代理類,可以方便的實現對源對象方法的調用,如使用:

Object o = methodProxy.invokeSuper(proxy, args);//雖然第一個參數是被代理對象,也不會出現死循環的問題。

net.sf.cglib.proxy.MethodInterceptor接口是最通用的回調(callback)類型,它經常被基於代理的AOP用來實現攔截(intercept)方法的調用。這個接口只定義了一個方法

public Object intercept(Object object, java.lang.reflect.Method method,

Object[] args, MethodProxy proxy) throws Throwable;

第一個參數是代理對像,第二和第三個參數分別是攔截的方法和方法的參數。原來的方法可能通過使用java.lang.reflect.Method對象的一般反射調用,或者使用 net.sf.cglib.proxy.MethodProxy對象調用。net.sf.cglib.proxy.MethodProxy通常被首選使用,因為它更快。

[java] view plain copy print?

package com.meituan.hyt.test3.cglib;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor {

@Override

public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

System.out.println(“++++++before ” + methodProxy.getSuperName() + “++++++”);

System.out.println(method.getName());

Object o1 = methodProxy.invokeSuper(o, args);

System.out.println(“++++++before ” + methodProxy.getSuperName() + “++++++”);

return o1;

}

}

[java] view plain copy print?

package com.meituan.hyt.test3.cglib;

import com.meituan.hyt.test3.service.UserService;

import com.meituan.hyt.test3.service.impl.UserServiceImpl;

import net.sf.cglib.proxy.Enhancer;

public class Main2 {

public static void main(String[] args) {

CglibProxy cglibProxy = new CglibProxy();

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(UserServiceImpl.class);

enhancer.setCallback(cglibProxy);

UserService o = (UserService)enhancer.create();

o.getName(1);

o.getAge(1);

}

}

運行結果:

++++++before CGLIB$getName$0++++++

getName

——getName——

++++++before CGLIB$getName$0++++++

++++++before CGLIB$getAge$1++++++

getAge

——getAge——

++++++before CGLIB$getAge$1++++++

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/296290.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-27 12:58
下一篇 2024-12-27 12:58

相關推薦

  • Java JsonPath 效率優化指南

    本篇文章將深入探討Java JsonPath的效率問題,並提供一些優化方案。 一、JsonPath 簡介 JsonPath是一個可用於從JSON數據中獲取信息的庫。它提供了一種DS…

    編程 2025-04-29
  • java client.getacsresponse 編譯報錯解決方法

    java client.getacsresponse 編譯報錯是Java編程過程中常見的錯誤,常見的原因是代碼的語法錯誤、類庫依賴問題和編譯環境的配置問題。下面將從多個方面進行分析…

    編程 2025-04-29
  • Java Bean加載過程

    Java Bean加載過程涉及到類加載器、反射機制和Java虛擬機的執行過程。在本文中,將從這三個方面詳細闡述Java Bean加載的過程。 一、類加載器 類加載器是Java虛擬機…

    編程 2025-04-29
  • QML 動態加載實踐

    探討 QML 框架下動態加載實現的方法和技巧。 一、實現動態加載的方法 QML 支持從 JavaScript 中動態指定需要加載的 QML 組件,並放置到運行時指定的位置。這種技術…

    編程 2025-04-29
  • Java騰訊雲音視頻對接

    本文旨在從多個方面詳細闡述Java騰訊雲音視頻對接,提供完整的代碼示例。 一、騰訊雲音視頻介紹 騰訊雲音視頻服務(Cloud Tencent Real-Time Communica…

    編程 2025-04-29
  • Java Milvus SearchParam withoutFields用法介紹

    本文將詳細介紹Java Milvus SearchParam withoutFields的相關知識和用法。 一、什麼是Java Milvus SearchParam without…

    編程 2025-04-29
  • Java 8中某一周的周一

    Java 8是Java語言中的一個版本,於2014年3月18日發布。本文將從多個方面對Java 8中某一周的周一進行詳細的闡述。 一、數組處理 Java 8新特性之一是Stream…

    編程 2025-04-29
  • Java判斷字符串是否存在多個

    本文將從以下幾個方面詳細闡述如何使用Java判斷一個字符串中是否存在多個指定字符: 一、字符串遍歷 字符串是Java編程中非常重要的一種數據類型。要判斷字符串中是否存在多個指定字符…

    編程 2025-04-29
  • VSCode為什麼無法運行Java

    解答:VSCode無法運行Java是因為默認情況下,VSCode並沒有集成Java運行環境,需要手動添加Java運行環境或安裝相關插件才能實現Java代碼的編寫、調試和運行。 一、…

    編程 2025-04-29
  • Java任務下發回滾系統的設計與實現

    本文將介紹一個Java任務下發回滾系統的設計與實現。該系統可以用於執行複雜的任務,包括可回滾的任務,及時恢復任務失敗前的狀態。系統使用Java語言進行開發,可以支持多種類型的任務。…

    編程 2025-04-29

發表回復

登錄後才能評論