在java编程中,代理模式是我们常见的东西,说道代理,从字面意义理解,就是代为执行,一个类代替另一个类,那么代理有什么好处呢?
代理的好处
1. 代理可以屏蔽复杂的业务逻辑
2. 代理可以对代理对象运行前后做前置后置处理,而不改变代理对象的行为
3. 代理模式具有安全性。诸如可以为client端提供代理打桩,从而安全的调用到server端上。
静态代理
顾名思义,就是对指定的类或者对象进行代理操作,诸如:
public class Customer {
private String firstName;
private String lastName;
public Customer(final String firstName, final String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public void printFullName( ) {
System.out.println(this.firstName + " " +this.lastName);
}
}
public class CustomerProxy {
private final Customer customer;
private int invocationCount = 0;
public CustomerProxy(final Customer customer) {
this.customer = customer;
}
public void printFullName( ) {
invocationCount++;
this.customer.printFullName( );
}
public int getInvocationCount() {
return invocationCount;
}
}
如上代码中,CustomerProxy对Customer类进行了代理,由于是指定的,所以这里就是静态代理了。从这里可以看出,代理对象内部耦合了原对象并进行了调用,对原对象无任何改动和污染。
不过这里的代理是通过直接耦合的方式来进行的,如果直接使用到业务中,会使得代码整体的可维护性较差,如果有多个代理对象需要代理的时候,则需要破开原来的代码来进行,按照OO设计五大原则中的对扩展开放,对修改关闭的话,这里我们利用简单工厂模式进行优化一下:
public class CustomerClassFactory {
public final static CustomerProxy getProxy( ) {
Customer customer = new Customer("Shohra", "Afaque");
return new CustomerProxy(customer);
}
}
public class Demo {
public static final void main(final String[] args) {
CustomerProxy proxy = CustomerClassFactory.getProxy( );
proxy.printFullName( );
System.out.println(proxy.getInvocationCount());
}
}
从上面代码看出,我们定义了一个代码工厂,此工厂可以为原对象创建各种各样的代理,如果用户需要注册多个代理,只需要在代理工厂中进行适配即可,大大的解藕了代理类和业务代码纠缠的问题。
从如上代码中,可以看到,静态代理的一些问题,总体说来如下:
1. 静态代理需要指定代理对象,不灵活。
2. 静态代理很多对象的时候,将会使代理类爆炸增长,不利于维护。
由此,我们来看看动态代理。
动态代理
动态代理和静态代理不同的地方是,动态代理在JDK执行的时候,才会生成,而不是编译的时候生成。这里来说下其需要用到的特性:
Invocation Handler
说到动态代理,就不得不提到Invocation handler,它是一个java.lang.reflect包中的一个接口,实现动态代理,必须继承自此接口。在此接口中,已经定义好了调用原对象的方法,我们只需要按照规约进行编码即可,如下:
public class MethodInvocationCountHandler implements InvocationHandler {
private int invocationCount = 0;
private final Object implementation;
public MethodInvocationCountHandler(final Object implementation) {
this.implementation = implementation;
}
public int getInvocationCount( ) {
return invocationCount;
}
public Object invoke(Object proxy, Method meth, Object[] args)
throws Throwable {
try {
this.invocationCount++;
Object result = meth.invoke(implementation, args);
return result;
} catch (final InvocationTargetException ex) {
throw ex.getTargetException( );
}
}
}
上面就是InvocationHandler的一个实现,当原对象中的方法被调用的时候,就会转到此invocation handler类中来执行(具体说来是转到invoke方法中来执行)。当执行完毕之后,会将得到的结果回传回去。这里需要注意的是,大部分情况下,我们都无需显式调用invoke方法来触发,此方法会被自动触发。
接下来,和之前一样,我们利用简单工厂方法来触发动态代理的调用:
public class CustomerClassFactory {
public static final Customer getDynamicSomeClassProxy( ) {
Customer customer = new Customer("Shohra","Afaque");
InvocationHandler handler = new MethodInvocationCountHandler(customer);
Class[] interfaces = new Class[] { Customer.class };
ClassLoader loader = CustomerClassFactory.class.getClassLoader( );
Customer proxy = (Customer)Proxy.newProxyInstance(loader, interfaces, handler);
return proxy;
}
}
public class Demo {
public static final void main(final String[] args) {
Customer proxy = CustomerClassFactory.getDynamicSomeClassProxy( );
proxy.printFullName( );
InvocationHandler handler = Proxy.getInvocationHandler(proxy);
if (handler instanceof MethodCountingHandler) {
System.out.println(((MethodCountingHandler)handler).getInvocationCount());
}
}
}
上面就是动态代理的触发调用方式,从这里看出,我们不需要像静态代理那样,专门定义一个代理类。我们只需要继承自InvocationHandler接口并实现就行了。同时在触发的时候,我们需要将原对象传入,然后利用Proxy.newProxyInstance创建代理对象并返回就行了。
由于在RPC中,client端都是通过这种动态代理的原理来进行的,所以你应该能明白为什么consumer虽然没被显式调用,但是可以触发方法调用了吧。
参考: https://medium.com/@shohraafaque/proxies-in-java-static-dynamic-8ccc51d16346