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:做日志,插入另一张表");
}
}
在一段程序的外面在包上一段其他程序,这样就会比较方便
这里要注意,是指的是在仅仅是方法外层包上一层其他方法,而不是抱在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:做日志,插入另一张表");
}
}