您的位置:首页 > 编程语言 > Java开发

java 设计模式 动态代理

2015-07-22 22:29 477 查看
动态代理的工作是:

在一段程序的外面在包上一段其他程序,这样就会比较方便

这里要注意,是指的是在仅仅是方法外层包上一层其他方法,而不是抱在jdk把方法实例化后再包上其他方法

如果实例化后,这里会包上一层jdk 为程序运行加上的虚拟机程序

首先应该想到的是继承:重写类的方法,调用父类方法用super.xxxx(),然后加入新的“包括” 方法

其次应该想到聚合(一个类里头有另外一个类的对象):

申明另一个类实现同样的接口,在类里拥有“被包装类”A(代理了类A的),然后在实现里头调用类A的方法,然后在调用前后加入想“包括”的方法

聚合比继承更灵活

申明所有Proxy类统一实现一个方法接口,里面只有一个申明方法;

在所有Proxy类中写 申明 “接口类型” 的 一个对象;并生成构造方法

其他“包括”方法就包在 调用 “对象里方法”的 外面

写法:

Tank t=new Tank();

TankTimeProxy ttp=new TankTimeProxy(t);

TankLogProxy tlp=new TankLogProxy(ttp);

Movable m = tlp;

m.move()

但是如果接口里有两个以上的方法,或者要求某个类实现的“包括”方法能代理所有的类型的对象

这里默认这些类都实现了某个特定的接口,用接口做代理,而不用特定的类

动态代理:连类名都可以全部隐藏,因为类信息是动态生成的(动态编译 jdk Compiler api ; CGLib ; ASM)

一个例子

申明一个类:把类的代码全部拷贝(包括引用和包名)作为一个字符串加入类中;特殊字符要转义

写法:

//项目的根目录+src 路径下的文件目录(写入文件)

String fileName=System.getProperty("user.dir")

+"/src/com/bjsxt/proxy/TankTimeProxy.java";

File f=new File(fileName);

FileWriter fw=new FileWriter(f);

fw.write(src);

fw.flush();

fw.close();

//动态编译

JavaCompiler compiler =ToolProvider.getSystemJavaCompiler();

System.out.println(compiler.getClass().getName());

StandardJavaFileManager fileMag=compiler.getStandardFileManager(null, null, null);

Iterable<? extends JavaFileObject> units=fileMag.getJavaFileObjects(fileName);

CompilationTask t=compiler.getTask(null, fileMag, null, null, null, units);

t.call();

fileMag.close();

怎么看有没有生成类:show view navigator 会在 目录下生成一个.class 文件

//生成的.class 文件不在bin 目录下,而在src 目录下。这个用之前的loaderclass 是load不了的

//也可以干脆把类生成到bin 目录下。

//载入内存并且创建新实例(注意classloader导入class的时候必须保证在classpath里面,

//在源码路径不在bin目录里,bin里的东西都可以删掉。所以改用urlclassloader,甚至可以从网络上load传过来的class类)

URL[] urls=new URL[]{new URL("file:/"+System.getProperty("user.dir")+"/src")};

URLClassLoader ul =new URLClassLoader(urls);

Class c=ul.loadClass("com.bjsxt.proxy.TankTimeProxy");

System.out.println(c);

Constructor ctr=c.getConstructor(Movable.class);//拿到参数类型为Movable的构造方法

//注意区别,如果用c.newInstance() 调用类里参数为空的构造方法,但是这里没有!!!

Movable m=(Movable)ctr.newInstance(new Tank());//得到的构造方法里把被代理对象 new Tank()参数传进去 ,因为实现了Movable 接口,所以可强制转换

m.move();//这里调用的是TankTimeProxy里头的move 在由这个方法里再去调用Tank里的move方法,甚至连名字TankTimeProxy都不重要,随意取

如果要实现任意接口的代理:(把接口类型作为参数传入):

