当前位置:首页 > CN2资讯 > 正文内容

从坦克聊聊代理模式之动态代理2

2天前CN2资讯


本文可作为 北京尚学堂设计模式的 学习笔记
在上一节文章里 我们能让Proxy类代理Imoveable接口 但是也只有这一个接口 别的还不行

今天我们让它能代理所有接口

(在看这篇文章之前 请一定先看看上一篇)

先看看项目目录



那第一步就是分析接口逻辑 在上一篇文章里 我们要在坦克移动前后记录时间 把硬代码写到了字符串里 这个就不可以改变了


为了做出在被代理对象前后做操作的逻辑 我们写出下面这个类

package proxy;

import java.lang.reflect.Method;

public class TimeInvHan implements InvocationHandler {

public TimeInvHan(Object object) {
super();
this.obj = object;
}

Object obj;

@Override
public void invoke(Method m, Object o) {

long start = System.currentTimeMillis();
System.out.println("starttime:" + start);
try {
m.invoke(obj, new Object[]{});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();

}
long end = System.currentTimeMillis();
System.out.println("end time:" + end);
System.out.println("expend time:" + (end - start));

}
}

TimeInvHan 里面有个Object类型的属性obj 它所代表的就是被代理的对象 (就是最原始的那个坦克)
再看invoke 这个方法
Method m 这是我们被代理对象的方法 (在说简单点就是 move方法)
Object o 这个参数我们类里没有用到 (我们所讨论的情况 比较简单 但是在复杂的情况下这个o就会用到了)
m.invoke(obj, new Object[]{});
这行代码就更简单了 就是调用obj的m方法 其参数为new Object[]{} ----(就是为空)
然后我们在这个核心代码前后加上 我们要的逻辑 --记录时间

InvocationHandler 这个接口也很简单 如下

package proxy;

import java.lang.reflect.Method;

public interface InvocationHandler {

void invoke(Method m,Object o);
}



(大家肯定还很疑惑 这个InvocationHandler到底是干什么的 先别急 往下看)



那第二步就是修改Proxy里面的那个字符串 改接口



newInstance 里面的参数Class 就是接口 在上面的例子中就是Imoveable


public static Object newInstance(Class h,InvocationHandler in) throws Exception{
String rt = "\r\n";
String methodstr="";
Method[] methods=h.getMethods();
for(Method m:methods){
methodstr+="@Override"+rt;
methodstr+="public "+m.getReturnType()+" "+m.getName()+"() { "+rt
+" try{" + rt
+" Method md = " + h.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt
+" h.invoke(md,"+"this);" +rt
+" }catch(NoSuchMethodException | SecurityException e){"+rt
+" e.printStackTrace();" +rt
+" }" + rt
+" }";
}
String src = "package proxy.compiler.test;" + rt
+ "import proxy.InvocationHandler;"+rt
+ " import java.lang.reflect.Method;"+rt
+ "public class Proxy1 implements "+ h.getName() + "{" + rt
+ " public Proxy1(InvocationHandler t) {" + rt
+ " super();" + rt
+" this.h = t;" + rt
+ " }" + rt +


" InvocationHandler h;" + rt +
methodstr+rt+
"}";

}
........
}

String methodstr="";


        Method[] methods=h.getMethods();


        for(Method m:methods){


            methodstr+="@Override"+rt;


            methodstr+="public "+m.getReturnType()+" "+m.getName()+"() { "+rt


                    +"   try{" + rt


                    +"      Method md = " + h.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt


                    +"      h.invoke(md,"+"this);" +rt


                    +"      }catch(NoSuchMethodException | SecurityException e){"+rt


                    +"         e.printStackTrace();" +rt


                    +"   }" + rt


                    +"  }";


        }

上面这部分就是将h这个接口里面的所有方法都放入字符串中



下面是proxy的全部代码

package proxy;

import java.io.File;
import java.io.FileWriter;

import java.lang.reflect.Constructor;

import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

