需求:写一段代码,监控汽车运行了多少时间,该怎么做?
原始代码:
long startTime = System.currentTimeMillis();
/**
* 这边是运行代码,为了方便,就直接获取线程,sleep2秒
*/
Thread.sleep(2000);
long endTime = System.currentTimeMillis();
System.out.print("运行了:"+(endTime-startTime)+"毫秒");
现在,我们用代理模式来实现这个情况
方法一:继承
先定义一个行驶的接口
public interface MoveAble {
void move() throws InterruptedException;
}
然后是被代理类,也就是父类
public class Car implements MoveAble {
public void move() throws InterruptedException {
Thread.sleep(2000);
}
}
然后是代理类,也就是子类
public class Car2 extends Car{
public void move() throws InterruptedException {
long startTime = System.currentTimeMillis();
super.move();
long endTime = System.currentTimeMillis();
System.out.print("运行了:"+(endTime-startTime)+"毫秒");
}
}
方法二:聚合
接口MoveAble和Car类不变,先添加代理类:
public class Car3 implements MoveAble {
private Car car;
//通过构造方法将car传进来
public Car3(Car car){
super();
this.car=car;
}
public void move() throws InterruptedException {
long startTime = System.currentTimeMillis();
car.move();
long endTime = System.currentTimeMillis();
System.out.print("运行了:"+(endTime-startTime)+"毫秒");
}
}
现在思考一个问题,如果我们现在需要对Car的move方法加一个log处理怎么办?
继承不好搞,那聚合呢?
好的,我们来实现;
接口MoveAble和Car类不变,先添加代理类:
代理类一:时间
public class TimeProxy implements MoveAble {
private MoveAble m;
public TimeProxy(MoveAble moveAble){
super();
this.m=moveAble;
}
public void move() throws InterruptedException {
long startTime = System.currentTimeMillis();
System.out.println("开始计时");
m.move();
long endTime = System.currentTimeMillis();
System.out.println("运行了:"+(endTime-startTime)+"毫秒");
}
}
代理类二:log
public class LogProxy implements MoveAble {
private MoveAble m;
public LogProxy(MoveAble moveAble){
super();
this.m=moveAble;
}
public void move() throws InterruptedException {
System.out.println("开始log");
m.move();
System.out.println("结束log");
}
}
编写测试类:
用这种办法,我们可以预见的是,我们是先计时,还是先开始log记录呢?其实是都可以的;
先log后计时
public static void main(String[] args) throws InterruptedException {
MoveAble m = new Car();
TimeProxy timeProxy = new TimeProxy(m);
LogProxy logProxy = new LogProxy(timeProxy);
logProxy.move();
}
打印:
开始log
开始计时
我睡了2秒钟
运行了:2001毫秒
结束log
先计时后log
public static void main(String[] args) throws InterruptedException {
MoveAble m = new Car();
LogProxy logProxy = new LogProxy(m);
TimeProxy timeProxy = new TimeProxy(logProxy);
timeProxy.move();
}
打印:
开始计时
开始log
我睡了2秒钟
结束log
运行了:2001毫秒
其实把顺序换一下就可以了~
现在考虑一下,如果我们再要搞个监控自行车的,火车的,飞机的,那是不是还要写对应的代理类?1000个类就得写2000个代理类,爆炸了!那我们怎么统一一下呢?比较代码都一样!
下面来说两张办法:
一:JDK动态代理
二:CGLIB动态代理
实现
JDK动态代理
MoveAble.java和Car.java不变
新建代理类:
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target){
super();
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
System.out.println("开始计时");
Object result = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.print("运行了:"+(endTime-startTime)+"毫秒");
return result;
}
/**
* 获取目标对象的代理对象
* @return 代理对象
*/
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),target.getClass().getInterfaces(), this);
}
}
测试类:
public static void main(String[] args) throws InterruptedException {
MoveAble m = new Car();
MyInvocationHandler mi = new MyInvocationHandler(m);
MoveAble proxy = (MoveAble) mi.getProxy();
proxy.move();
}
这样可以实现对任何类的代理而不需要写额外的代理类。
然后,我要如最开始的时候一样同时集成时间代理和log代理,怎么办?
时间代理类:
public class MyTimeInvocationHandler implements InvocationHandler {
private Object target;
public MyTimeInvocationHandler(Object target){
super();
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
System.out.println("开始计时");
Object result = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println("运行了:"+(endTime-startTime)+"毫秒");
return result;
}
/**
* 获取目标对象的代理对象
* @return 代理对象
*/
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
target.getClass().getInterfaces(), this);
}
}
log代理类:
public class MyLogInvocationHandler implements InvocationHandler {
private Object target;
public MyLogInvocationHandler(Object target){
super();
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始log");
Object result = method.invoke(target, args);
System.out.println("结束log");
return result;
}
/**
* 获取目标对象的代理对象
* @return 代理对象
*/
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
target.getClass().getInterfaces(), this);
}
测试类(这是先log后time,明显log在外层,如果要换,调换一下位置就好了)
public static void main(String[] args) throws InterruptedException {
MoveAble m = new Car();
MyTimeInvocationHandler mt = new MyTimeInvocationHandler(m);
MoveAble proxy = (MoveAble) mt.getProxy();
MyLogInvocationHandler ml = new MyLogInvocationHandler(proxy);
proxy = (MoveAble) ml.getProxy();
proxy.move();
}
至于JDK动态代理,下次再研究~
CGLIB动态代理
这边就用不到接口方面的东西啦!
新建汽车类:
public class Car1 {
public void move(){
System.out.println("我在move!");
}
}
然后代理类:
public class MyMethodInterceptor implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置代理");
//通过代理类调用父类中的方法
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("后置代理");
return result;
}
public Object getProxy(Class clazz){
//设置需要创建子类的类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通过字节码技术动态创建子类实例
return enhancer.create();
}
}
测试类
public class testMain {
public static void main(String[] args) {
MyMethodInterceptor mm = new MyMethodInterceptor();
Car1 c = (Car1) mm.getProxy(Car1.class);
c.move();
}
}
compare:
JDK动态代理:
1,只能代理实现了接口的类
2,没有实现接口的类不能实现JDK动态代理
CGLIB动态代理:
1,针对类来实现代理
2,对指定目标类产生一个子类,通过方法拦截技术拦截所有父类方法的调用
这两种详细的情况,明天学习~