public static Object newProxyInstance(Class intfac)throws Exception{//jdk6 Compiler api,CGLib,ASM

String methodStr ="";

String rt="\n\t";

//我在想md 是不是可以换成m.getName();

for (Method m : methods) {

methodStr+="@Override"+rt+"public void "+m.getName()+"(){"+rt+

"long start =System.currentTimeMillis();"+rt+

"t."+m.getName()+"();"+rt+

"long end=System.currentTimeMillis();"+rt+

"System.out.println(\"time:\"+(end-start));"+rt+

"}";

}

//动态编译这段代码,然后把生成的类加入内存

String src=

"package com.bjsxt.proxy;"+rt+

"import java.lang.reflect.Method;"+rt+

"public class TankTimeProxy implements "+intfac.getName()+" {"+

"public TankTimeProxy(Movable t) {"+

"super();"+

"this.t = t;"+

"}"+

"Movable t;"+

methodStr+

"}";

//项目的根目录+src 路径下的文件目录(写入文件)

String fileName=System.getProperty("user.dir")

+"/src/com/bjsxt/proxy/TankTimeProxy.java";// 避免loadclass混淆这里路径也要改

File f=new File(fileName);

FileWriter fw=new FileWriter(f);

fw.write(src);

fw.flush();

fw.close();

//动态编译

JavaCompiler compiler =ToolProvider.getSystemJavaCompiler();

System.out.println(compiler.getClass().getName());

StandardJavaFileManager fileMag=compiler.getStandardFileManager(null, null, null);

Iterable<? extends JavaFileObject> units=fileMag.getJavaFileObjects(fileName);

CompilationTask t=compiler.getTask(null, fileMag, null, null, null, units);

t.call();

fileMag.close();

//载入内存并且创建新实例(注意classloader导入class的时候必须保证在classpath里面,

//在源码路径不在bin目录里,bin里的东西都可以删掉。所以改用urlclassloader,甚至可以从网络上load传过来的class类)

URL[] urls=new URL[]{new URL("file:/"+System.getProperty("user.dir")+"/src")};//如果要loadclass避免混淆,这里路径要改,写法上也要注意,路径文件夹加上/

URLClassLoader ul =new URLClassLoader(urls);

Class c=ul.loadClass("com.bjsxt.proxy.TankTimeProxy");

System.out.println(c);

Constructor ctr=c.getConstructor(Movable.class);

Object m=ctr.newInstance(new Tank);

return m;

main 方法里写:

Movable m = (Movable) Proxy.newProxyInstance(Movable.class);

m.move();

更进一步,要求连“包括”方法 的内容,也交由用户去指定:

申明一个handler 接口,里面有一个申明方法:void revoke (Object o,Method m)// 对哪个对象调用的m方法,把对象也传进去

子类handler 实现接口,

private Object target;//这里写上被代理对象

public TimeHandler(Object target) {

super();

this.target = target;

}

@Override

public void invoke(Object o,Method m) {

long start =System.currentTimeMillis();

System.out.println("starttime:"+start);

System.out.println(o.getClass().getName());

try {

m.invoke(target);//系统函数固定写法,函数类型对象调用。相当于调用被代理对象里的m方法;

}

catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

long end =System.currentTimeMillis();

System.out.println("time:"+(end-start));

}

新的newProxyInstance

public static Object newProxyInstance(Class intfac,InvocationHandler h)throws Exception{//jdk6 Compiler api,CGLib,ASM

String methodStr ="";

String rt="\n\t";

//我在想md 是不是可以换成m.getName();

Method[] methods=intfac.getMethods();

for (Method m : methods) {

methodStr+="@Override"+rt+"public void "+m.getName()+"(){"+rt+

"try{"+rt+

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

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

"}catch(Exception e){e.printStackTrace();}"+rt+

"}";

}

//动态编译这段代码,然后把生成的类加入内存

String src=

"package com.bjsxt.proxy;"+rt+

"import java.lang.reflect.Method;"+rt+

"public class TankTimeProxy implements "+intfac.getName()+" {"+

"public TankTimeProxy(InvocationHandler h) {"+

"super();"+

"this.h = h;"+

"}"+

"com.bjsxt.proxy.InvocationHandler h;"+rt+

methodStr+

"}";

//项目的根目录+src 路径下的文件目录(写入文件)

String fileName=System.getProperty("user.dir")

+"/src/com/bjsxt/proxy/TankTimeProxy.java";

File f=new File(fileName);

FileWriter fw=new FileWriter(f);

fw.write(src);

fw.flush();

fw.close();

//动态编译

JavaCompiler compiler =ToolProvider.getSystemJavaCompiler();

System.out.println(compiler.getClass().getName());

StandardJavaFileManager fileMag=compiler.getStandardFileManager(null, null, null);

Iterable<? extends JavaFileObject> units=fileMag.getJavaFileObjects(fileName);

CompilationTask t=compiler.getTask(null, fileMag, null, null, null, units);

t.call();

fileMag.close();

//载入内存并且创建新实例(注意classloader导入class的时候必须保证在classpath里面,

//在源码路径不在bin目录里,bin里的东西都可以删掉。所以改用urlclassloader,甚至可以从网络上load传过来的class类)

URL[] urls=new URL[]{new URL("file:/"+System.getProperty("user.dir")+"/src")};

URLClassLoader ul =new URLClassLoader(urls);

Class c=ul.loadClass("com.bjsxt.proxy.TankTimeProxy");

System.out.println(c);

Constructor ctr=c.getConstructor(InvocationHandler.class); //得到参数为movable的构造方法

Object m=(Object)ctr.newInstance(h);

return m;

}

main 方法里:

//Movable m = (Movable) Proxy.newProxyInstance(Movable.class,new TimeHandler());

//m.move(); //调用invokationHandler 然后在调用子类的实现方法

换成终板:

Tank t=new Tank();

InvocationHandler h=new TimeHandler(t);//因为构造方法赋值了

Movable m = (Movable) Proxy.newProxyInstance(Movable.class,h);

m.move();

再来一个项目,重用上面的invokationhandler 接口 和Proxy 类

client:

package com.bjsxt.proxy.test;

import com.bjsxt.proxy.InvocationHandler;

import com.bjsxt.proxy.Proxy;

public class Client {

public static void main(String[] args) throws Exception {

UserMgr mgr=new UserMgrImpl();

InvocationHandler h=new TransactionHandler(mgr);

//好奇怪哦,这个动态代理一定要提前先生成TankTimeProxy.java文件才可以运行

UserMgr u=(UserMgr) Proxy.newProxyInstance(UserMgr.class, h);

u.addUser();

}

}

TransactionHandler

package com.bjsxt.proxy.test;

import java.lang.reflect.Method;

import com.bjsxt.proxy.InvocationHandler;

public class TransactionHandler implements InvocationHandler{

private Object target;

public TransactionHandler(Object target) {

super();

this.target = target;

}

@Override

public void invoke(Object o, Method m) {

System.out.println("Transaction Start");

try {

m.invoke(target);

}catch(Exception e){

e.printStackTrace();

}

System.out.println("Transaction submit");

}

}

UserMgr 接口

package com.bjsxt.proxy.test;

public interface UserMgr {

void addUser();

}

UserImpl 实现接口

package com.bjsxt.proxy.test;

public class UserMgrImpl implements UserMgr {

@Override

public void addUser() {

System.out.println("1:插入记录到user表");

System.out.println("2:做日志,插入另一张表");

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: