如何简单实现JDK的动态代理功能

Spring框架有一个很重要的机制——代理机制,而这个机制是实现Spring一个重要编程思想——切面编程思想重要工具。

那么代理功能有什么用呢?假设我们有一个方法,我们想查看这个方法的执行性能与开销时间,我们是怎么做的呢?

Screenshot from 2017-12-29 13-51-38

可能大部分的人都会这样直接在方法内部加上这两句代码:

1
2
long start = System.currentTimeMillis();
System.out.println("该方法消耗时间" + (System.currentTimeMillis() - start));

可能感觉这样子,才两行代码,没有什么大不了的,但是如果方法一多,需要对大多数的业务逻辑代码进行开销记录呢?那个就会增加无数的重复代码,而这些代码跟我们的业务逻辑是根本没有关系的,我们只是希望在这个方法执行的过程中能够记录其开销而已,并不想去破坏这个方法原有的代码结构。那么这个要怎么实现呢?这个其实就是依靠Java自带的代理机制来解决,其实Java的JDK就有类的动态代理功能,而这本质又依赖于Java中的反射机制来实现。

那么什么是代理机制呢?所谓代理,就是我把一切都弄好了,但是呢,我本身不去做这件事情,我把这件事情的实施交给别人,这就是代理机制;只不过现在是换成我们主动想要一个代理人去实施这个事情,而不是方法本身去实施;当目标方法准备执行时被我们的代理类拦截了下来,然后,由代理类自带的方法去间接执行目标方法,在这过程当中,就可以增加任何你想要做的事情:加入开销时间、记录每一时刻执行的方法的详细信息(方法名、参数名)等等;这样,你会发现,只需要一个代理类,就可以实现我们刚刚想对所有方法进行开销时间的记录操作,并且还能不断扩充我们想要实现的增强功能(附录:这个其实已经有一点点Spring的切面编程的意味了)

代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyInvokationHandler<E> implements InvocationHandler {

private E target;

public MyInvokationHandler(E target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Long beginTime = System.currentTimeMillis();
Object returnValue = method.invoke(target, args);
System.out.println("方法" + method.getName() + "调用结束,耗时"+ (System.currentTimeMillis() -beginTime));
return returnValue;
}
}

被代理的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface proxyInterface extends ControlDatabase {

@Override
<T>ResultBean<T> query(Object o, String sql) throws SQLException;

@Override
<T>ResultBean<T> insert(PreparedStatement preparedStatement) throws SQLException;

@Override
<T>ResultBean<T> delete(PreparedStatement preparedStatement);

@Override
<T>ResultBean<T> update(PreparedStatement preparedStatement);
}

实现代理调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class MyProxy{

private ControlDatabase controlDatabase;
private InvocationHandler handler;
private proxyInterface proxy;

public MyProxy(ControlDatabase controlDatabase) {
this.controlDatabase = controlDatabase;
handler = new MyInvokationHandler<>(controlDatabase);
proxy = (proxyInterface) Proxy.newProxyInstance(proxyInterface.class.getClassLoader(),
new Class[]{proxyInterface.class}, handler);
}

public proxyInterface getProxy() {
return proxy;
}
}

public class messageImpl implements messageMap {
@Override
public <T> ResultBean<T> queryCompyInfo() throws SQLException {
proxyInterface proxy = new AdaperFactory(new MysqlQuery()).getProxy();
Connection connection = proxy.getConnection();
String sql = "SELECT * FROM Lanmu WHERE Lname='公司介绍'";
Statement statement = connection.createStatement();
return new ResultBean(proxy.query(statement, sql).getData());
}

从实现代理调用的 MyProxy 这个类中我们可以看出,只要是继承了proxyInterface这个接口的类或者接口,都会被我们的代理类所拦截下来,进入代理的流程。让我们通过一个视频来了解下吧:

YouTube站点

[youtube https://www.youtube.com/watch?v=WVU7S8nD9NQ&w=560&h=315]

哔哩哔哩站点

https://www.bilibili.com/video/av18146765/