目的:
1.动态代理本身机制的理解
2.动态代理jvm运行的原理。
了解动态代理之前首先要了解代理设计模式。代理模式:当其他对象提供一个代理,用来控制对一个真实对象的访问,代理类用来做一些对委托类消息的预处理,过滤,或者日志处理,这是消息的后续处理等等。主要是为了增加程序的一个灵活性。
代理模式就是在访问实际对象时,会创建一个代理对象去访问,而不是直接访问实际对象。 在访问实际对象时引入一定程度的间接性,这个间接性可以扩展很多的用途,比如日志,验证等等,还能协调调用者和被调用者的关系,降低系统的耦合度。第二个是代理对象可以作为调用者和实际对象之间的一个中间件,对实际对象起到保护作用。
例子:
现在以一个简单的支付接口为例。
public interface Payment {
/**
* @param uid
* @return
*/
String doPay(String uid);
}
这是一个支付接口。
public class ThirdChannelPayment implements Payment{
@Override
public String doPay(String uid) {
System.out.println("这是第三方支付! uid :" + uid);
return "success";
}
}
这是上面支付接口的实现。
public class ProxyDemo {
public static void main(String[] args) {
Payment payment = new ThirdChannelPayment();
System.out.println(payment.doPay("GZC"));
}
}
然后对上面的方法进行简单的调用。
输出:
这是第三方支付! uid :GZC
success
现在要在记录支付的日志,我们再创建一个PaymentLogger来做这个事情。然后设计DynamicProxy动态代理类来执行支付之所以要这样设计,是因为如果我们的支付方式有很多的话,要是在支付每个方法都加打印日志的话很繁琐,也不符合设计原则。
public class PaymentLogger {
public void Log(String uid){
System.out.println(uid + "发起了支付行为,记录日志!");
}
}
public class DynamicProxy implements InvocationHandler {
/**
* 被代理的对象
*/
private Object target;
public Object bind(Object target){
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行前记录日志-----");
Object result = method.invoke(target,args);
System.out.println("执行后记录日志=====");
return result;
}
}
上面是DynamicProxy代理类。然后将mian方法修改如下:
public static void main(String[] args) {
Payment payment = new ThirdChannelPayment();
DynamicProxy proxy = new DynamicProxy();
Payment proxyPayment = (Payment)proxy.bind(payment);
proxyPayment.doPay("GZC");
}
执行结果:
执行前记录日志-----
这是第三方支付! uid :GZC
执行后记录日志=====
可以看到,在支付方法执行前后我们都可以做自己想做的扩展功能。
小结:虽然我们写的代码量增加了,但是优点是可维护性,灵活性增强了,我们想要代理哪个类,只需要将被代理类传入bind方法就可以了。java jdk的动态代理实现方式有个缺点,代理对象继承了Proxy,所以无法再继承其他类,所以只能通过实现接口的方式完成动态代理,即java jdk提供的代理模式只能代理接口。如果我们要代理一些非接口的实现,就可以使用一些其他的方式,例如Cglib。
下面我们将DynamicProxy使用Cglib来实现:
public class CglibProxy implements MethodInterceptor {
private Object target;
public Object getInstance(Object target){
this.target = target;
Enhancer enhancer = new Enhancer();//加强器
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
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;
}
}
这是cglib代理类。下面将main方法改写:
public static void main(String[] args) {
ThirdChannelPayment payment = new ThirdChannelPayment();
CglibProxy cglibProxy = new CglibProxy();
ThirdChannelPayment thirdChannelPaymentProxy = (ThirdChannelPayment) cglibProxy.getInstance(payment);
thirdChannelPaymentProxy.doPay("GZC");
}
输出结果:
执行前记录日志-----
这是第三方支付! uid :GZC
执行后记录日志=====
可以看到输出结果与之前相同。需要注意到的是我们使用cglib时,创建代理类传入的是ThirdChannelPayment 这个实体类,而不是之前的Payment 接口。使用cglib,就可以满足我们想要动态代理实体类的需求了。推荐两个其他动态代理工具:javassit,asm。