本文可作为 北京尚学堂设计模式的 学习笔记
在上一节文章里 我们能让Proxy类代理Imoveable接口 但是也只有这一个接口 别的还不行
那第一步就是分析接口逻辑 在上一篇文章里 我们要在坦克移动前后记录时间 把硬代码写到了字符串里 这个就不可以改变了
(大家肯定还很疑惑 这个InvocationHandler到底是干什么的 先别急 往下看)
那第二步就是修改Proxy里面的那个字符串 改接口
newInstance 里面的参数Class 就是接口 在上面的例子中就是Imoveable
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
+" }";
}
这下大家应该明白了吧
在client测试时
Say s=(Say)Proxy.newInstance(Say.class, in);
这个s实际上就是Proxy1的一个实例对象
而相应的InvocationHandler在Proxy.newinstance的最后几行代码里已经注入了
至于InvocationHandler的invoke方法在文章上面已经给出
这下大家应该明白InvocationHandler的作用了吧
写到这里 我们其实就已经模拟了jdk动态代理的机理 当然我们这只是最原始的模拟 很多细节方面还没有考虑到
但最核心的原理 就是上面所说的那个
现在 我们用一次真实的jdk动态代理
很疑惑public Object invoke(Object proxy, Method method, Object[] args)
这个proxy参数似乎没有用 我们的被代理对象在构造函数里已经传进来了
那它有什么用 我自己也没搞清楚 哪位朋友清楚的 请告诉我一声
这是多重代理
测试结果
starttime:1406365283656
start log:
i can move...
end log:
end time:1406365284343
expend time:687
其实这个多重代理 之前我是这样写的
在TimeInvHan的invoke方法里面
method.invoke(obj, new Object[]{});
这句代码里报这个错误
java.lang.IllegalArgumentException: object is not an instance of declaring class
改成如上那个才ok了
大家对这个错误怎么看?
在上一节文章里 我们能让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了
大家对这个错误怎么看?