我们有几个类, 类里有几个方法, 想写一个监控类来打印每个方法的执行时间.
public interface Do {
public void doing(long time);
}
public interface Say {
public void saying(long time);
}
public class User implements Say, Do {
@SneakyThrows
public void saying(long ms) {
System.out.println("saying:");
Thread.sleep(ms);
System.out.println("...");
}
@SneakyThrows
public void doing(long ms) {
System.out.println("doing:");
Thread.sleep(ms);
System.out.println("!!!");
}
}
最简单的方法就是在每个方法周围获取时间戳, 然后计算时间差再打印, 为了不把原来的代码搞脏, 我们写一个代理类来做这件事:
静态代理
public final class Monitor implements Do, Say {
private User user;
public Monitor(User user) {
this.user = user;
}
@Override
public void doing(long ms) {
long startAt = System.currentTimeMillis();
user.doing(ms);
long endAt = System.currentTimeMillis();
System.out.println("During " + (endAt - startAt) + " ms");
}
@Override
public void saying(long ms) {
long startAt = System.currentTimeMillis();
user.saying(ms);
long endAt = System.currentTimeMillis();
System.out.println("During " + (endAt - startAt) + " ms");
}
}
调用:
Monitor monitor = new Monitor(new User());
monitor.saying(1000L);
monitor.doing(500L);
动态代理 JVM InvocationHandler
我们看到上面的代理类, 有很多重复的计时步骤, 更糟的是, 我们要为每一个方法都写一个这样的代理方法, 这是不可能的. JVM 为我们提供了动态代理的方案来解决这个问题.
public final class Monitor implements InvocationHandler {
private Object target;
public Monitor(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startAt = System.currentTimeMillis();
Object result = method.invoke(target, args);
long endAt = System.currentTimeMillis();
System.out.println("During " + (endAt - startAt) + " ms");
return result;
}
@SuppressWarnings("unchecked")
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
}
调用:
Object proxy = new Monitor(new User()).getProxy();
((Say) proxy).saying(1000L);
((Do) proxy).doing(500L);
GCLib 动态代理
上的JVM动态代理看起来好多了, 但是有个限制就是依赖接口, 万一我的类没有接口呢? GCLib
可以帮我们:
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.3.0</version>
</dependency>
public final class Monitor implements MethodInterceptor {
private static Monitor monitor = new Monitor();
/**
* 禁用构造方法
*/
private Monitor() {
// Nothing
}
/**
* 单例获取实例
*
* @return
*/
public static Monitor getInstance() {
return monitor;
}
@Override
public Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
long startAt = System.currentTimeMillis();
Object result = methodProxy.invokeSuper(o, params);
long endAt = System.currentTimeMillis();
System.out.println("During " + (endAt - startAt) + " ms");
return result;
}
public <T> T getProxy(Class<T> cls) {
return (T) Enhancer.create(cls, this);
}
}
调用:
User proxy = Monitor.getInstance().getProxy(User.class);
proxy.saying(1000L);
proxy.doing(500L);