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

设计模式之动态代理模式

2014-10-19 18:48 351 查看
java的动态代理代理模式:为其他对象提供一种代理,并以控制对这个对象的访问。其特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等.
代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
代理分为两种:静态代理和动态代理。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在。
动态代理:在程序运行时,利用java的反射机制动态创建而成。
动态代理原理     
JDK动态代理中包含一个类和一个接口:
InvocationHandler接口:     

public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
/*     参数说明:
Object proxy:指被代理的对象 。
Method method:要调用的方法
Object[] args:方法调用时所需要的参数
*/
}
可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。

Proxy类:     

Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)   throws IllegalArgumentException;
/*  ClassLoader loader:类加载器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子类实例
*/


利用JDK的动态代理写一个小Demo.

先一个Hello.java接口
public interface Hello{
void sayHello(String to);
void print(String p);
}


再写这个接口的实现HelloImpl.java




public class HelloImpl implements Hello {
@Override
public void sayHello(String to) {
System.out.println("Say Hello to  " + to);
}

@Override
public void print(String p) {
System.out.println("Print: " + p);
}
}
接下来写JDK动态代理代理类LogHandler.java

public class LogHandler implements InvocationHandler {

private Object target;

public LogHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
doBefore();
Object result = method.invoke(target,args);
doAfter();
return result;
}

public void doBefore(){
System.out.println("before....");
}

public void doAfter(){
System.out.println("after....");
}
}


最后写一个测试类LogTest.java

public class LogTest {

public static void main(String[] args){

HelloImpl helloImpl = new HelloImpl();
LogHandler logHandler = new LogHandler(helloImpl);

Hello hello = (Hello) Proxy.newProxyInstance(helloImpl.getClass().getClassLoader(), helloImpl.getClass().getInterfaces(), logHandler);
hello.sayHello("Peter");
hello.print("z");
}
}


输出的结果为:

before....
Say Hello tozhangbojun
after....
before....
Print: z
after....
这里我们使用的是JDK中的动态代理来实现。

其实我们可以自己来模拟一个动态代理类来实现相应的功能。

问题描述:为Tank类的move()函数做一个时间日志管理。

1.先写一个Moveable接口

  

public interface Moveable {

void move();
}


2.再写Tank.java类

public class Tank implements Moveable{

@Override
public void move() {
System.out.println("Tank is moving");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
}
}
}


3.模拟JDK中的InvocationHandler接口

public interface InvocationHandler {

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


4.写相应的时间日志操作 如TimeHandler

public class TimeHandler implements InvocationHandler {

private Object target;
public TimeHandler(Object target) {
this.target = target;
}
@Override
public void invoke(Object o, Method method) {
long start = System.currentTimeMillis();
System.out.println("starttime: " + start);
System.out.println(o.getClass().getName());
try {
method.invoke(target);
} catch (IllegalAccessException e) {
e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
} catch (InvocationTargetException e) {
e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
}
long end = System.currentTimeMillis();
System.out.println("time:" + (end - start));
}
}


5.最后是代理类Proxy.java
public class Proxy {

public static Object newProxyInstance(Class infce,InvocationHandler h) throws Exception{  //JDK6,   Complier API,  CGLIB  ,ASM
String methodStr = "";
String rt = "\r\n";
Method[] methods = infce.getMethods();

for(Method m:methods){
methodStr += "@Override" + rt +
"public void " + m.getName() + "() {" + rt +
"    try {" + rt +
"    Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
"    h.invoke(this, md);" + rt +
"    }catch(Exception e) {e.printStackTrace();}" + rt +

"}";
}

String src =
"package org.whut.proxy.proxy;" +  rt +
"import java.lang.reflect.Method;" + rt +
"public class $Proxy1 implements " + infce.getName() + "{" + rt +
"    public $Proxy1(InvocationHandler h) {" + rt +
"        this.h = h;" + rt +
"    }" + rt +

"    org.whut.proxy.proxy.InvocationHandler h;" + rt +
methodStr +
"}";
String fileName = "d:/src/org/whut/proxy/proxy/$Proxy1.java";//这里要写在d盘中写见src/org/whut/proxy/proxy文件。要将以上的字符串src写到相应的文件中
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();

//编译
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null,null,null);
Iterable units = fileMgr.getJavaFileObjects(fileName);
JavaCompiler.CompilationTask task = compiler.getTask(null,fileMgr,null,null,null,units);
task.call();
fileMgr.close();

//加载到内存和生成新对象
URL[] urls = new URL[]{new URL("file:/" + "d:/src/")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("org.whut.proxy.proxy.$Proxy1");
System.out.println(c);

Constructor ctr = c.getConstructor(InvocationHandler.class);
Object m = ctr.newInstance(h);

return m;

}
}
6.测试其正确性

public class Client {

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

Tank t = new Tank();
InvocationHandler h = new TimeHandler(t);

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


运行结果为:
class org.whut.proxy.proxy.$Proxy1
starttime: 1413718875079
org.whut.proxy.proxy.$Proxy1
Tank is moving
time:835


静态代理与动态代理的比较

     与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。

JDK动态代理也有缺陷

   JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理。

Cglib动态代理 

JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

cglib实例   

    先导入两个包cglib.jar和asm.jar

    Cglib中的被代理类必须实现方法

    先写一个HelloImpl.java类

public class HelloImpl2 {

public void sayHello(String to) {
System.out.println("Say Hello to  " + to);
}

public void print(String p) {
System.out.println("Print: " + p);
}
}


接着LogCglib.java类

public class LogCglib implements MethodInterceptor {

public Object obj;
/*
创建代理对象
*/

public Object getInstance(Object target){
System.out.println("========");
this.obj = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.obj.getClass());
//回调方法
enhancer.setCallback(this);
enhancer.setClassLoader(target.getClass().getClassLoader());
//创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

doBefore();
methodProxy.invokeSuper(o,objects);
doAfter();
return null;

}

public void doBefore(){
System.out.println("before....");
}

public void doAfter(){
System.out.println("after....");
}
}


测试LogTest2.java

public class LogTest2 {
public static void main(String[] args){

LogCglib logCglib = new LogCglib();
HelloImpl2 helloImpl = (HelloImpl2) logCglib.getInstance(new HelloImpl2());
helloImpl.sayHello("peter");
helloImpl.print("z");
}

}
运行结果

========
before....
Say Hello to  peter
after....
before....
Print: z
after....
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息