学习动态代理stepbystep(4)

通过上三篇文章,我想大家对“代理”这个概念有点明白了,但在(3)的代码中,还是有一个很大的问题,它并没有找到一个动态的实质。

我的意思是说,在Proxy.java中,对于计时的代码,并不没有真正的拿出来。我们希望的,是把真正代理的代码拿出来,通过我们自己改变需要代理的业务逻辑,再让Proxy.java帮我们生成一个真正的代理类。

好了,下面,我们就完成这个最最核心的问题:这个视频上讲的有点乱,我查了些资料,才会把它弄得差不多了。如有不对的地方,希望朋友们指正。


为了能更清晰一些,我把全部代码贴上来,再注上与(3)中的区别:

注:写好后,发现程序不能正常运行,但刷新之后,再运行就正常了。(可能是生成的代理类没有及时能加载所致)

1.Moveable.java

package com.compiler.test.two;
public interface Moveable {
void move();
}


2.tank.java 它是被代理类

package com.compiler.test.two;
import java.util.Random;
public class Tank implements Moveable {
@Override
public void move() {
System.out.println("Tank Moving....");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

3.模拟的一个jdk中的类:InvocationHandler.java

/*方法调用的处理器*/
package com.compiler.test.two;
import java.lang.reflect.Method;
/*给一个方法,就可以去它进行处理。由它的子类来指定*/
public interface InvocationHandler {
对于传入的参数(方法m)进行处理
///注意:这个invoke是自己写的,不是jdk本身的

public void invoke(Object o,Method m);
}


4.然后,我们写自己希望拥有的代理逻辑:加上时间代理。

/*我们要实现定义自己的处理方式 ,做做三步:
第一,定义好被代理的对象。target,它应该是任意类型的。
第二,定义好要要实现的接口类:InvocationHandler
第三:定义好自己的逻辑。在逻辑中调用被代理对象的方法*/

TimeHandler.java     /*还可以有LogHandler等等*/

package com.compiler.test.two;
import java.lang.reflect.Method;

public class TimeHandler implements InvocationHandler {

//在代理逻辑中,要把被代理的对象写成自己的成员变量,由于要这实现对任何对象代理,故用object
private Object targer;
public TimeHandler(Object targer) {
super();
this.targer = targer;
}

///下面重写接口中定义的Invoke方法
@Override
///通过     m.invoke(targer);  拿到方法被代理类中的方法,然后,按需要在其上下加上内容

///Object  o 是生成的代理类,现在没用,但在别的代理逻辑上可能会用到(o.getName():TimeHandler,在jdk中叫$Proxy1)

public void invoke(Object o,Method m) {
long start = System.currentTimeMillis();
System.out.println("starttime:" + start);
对于方法的调用,一定要知道这个方法的对象是谁
///没有对象,就没法调用除了静态方法以外的方法
try {
这个invoke是jdk的
m.invoke(targer);///调用的被代理对象的方法
} catch (Exception e) {
e.printStackTrace();

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


5.

如果看看上面4个文件,也许我们就知道我们还需要一个什么文件了:

我们就需要一个产生真实的代理类的方法,这个方法应该是有参数的(要不就不叫动态生成了),参数嘛。。。

当然是两个了,一个是被代理的类,别一个是对这个被代理类我们想要完成的操作:

于是乎:最终的proxy.java诞生了。。

package com.compiler.test.two;
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 {

//(3)中的代码 public static Object newProxyInstance(Class infce) throws Exception{
//通过对第一个参数(被代理类的接口)的实例化,我们就通过反射知道里面的方法了。

 ///通过对第二个参数(代理逻辑的接口)的实例化,我们就知道要对被委托类,采用何种操作了。

public static Object newProxyInstance(Class infce,InvocationHandler h) throws Exception{

String rt = "\r\n";
String methodStr = "";
Method[] methods = infce.getMethods();

for(Method m:methods){
methodStr  = methodStr + "@Override" + rt + 
"\tpublic void " + m.getName() + "(){" + rt +   //得到方法的对象
//通过反射拿到方法:生成的代码是Method md = com.bjsxt.proxy.Moveable.class.getMethod("move");要加上try,catch
"\t\ttry {" + rt +
"\t\tMethod md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +  
//invoke()两个参数,第一个是object,第二个是参数一个Method对象
//this:被实例化的代理对象TimeHandler 
"\t\th.invoke(this,md);}" + rt +
"\tcatch(Exception e){e.printStackTrace();" + rt +
"\t\t}" + rt +
"\t}";
}
String str = 
"package com.compiler.test.two;" + rt +
"import java.lang.reflect.Method;" + rt+
"public class TankTimeProxy2 implements " +infce.getName() + "{" + rt + 

"\tpublic TankTimeProxy2(InvocationHandler h ){" + rt + 
"\tsuper();" + rt +
重点:对于这的传参我找了一个晚上才找到是怎么实例化的。在最后的构造方法中将其传入,哎。。郁闷啊
"\tthis.h = h;" + rt +
"\t}" +  rt +
"\t com.compiler.test.two.InvocationHandler h;" + rt +
methodStr + rt +
"}";
String fileName = System.getProperty("user.dir") + "/src/com/compiler/test/two/TankTimeProxy2.java";
File f  = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(str);
fw.flush();
fw.close();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable units = fileManager.getJavaFileObjects(fileName);
CompilationTask t = compiler.getTask(null, fileManager, null, null, null, units);
t.call();
fileManager.close();
URL[] uris = new URL[] {new URL("file:/" + System.getProperty("user.dir") + "/src")};
URLClassLoader  ul = new URLClassLoader(uris);
Class c = ul.loadClass("com.compiler.test.two.TankTimeProxy2");  //此处的名字应该与上面遥相呼应
///自己理解:调用哪个构造方法生成对象
//(3)中的代码 Constructor ctr = c.getConstructor(Moveable.class);
Constructor ctr = c.getConstructor(InvocationHandler.class);
//(3中的代码) Object o = (Object)ctr.newInstance(new tank());   //传入参数,正式生成对象
///
Object o = (Object)ctr.newInstance(h);   ///h在这里传入,传入参数,正式生成对象

/*如果调用o.move(),它就会调用TankTimeProxy2中的
* invoke(this.method),而method就是h:具体实现了invocationHandler的子类的method
*proxy.java产生代理的类,以后不用再变了
*/
return o;
}
}

好啦,大功造成了。。

测试一下:

Client.java

package com.compiler.test.two;
public class Client {
public static void main(String[] args) throws Exception {

Tank t = new Tank();  ///委托类(被代理类)
InvocationHandler h = new TimeHandler(t);//这里代理的处理逻辑对象,而不是代理的对象。对这个对象进行处理

///现在我们有了处理逻辑的对象,但处理逻辑的对象不知道被代理的类中都有什么方法
而在newProxyInstance中,将被代理对象的类中的方法拿到,然后生成真正的代理类
在生成的代理类中,它的处理对象已经被实例化为t(TimeHandler).


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

m.move();

///在调用m.move的时候,它会到(TankTimeProxy2)中调用move方法,由于h是TimeHandler对象,
///所以就会用TimeHandler的invoke方法,而在t中,它的成员变量是t,因此,它最终会调用t的move方法
}
}

输出结果:

starttime:1319001666015
Tank Moving....
time: 9531


另外:我们也可以看到它自动生成的代理类:

package com.compiler.test.two;
import java.lang.reflect.Method;
public class TankTimeProxy2 implements com.compiler.test.two.Moveable{
public TankTimeProxy2(InvocationHandler h ){
super();
this.h = h;
}
com.compiler.test.two.InvocationHandler h;
@Override
public void move(){
try {
Method md = com.compiler.test.two.Moveable.class.getMethod("move");
h.invoke(this,md);}
catch(Exception e){e.printStackTrace();
}
}
}

我们对于java中对动态代理的模拟,终于是可以画上一个句号了。。。。

我们还可以来个别的测试:来体验一下proxy的凶猛之处:

UserManager.java

package com.compiler.test.two.test;
public interface UserManager {
void addUser();
}

UserManagerImpl.java

package com.compiler.test.two.test;
public class UserManagerImpl implements UserManager{
//现在对它进行控制,让他们成为一个事务
@Override
public void addUser() {
System.out.println("1:插入记录到User表");
System.out.println("2:做日志在另外一张表");
}
}

package com.compiler.test.two.test;
import java.lang.reflect.Method;
import com.compiler.test.two.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 Commit");
}
}

对于proxy.java,我就引用上面的那个了。。。

TransactionHandler .java

package com.compiler.test.two.test;
import java.lang.reflect.Method;
import com.compiler.test.two.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 Commit");
}
}

测试:

Client.java

package com.compiler.test.two.test;
import com.compiler.test.two.Proxy;
import com.compiler.test.two.InvocationHandler;
public class Client {
public static void main(String[] args) throws Exception {
UserManager um = new UserManagerImpl();
InvocationHandler h = new TransactionHandler(um);
UserManager u = (UserManager) Proxy.newProxyInstance(UserManager.class,h);
u.addUser();
}
}

输出结果:

Transaction Start
1:插入记录到User表
2:做日志在另外一张表
Transaction Commit


上面,就是我这两天学习的内容了,关于java动态代理的小模拟。。。。我还会再写一篇,看试试怎么用jdk提供的动态代理类。就是演示一下它的应用了。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值