用动态代理要解决的问题:
当我们想测试别人给出的jar包中的一个方法的运行时间,而我们没有源码,无法修改代码(如果有源码可以在方法前得到系统时间,
在方法后得到系统时间,两个值相减就能实际),怎么可以得到运行时间?
聚合的概念:
在一个类中调用另一个类
①写一个静态代理
(静态代理是针对一个或几个方法时是比较方便的,对于几百个或上千个的话,就不方便了,因为要写几百个或上千个静态代理类)
实现的接口:Moveable.java
package com.bjsxt.service;
public interface Moveable {
void move();
}
目标类:Tank.java
package com.bjsxt.service;
public class Tank implements Moveable{
@Override
public void move() {
System.out.println("坦克正在移动……");
}
}
静态代理类:StaticProxy.java
package com.bjsxt.service;
public class StaticProxy implements Moveable{
Moveable t = new Tank();
@Override
public void move() {
System.out.println("坦克移动开始时间: "+System.currentTimeMillis());//前面加的信息
t.move();
System.out.println("坦克移动结束时间: "+System.currentTimeMillis());//后面加的信息
}
}
测试静态代理:TestStaticProxy.java
package com.bjsxt.service;
public class TestStaticProxy {
public static void main(String []arg){
StaticProxy staticProxy = new StaticProxy();
staticProxy.move();
}
}
②.动态代理
总结概括:大概思路是,把目标类(Tank.java)的方法move()通过(TimeInvocationHandler.java)中的invoke(Object o,Method method)方法,通过反射机制invoke(目标类Tank)类中的方法,在方法前后加入一些需要执行的代码,然后把接口和相应的TimeInvocationHandler传入到能够获得代理类的类Proxy的newProxyInstance(Class infce, InvocationHandler invocationHandler) 方法中,在这个方法中,它把重新封装的方法(在前后加入了代码的方法)写入实现了对应接口的类中,并连接成字符串,然后用java的编译工具把生成类文件编译成.class文件并装入内存,通过.class文件生成对象,返回这个对象(代理对象)
实现的接口:Moveable.java
package com.bjsxt.service;
public interface Moveable {
void move();
}
实现的接口:InvocationHandler
package com.bjsxt.proxy;
import java.lang.reflect.Method;
public interface InvocationHandler {
public void invoke(Object o, Method m);
}
目标类:Tank.java
package com.bjsxt.service;
public class Tank implements Moveable{
@Override
public void move() {
System.out.println("坦克正在移动……");
}
}
具体handler的类:TimeInvocationHandler.java
package com.bjsxt.proxy;
import java.lang.reflect.Method;
public class TimeInvocationHandler implements InvocationHandler{
private Object target;
public TimeInvocationHandler(Object target) {
super();
this.target = target;
}
@Override
public void invoke(Object o, Method method) {
long start = System.currentTimeMillis();
try {
method.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("time:" + (end-start));
}
}
用来产生代理类的静态类:Proxy
package com.bjsxt.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 newProxyInstance(Class infce, InvocationHandler invocationHandler) throws Exception { //JDK6 Complier API, CGLib, ASM
String methodStr = "";
String rt = "\r\n";
Method[] methods = infce.getMethods();
for(Method method : methods) {
methodStr += " @Override" + rt +
" public void " + method.getName() + "() {" + rt +
" try {" + rt +
" Method method = " + infce.getName() + ".class.getMethod(\"" + method.getName() + "\");" + rt +
" invocationHandler.invoke(this, method);" + rt +
" }catch(Exception e) {"+ rt
+ " e.printStackTrace();" + rt
+ " }" + rt +
"}";
}
String src =
"package com.bjsxt.proxy;" + rt +
"import java.lang.reflect.Method;" + rt +
"public class $Proxy1 implements " + infce.getName() + "{" + rt +
" com.bjsxt.proxy.InvocationHandler invocationHandler;" + rt +
" public $Proxy1(InvocationHandler invocationHandler) {" + rt +
" this.invocationHandler = invocationHandler;" + rt +
" }" + rt + methodStr +
"}";
String fileName =
"d:/src/com/bjsxt/proxy/$Proxy1.java";//java的源码
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
//compile
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();//获得java的编译器
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(fileName);
CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
fileMgr.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("com.bjsxt.proxy.$Proxy1");
Constructor ctr = c.getConstructor(InvocationHandler.class);
Object proxyObject = ctr.newInstance(invocationHandler);
return proxyObject;
}
}
测试类:Client.java
package com.bjsxt.proxy;
public class Client {
public static void main(String[] args) throws Exception {
Tank target = new Tank();//被代理对象
InvocationHandler timeInvocationHandler = new TimeInvocationHandler(target);//代理对象
Moveable proxyMoveable = (Moveable)Proxy.newProxyInstance(Moveable.class, timeInvocationHandler);//通过静态方法实现代理,得到代理对象,
proxyMoveable.move();
}
}
//可以对任意的对象、任意的接口方法,实现任意的代理Test
③jdk的动态代理实现
注意:用jdk实现动态代理,目标类要实现接口,如果用hibernate的话,就不用实现接口
实际底层用的就是jdk的动态代理,如果不实现接口的话,用的就是继承
实现的接口:Moveable.java
package com.bjsxt.service;
public interface Moveable {
void move();
}
目标类:Tank.java
package com.bjsxt.service;
public class Tank implements Moveable{
@Override
public void move() {
System.out.println("坦克正在移动……");
}
}
处理
处理类,加入自己的逻辑:TankInvocationHandler.java
package com.zhk.test.Jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TankInvocationHandler implements InvocationHandler{
Object target;
public TankInvocationHandler(Object target) {
super();
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("开始执行移动……");
Object result = method.invoke(target, args);//第一个参数时指调用method方法的对象,第二个参数时指传入调用方法中的参数
System.out.println("移动结束……");
return result;
}
}
测试jdk动态代理:TestJdkProxy.java
package com.zhk.test.Jdkproxy;
import java.lang.reflect.Proxy;
public class TestJdkProxy {
public static void main(String []args){
Moveable tankTarget = new Tank();
TankInvocationHandler tankInvocationHandler = new TankInvocationHandler(tankTarget);
Moveable tankProxyClass = (Moveable)Proxy.newProxyInstance(
tankTarget.getClass().getClassLoader(),
tankTarget.getClass().getInterfaces(),
tankInvocationHandler);//第一个参数时被代理类的classloader,第二个参数时被代理类的接口,
//第三个是handler(也就是加处理逻辑的代码类)
tankProxyClass.move();
}
}