public class Proxy {
public static Object newInstance(Class h,InvocationHandler in) throws Exception{
String rt = "\r\n";
String methodstr="";
Method[] methods=h.getMethods();
for(Method m:methods){
methodstr+="@Override"+rt;
methodstr+="public "+m.getReturnType()+" "+m.getName()+"() { "+rt
+" try{" + rt
+" Method md = " + h.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt
+" h.invoke(md,"+"this);" +rt
+" }catch(NoSuchMethodException | SecurityException e){"+rt
+" e.printStackTrace();" +rt
+" }" + rt
+" }";

}
String src = "package proxy.compiler.test;" + rt
+ "import proxy.InvocationHandler;"+rt
+ " import java.lang.reflect.Method;"+rt
+ "public class Proxy1 implements "+ h.getName() + "{" + rt
+ " public Proxy1(InvocationHandler t) {" + rt
+ " super();" + rt
+" this.h = t;" + rt
+ " }" + rt +


" InvocationHandler h;" + rt +
methodstr+rt+
"}";


String fileName ="d:/src/proxy/compiler/test/Proxy1.java";

File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();

//compoler
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = javaCompiler
.getStandardFileManager(null, null, null);
Iterable units = fileManager.getJavaFileObjects(fileName);
CompilationTask task = javaCompiler.getTask(null, fileManager, null,
null, null, units);
task.call();
fileManager.close();

// load into memory and create an instance
URL[] urls = new URL[] {new URL("file:/" + "d:/src/")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("proxy.compiler.test.Proxy1");


Constructor cons= c.getConstructor(InvocationHandler.class);
Object m=cons.newInstance(in);
return m;
}
}


//这是测试代码
package proxy;

public class Client {
public static void main(String[] args) throws Exception {
Say a=new SayHelloOrCurse();
InvocationHandler in= new HelloLogInvHan(a);

Say s=(Say)Proxy.newInstance(Say.class, in);
s.hello();
}
}
package proxy;

public class SayHelloOrCurse implements Say {

@Override
public void hello() {
// TODO Auto-generated method stub
System.out.println("hello world");

}

@Override
public void curse() {
// TODO Auto-generated method stub
System.out.println("i curse you");
}
}
package proxy;

public interface Say {
void hello();
void curse();
}

测试结果如下
start log:
hello world
end log:

在Proxy.newInstance中
我们把类保存到了如下的位置
String fileName ="d:/src/proxy/compiler/test/Proxy1.java";
在相应位置可以找到Proxy1 如下

package proxy.compiler.test;
import proxy.InvocationHandler;
import java.lang.reflect.Method;
public class Proxy1 implements proxy.Say{
public Proxy1(InvocationHandler t) {
super();
this.h = t;
}
InvocationHandler h;
@Override
public void hello() {
try{
Method md = proxy.Say.class.getMethod("hello");
h.invoke(md,this);
}catch(NoSuchMethodException | SecurityException e){
e.printStackTrace();
}
}@Override
public void curse() {
try{
Method md = proxy.Say.class.getMethod("curse");
h.invoke(md,this);
}catch(NoSuchMethodException | SecurityException e){
e.printStackTrace();
}
}
}


这下大家应该明白了吧


在client测试时


Say s=(Say)Proxy.newInstance(Say.class, in);


这个s实际上就是Proxy1的一个实例对象


而相应的InvocationHandler在Proxy.newinstance的最后几行代码里已经注入了


至于InvocationHandler的invoke方法在文章上面已经给出


这下大家应该明白InvocationHandler的作用了吧



写到这里 我们其实就已经模拟了jdk动态代理的机理 当然我们这只是最原始的模拟 很多细节方面还没有考虑到


但最核心的原理 就是上面所说的那个



现在 我们用一次真实的jdk动态代理



package JDKProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class LogInvHan implements InvocationHandler {

public LogInvHan(Object object) {
super();
this.obj = object;
}

Object obj;

@Override
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
System.out.println("start log:" );
try {
method.invoke(obj, new Object[]{});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("end log:" );
return null;
}
}


很疑惑public Object invoke(Object proxy, Method method, Object[] args)


这个proxy参数似乎没有用 我们的被代理对象在构造函数里已经传进来了


那它有什么用 我自己也没搞清楚 哪位朋友清楚的 请告诉我一声


package JDKProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class JdkProxyTest {

public static void main(String[] args) {
Imoveable t=new Tank();
InvocationHandler h=new LogInvHan(t);

Imoveable m=(Imoveable)Proxy.newProxyInstance(t.getClass().getClassLoader(),
t.getClass().getInterfaces(), h);

InvocationHandler ha=new TimeInvHan(m);
Imoveable m2=(Imoveable)Proxy.newProxyInstance(t.getClass().getClassLoader(),
t.getClass().getInterfaces(), ha);
m2.move();
}
}


这是多重代理


测试结果


starttime:1406365283656


start log:


i can move...


end log:


end time:1406365284343


expend time:687


其实这个多重代理 之前我是这样写的


       

Imoveable t=new Tank();
InvocationHandler h=new LogInvHan(t);
InvocationHandler ha=new TimeInvHan(h);

Imoveable m=(Imoveable)Proxy.newProxyInstance(t.getClass().getClassLoader(),
t.getClass().getInterfaces(), ha);
m.move();



在TimeInvHan的invoke方法里面


method.invoke(obj, new Object[]{});


这句代码里报这个错误


java.lang.IllegalArgumentException: object is not an instance of declaring class


改成如上那个才ok了  


大家对这个错误怎么看?












