這篇文章主要為大家展示了“JAVA如何實(shí)現(xiàn)動態(tài)代理技術(shù)”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“JAVA如何實(shí)現(xiàn)動態(tài)代理技術(shù)”這篇文章吧。
在棲霞等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供成都做網(wǎng)站、網(wǎng)站制作、成都外貿(mào)網(wǎng)站建設(shè) 網(wǎng)站設(shè)計(jì)制作按需網(wǎng)站設(shè)計(jì),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),品牌網(wǎng)站建設(shè),全網(wǎng)整合營銷推廣,外貿(mào)營銷網(wǎng)站建設(shè),棲霞網(wǎng)站建設(shè)費(fèi)用合理。
一、引出動態(tài)代理
生活中代理應(yīng)該是很常見的,比如你可以通過代理商去買電腦,也可以直接找廠商買電腦,最終都是買到了電腦。程序中也一樣存在代理的情況,比如要為已經(jīng)存在的多個(gè)具有相同接口的目標(biāo)類的各個(gè)方法增加一些系統(tǒng)功能,例如:異常處理、日志、計(jì)算方法耗時(shí)等等,那么我們會怎么做呢?
1.會編寫一個(gè)與目標(biāo)類擁有相同接口的代理類,代理類的每個(gè)方法調(diào)用目標(biāo)類的相同方法,然后在調(diào)用方法前后加上系統(tǒng)功能所需要的代碼。
2.如果采用工廠模式或者配置文件的方式進(jìn)行管理,則不需要修改客戶端程序,在配置文件中配置是使用目標(biāo)類、還是代理類,這樣以后很容易切換。
樣例如下:
public class X{
public void sayHello(){
syso:say hello;
}
}
現(xiàn)在我要在這個(gè)方法之前添加一個(gè)時(shí)間,方法之后添加一個(gè)時(shí)間,計(jì)算這個(gè)方法執(zhí)行的時(shí)間一共是多少.如果我沒有得到sayHello源碼,那么我怎么做呢?寫一個(gè)代理:
public class XProxy
{
private X x;
public void sayHello
{
startTime:
x.syHello();
endTime;
}
}
說明:上面的是偽代碼。
把開始時(shí)間和結(jié)束時(shí)間放在這個(gè)方法的前后就可以了.
通常我們讓兩個(gè)方法實(shí)現(xiàn)同一個(gè)接口.那么client想用X也可以,想用XProxy也可以了.具體的原理圖,如下圖所示:
二、創(chuàng)建動態(tài)代理類
現(xiàn)在試想一下,上面只是代理了一個(gè)目標(biāo)類,如果多個(gè)目標(biāo)類,那么是不是要創(chuàng)建N多個(gè)代理類呢?那樣不是代碼太不靈活且笨重了。當(dāng)然不會。
java虛擬機(jī)可以在運(yùn)行期間動態(tài)生成類,這種類是以字節(jié)碼的形式生成出來的。這種動態(tài)生成的類往往呢就是代理類。即動態(tài)代理類。
JVM生成的動態(tài)代理類必須滿足一定的條件,這就是必須實(shí)現(xiàn)一個(gè)或多個(gè)接口。所以JVM生成的動態(tài)代理只能用作具有相同接口的目標(biāo)類的代理。(動態(tài)生成的類不是代理,我們只是吧這個(gè)類當(dāng)成代理來用。)
Proxy動態(tài)代理的API:
兩個(gè)參數(shù)應(yīng)該很容易理解:
第一個(gè)參數(shù):我們知道任何一個(gè)字節(jié)碼都是需要通過類加載器來加載的,那么這個(gè)動態(tài)生成的字節(jié)碼也不例外,需要給它一個(gè)類加載器。
第二個(gè)參數(shù):就是動態(tài)代理類生成,必須滿足的一個(gè)條件,需要實(shí)現(xiàn)一個(gè)或者多個(gè)接口,否則這個(gè)生成的類字節(jié)碼中就沒有方法了,沒有方法就失去了其功能意義。
下面我們動手來創(chuàng)建一個(gè)動態(tài)的代理類,大體思路為:
1.創(chuàng)建實(shí)現(xiàn)Collection接口的動態(tài)類和查看其名稱,分析Proxy.getProxyClass方法的各個(gè)參數(shù)
2.編碼列出動態(tài)類中的所有構(gòu)造方法和參數(shù)簽名
3.編碼列出動態(tài)類中的所有方法和參數(shù)簽名
4.創(chuàng)建動態(tài)類的實(shí)例對象:1)用反射獲取構(gòu)造方法 2)編寫一個(gè)最簡單的invocationHandle類 3)調(diào)用構(gòu)造方法創(chuàng)建動態(tài)類的實(shí)例對象,并將編寫的InvocationHandle類的實(shí)例對象傳遞進(jìn)去 4)打印創(chuàng)建對象和調(diào)用對象的沒有返回的方法和getClass方法,演示調(diào)用其他有返回值方法報(bào)告了異常。
5)將創(chuàng)建動態(tài)類的實(shí)例對象的代理寫成匿名內(nèi)部類方式,簡化代碼。
樣例分步實(shí)現(xiàn)如下:
(1)首先我們來完成前面3步:
package study.javaenhance;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
public class ProxyTest
{
public static void main(String[] args)
{
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
System.out.println(clazzProxy1.getName());
//上面輸出的為一個(gè)類,那么一個(gè)類肯定有其構(gòu)造方法和方法,下面我們來列出來
System.out.println("----------begin constructors list----------");
//1.列出構(gòu)造方法
Constructor[] constructors = clazzProxy1.getConstructors();
for (Constructor constructor : constructors)
{
String name = constructor.getName();
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('(');
Class[] clazzParams = constructor.getParameterTypes();
for (Class clazzParam : clazzParams) {
sBuilder.append(clazzParam.getName()).append(',');
}
if(clazzParams!=null && clazzParams.length != 0)
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(')');
System.out.println(sBuilder.toString());
}
//2.列出這個(gè)類字節(jié)碼中的所有方法
System.out.println("----------begin methods list----------");
Method[] methods = clazzProxy1.getMethods();
for(Method method : methods){
String name = method.getName();
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('(');
Class[] clazzParams = method.getParameterTypes();
for(Class clazzParam : clazzParams){
sBuilder.append(clazzParam.getName()).append(',');
}
if(clazzParams!=null && clazzParams.length != 0)
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(')');
System.out.println(sBuilder.toString());
}
}
}
輸出結(jié)果如下:
$Proxy0
----------begin constructors list----------
$Proxy0(java.lang.reflect.InvocationHandler)
----------begin methods list----------
add(java.lang.Object)
hashCode()
clear()
equals(java.lang.Object)
toString()
contains(java.lang.Object)
isEmpty()
addAll(java.util.Collection)
iterator()
size()
toArray([Ljava.lang.Object;)
toArray()
remove(java.lang.Object)
containsAll(java.util.Collection)
removeAll(java.util.Collection)
retainAll(java.util.Collection)
isProxyClass(java.lang.Class)
getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;)
getInvocationHandler(java.lang.Object)
newProxyInstance(java.lang.ClassLoader,[Ljava.lang.Class;,java.lang.reflect.InvocationHandler)
wait()
wait(long,int)
wait(long)
getClass()
notify()
notifyAll()
可以看到所有的方法均是來自Collection 和父類Object中的方法,符合我們的預(yù)期結(jié)果。接下來我們進(jìn)入第四步和第五步的實(shí)現(xiàn):
首先,我們來創(chuàng)建這個(gè)動態(tài)代理類的實(shí)例。那么直接clazzProxy1.newInstance();可不可以呢?顯然是不可以的嘛.我們剛剛說了,動態(tài)生成的這個(gè)代理類只有一個(gè)構(gòu)造方法,有沒有無參構(gòu)造方法呢?沒有啊.所以,創(chuàng)建 一個(gè)參數(shù)的構(gòu)造方法.參數(shù)類型是java.lang.reflect.InvocationHandler。
下面我們來實(shí)現(xiàn):
1.定義一個(gè)上述接口的實(shí)例:
package study.javaenhance;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
}
2.調(diào)用實(shí)現(xiàn)創(chuàng)建實(shí)例動態(tài)類:
//3.創(chuàng)建實(shí)例對象
System.out.println("----------begin create instance object----------");
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
Collection collection = (Collection) constructor.newInstance(myInvocationHandler);
System.out.println(collection);
collection.clear();
//collection.size(); //報(bào)錯(cuò),異常會發(fā)生,產(chǎn)生異常的原因在于其返回值為int類型,但是每一次調(diào)用一個(gè)方法都會調(diào)用到invoke方法,我們此時(shí)的invoke返回的為null,所以是沒有辦法轉(zhuǎn)換為int類型的。
3.我們采用匿名內(nèi)部類的方式優(yōu)化:
//3.1 采用匿名內(nèi)部類方式進(jìn)行創(chuàng)建
Collection collection2 = (Collection) constructor.newInstance(new InvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
});
System.out.println(collection2);
collection2.clear();
//collection2.size();
4.繼續(xù)優(yōu)化:思考如果我們每次都想上面方式去創(chuàng)建動態(tài)的代理類實(shí)在有點(diǎn)重復(fù),那么這個(gè)是JDK的Proxy類中提供了簡單的方法直接去創(chuàng)建動態(tài)代理類,方式如下:
//3.3 采用Proxy 中提供的簡單方法創(chuàng)建
Collection collection3 = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler()
{
private ArrayList target = new ArrayList();
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return method.invoke(target, args);
}
});
//System.out.println(collection3);
collection3.add("abc");
collection3.add("def");
collection3.add("hij");
System.out.println(collection3.size());
三、動態(tài)代理的原理簡單分析
上面我們創(chuàng)建了動態(tài)代理類,下面我們分析下代理的原理:
下面在來看一個(gè)問題:
動態(tài)代理的工作原理圖:
對上面的這個(gè)圖,我們簡單來說說:客戶端動態(tài)生成代理類,然后調(diào)用代理類的方法,代理類內(nèi)部調(diào)用handler.invoke()方法,在invoke中呢,我們又指向的目標(biāo)類.這樣就實(shí)現(xiàn)了代理了.我客戶端調(diào)用代理的什么方法,invoke就只向目標(biāo)類的同一個(gè)方法.而在指定目標(biāo)類方法的前后呢,我們還可以做其他的操作,比如記錄日志.圖中用圈圈出來的部分就是代理類自己實(shí)現(xiàn)的功能了.這就是代理類的原理.
我們來做最后一步,將上面的動態(tài)生成的代理類,編寫可生成代理和插入通告的通用方法:
test代碼:
package study.javaenhance;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
public class ProxyTest
{
public static void main(String[] args) throws Exception
{
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
System.out.println(clazzProxy1.getName());
//上面輸出的為一個(gè)類,那么一個(gè)類肯定有其構(gòu)造方法和方法,下面我們來列出來
System.out.println("----------begin constructors list----------");
//1.列出構(gòu)造方法
Constructor[] constructors = clazzProxy1.getConstructors();
for (Constructor constructor : constructors)
{
String name = constructor.getName();
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('(');
Class[] clazzParams = constructor.getParameterTypes();
for (Class clazzParam : clazzParams) {
sBuilder.append(clazzParam.getName()).append(',');
}
if(clazzParams!=null && clazzParams.length != 0)
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(')');
System.out.println(sBuilder.toString());
}
//2.列出這個(gè)類字節(jié)碼中的所有方法
System.out.println("----------begin methods list----------");
Method[] methods = clazzProxy1.getMethods();
for(Method method : methods){
String name = method.getName();
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('(');
Class[] clazzParams = method.getParameterTypes();
for(Class clazzParam : clazzParams){
sBuilder.append(clazzParam.getName()).append(',');
}
if(clazzParams!=null && clazzParams.length != 0)
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(')');
System.out.println(sBuilder.toString());
}
//3.創(chuàng)建實(shí)例對象
System.out.println("----------begin create instance object----------");
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
Collection collection = (Collection) constructor.newInstance(myInvocationHandler);
System.out.println(collection);
collection.clear();
//collection.size(); //報(bào)錯(cuò),異常會發(fā)生,產(chǎn)生異常的原因在于其返回值為int類型,但是每一次調(diào)用一個(gè)方法都會調(diào)用到invoke方法,我們此時(shí)的invoke返回的為null,所以是沒有辦法轉(zhuǎn)換為int類型的。
//3.1 采用匿名內(nèi)部類方式進(jìn)行創(chuàng)建
Collection collection2 = (Collection) constructor.newInstance(new InvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
});
System.out.println(collection2);
collection2.clear();
//collection2.size();
//3.3 采用Proxy 中提供的簡單方法創(chuàng)建
Collection collection3 = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler()
{
private ArrayList target = new ArrayList();
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return method.invoke(target, args);
}
});
//System.out.println(collection3);
collection3.add("abc");
collection3.add("def");
collection3.add("hij");
System.out.println(collection3.size());
System.out.println(collection3.getClass().getName());//這個(gè)返回的是
System.out.println("----------begin create instance object 抽化----------");
//3.4抽出動態(tài)代理讓目標(biāo)對象和切面對象都是傳入進(jìn)去的
final ArrayList target = new ArrayList();
Collection collection4 = (Collection)getProxy(target,new MyAdvice());
collection4.add("test1");
collection4.add("test2");
System.out.println(collection4.size());
}
private static Object getProxy(final Object target,final Advice advice) {
Object object = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
advice.beforeMethod(method);
Object retValue = method.invoke(target, args);
advice.afterMethod(method);
return retValue;
}
});
return object;
}
}
四、實(shí)現(xiàn)類似Spring的可配置的AOP框架
首先,我們要完成的要求如下:
我們來模擬Spring的工廠模式讀取從配置文件傳遞過來的類。如果這個(gè)類是一個(gè)普通類則直接返回。如果是一個(gè)代理類,則創(chuàng)建代理對象,返回代理類。
具體的理論知識可以看上面的圖片
首先創(chuàng)建一個(gè)BeanFactory.java類。這是一個(gè)Bean工廠,用于讀取配置文件中的類
package study.javaenhance.aopframework;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import study.javaenhance.Advice;
public class BeanFactory
{
private Properties properties = new Properties();
public BeanFactory(InputStream inStream)
{
try
{
properties.load(inStream);
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if(inStream != null)
{
try
{
inStream.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
public Object getBean(String name)
{
String className = properties.getProperty(name);
Object bean = null;
try
{
Class clazz = Class.forName(className);
bean = clazz.newInstance();
if(bean instanceof ProxyFactoryBean)
{
ProxyFactoryBean proxyBean = (ProxyFactoryBean) bean;
Advice advice = (Advice)Class.forName(properties.getProperty(name + ".advice")).newInstance();
Object target = Class.forName(properties.getProperty(name + ".target")).newInstance();
proxyBean.setAdvice(advice);
proxyBean.setTarget(target);
Object proxy = proxyBean.getProxy();
return proxy;
}
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return bean;
}
}
如果這個(gè)類中包含ProxyFactoryBean,則調(diào)用ProxyFactoryBean中的getProxy方法。動態(tài)生成代理類。
ProxyFactoryBean.java
package study.javaenhance.aopframework;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import study.javaenhance.Advice;
public class ProxyFactoryBean
{
private Object target;
private Advice advice;
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Advice getAdvice() {
return advice;
}
public void setAdvice(Advice advice) {
this.advice = advice;
}
public Object getProxy()
{
Object object = Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),
new InvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
advice.beforeMethod(method);
Object retValue = method.invoke(target, args);
advice.afterMethod(method);
return retValue;
}
});
return object;
}
}
接下來創(chuàng)建一個(gè)config.Properties文件.config.Properties
xxx=java.util.ArrayList
#xxx=study.javaenhance.aopframework.ProxyFactoryBean
xxx.advice=study.javaenhance.MyAdvice
xxx.target=java.util.ArrayList
最后建立測試類:
package study.javaenhance.aopframework;
import java.io.InputStream;
import java.util.Collection;
public class AopFrameworkTest
{
public static void main(String[] args)
{
InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");
Object bean = new BeanFactory(ips).getBean("xxx");
System.out.println(bean.getClass().getName());
((Collection)bean).clear();
}
}
以上是“JAVA如何實(shí)現(xiàn)動態(tài)代理技術(shù)”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
網(wǎng)頁名稱:JAVA如何實(shí)現(xiàn)動態(tài)代理技術(shù)
網(wǎng)頁鏈接:http://aaarwkj.com/article38/gjoipp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站改版、自適應(yīng)網(wǎng)站、網(wǎng)站設(shè)計(jì)、網(wǎng)頁設(shè)計(jì)公司、做網(wǎng)站、App開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)