java 设计模式 动态代理

动态代理的工作是:

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

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

}



















































































  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值