Jdk动态代理简单实现

来源:互联网 发布:音乐巅峰知乎 编辑:程序博客网 时间:2024/06/06 09:20

上一篇中简单介绍了Spring AOP,并且举了一个简单的实例说明,相信大家都有印象,当然那只是冰山一角。Spring的知识很丰富,IOC,AOP,JTA等等,有兴趣的读者推荐一本实用的书籍--《Spring In Action》。ok,废话少说,回到正题。今天这篇博客是我看到了一个微信公众号的文章之后想写的,将动态代理的知识讲的很生动。

那么什么是动态代理?我们要知道,Java 中动态代理的设计来自于代理模式(23中设计模式之一),所谓代理模式就是指为其他对象提供一个代理来控制对某个对象的访问。如果对象A不想或者不能直接访问/操作到对象B,那么这个时候代理模式会自动生成A的一个代理对象,来完成对B的操作。代理对象作为一个媒介来完成这个访问过程。

说了半天,我认为动态代理是这样的:在运行时动态的生成一个类,作为一个对象的代理来完成某些操作的过程的技术是动态代理。

听着有点绕,下面我们举例说明。


先定义一个接口IHello,

package com.sgx.test;public interface IHello {public void sayHello();}

在定义一个实现类HelloWorld,

package com.sgx.test;public class HelloWorld implements IHello{@Overridepublic void sayHello() {// TODO Auto-generated method stubSystem.out.println("Hello world!");}}
当然,作为有逼格的程序员,仅仅写一个HelloWorld不能满足我们,我们现在提出新需求:在说出HelloWorld(运行时,不是编译时,意味着不能重写原Java文件重新编译)前后我们需要打印日志!!细心的读者发现了是不是跟Spring AOP很相似,没错,Spring AOP的功能跟这个差不多,实现也是用了动态代理技术。

那么我们现在怎么做?动态代理,我们运行时会生成一个新类$HelloWorld100,让这个类去做为HelloWorld的代理去完成加日志的功能。这个代理类中也会实现IHello接口。
来达到调用的目的。


下面是一个简单打日志的类:

package com.sgx.test;


public class Logger {
public void startLog(){
System.out.println("Start To Print Log...");
}

public void endLog(){
System.out.println("End To Print Log...");
}
}


然后我们定义LoggerHandler,这个类实现了Java中的InvocationHandler接口,里面是真正处理业务逻辑的地方(这里的业务逻辑就是添加日志)。

package com.sgx.test;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class LoggerHandler implements InvocationHandler{private Object target;Logger logger = new Logger();public LoggerHandler(Object target){this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {// TODO Auto-generated method stublogger.startLog();Object result = method.invoke(target,args);logger.endLog();return result;}}
其中invoke()方法是真正实现的地方;这里的Loggerhandler相当于一个中间层,我们的代理类$HelloWorld100会去调用他,把sayHello的方法调用给他(就是invoke中的method变量),这样sayHello就会在前后添加上日志,而且target参数是个对象,这意味着不仅仅可以传递HelloWorld这样的对象,更加复杂的业务逻辑都可以通过动态代理技术来实现。最终的测试类Test如下:

package com.sgx.test;import java.lang.reflect.Proxy;public class ProxyTest {public static void main(String[] args) {IHello hw = new HelloWorld();LoggerHandler handler = new LoggerHandler(hw);IHello proxy = (IHello) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),hw.getClass().getInterfaces(), handler);proxy.sayHello();}}

测试结果跟预期一致:

Start To Print Log...Hello world!End To Print Log...

以上是对动态代理的简单例子实现,部分内容参考公众号《Java编程》的文章:《Java帝国之动态代理》。

动态代理的缺点是必须要有接口。