    你可能想看:

    扫描二维码推送至手机访问。

    版权声明:本文由皇冠云发布,如需转载请注明出处。

    本文链接:https://www.idchg.com/info/24484.html

    分享给朋友:

    “从坦克聊聊代理模式之动态代理2” 的相关文章

    如何选择高性能、美西VPS服务商: 比较、评测及优化建议

    美西VPS概述 美西VPS,简单来说,就是那些位于美国西部地区的虚拟专用服务器,像在洛杉矶这样的城市里。这些服务器给用户提供了一种灵活而高效的托管解决方案,特别是对于需要快速访问和低延迟连接的用户群体。美西VPS的设计理念是为用户提供高性能和高可靠性的服务,同时确保在数据传输时的安全性。 美西VPS...

    亿速云:构建安全、高效的云计算解决方案

    在当今数字化快速发展的时代,云计算服务的重要性愈发显著。我了解到,亿速云正是一家在这一领域崭露头角的企业,专注于提供高品质的云计算服务。自成立以来,亿速云围绕创新技术和卓越服务,努力帮助各行各业的用户实现数字化转型和升级。作为新一代云计算服务商,亿速云把握时代脉搏,致力于为全球用户提供稳定、安全、高...

    VPS IP被封怎么办?常见原因与解决方案分析

    在使用VPS的过程中,有时候会遇到VPS IP被封的情况,这对很多用户来说,会带来一些困扰。虽然情况不同,但通常来说,下列几个原因是比较常见的,了解这些原因能够帮助我们更好地避免封禁。 首先,敏感操作常常是导致IP被封的重要原因之一。比如,如果你进行了一些网络爬虫、频繁请求某个网站内容,或者试图访问...

    香港 CN2 GIA: 提升国际网络连接的高质量解决方案

    CN2 GIA 的定义与背景 在数字时代,网络的质量直接影响着我们的生活。香港 CN2 GIA是中国电信推出的一项高品质专线服务,它为企业和个人提供高速、稳定的国际互联网连接。CN2的全称是“China Next Generation Internet”,而GIA则代表“Global Interne...

    CloudFront在病毒防护中的应用与安全策略解析

    在互联网时代,内容分发网络(CDN)扮演着至关重要的角色。CloudFront便是亚马逊提供的一款优秀的CDN服务。简单来说,CloudFront分发数据、视频、应用程序和API,可以通过全球的边缘位置快速传递内容。这种网络架构的设计使得用户能够从离他们最近的服务器获取信息,从而提升了访问速度和用户...

    云桌面怎么实现:全面解析云桌面部署的关键步骤与安全策略

    云桌面是一个非常有意思的概念,它将传统的桌面环境转移到云端。不再需要用户在本地计算机上安装复杂的软件,所有运算和存储都在远程服务器上完成。用户只需通过终端设备(如个人电脑、平板或甚至手机)访问云桌面,即可获得与物理机器相似的使用体验。简单来说,就是把整个计算环境通过网络交给云服务器来管理,用户只需负...