代理顾名思义,就是一个中间层,当我们要使用某个方法时,不直接调用,而是告诉代理,让代理替我们去请求方法,并返回结果。在这个过程中,我们只知道代理执行并返回给了我们操作结果,至于它有没有其他操作并不知道,但这个也无关紧要,因为不管代理在请求实际方法之前和之后做了什么,对我们都不会产生任何影响。由于代理的这个特性,可以用来进行记录日志等操作。
代理分为静态代理和动态代理。
静态代理
静态代理需要我们在调用者和具体的执行者之间创建一个代理类,其实现需要被代理的接口并持有一个此接口,具体代码如下:
接口类(被代理):
public interface Hello { void say(String name); }
代理类:
public class HelloProxy implements Hello { ? private Hello helloImpl; ? public HelloProxy() { this.helloImpl = new HelloImpl(); // HelloImpl为一个Hello接口的一个普通实现类 } ? @Override public void say(String name) { System.out.println("before say"); helloImpl.say(name); System.out.println("after say"); } }
使用的时候只需要new一个代理类赋值给接口即可:Hello hello = new HelloProxy()
,之后的执行会由代理类去负责。但是这么做有一个问题就是对每个需要代理的类,都需要创建一个对应的代理类,即使代理类的逻辑都是相似的,动态代理就是解决这个问题的。
动态代理
动态代理相当于在调用者和代理类之间又增加了一个中间层,在这个中间层中完成代理类的逻辑,达到复用代码的效果。这个中间层需要实现InvocationHandler
接口,并持有一个被代理的类,大致代码如下:
public class Myproxy implements InvocationHandler { ? private Object target; // 被代理的类 ? public Myproxy(Object target) { this.target = target; } ? @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before invoke"); // 执行代理的方法前进行的操作 Object invoke = method.invoke(target, args); // 调用执行被代理的方法 System.out.println("after invoke"); // 执行代理的方法后进行的操作 return invoke; // 返回方法执行结果 } }
使用时,使用Proxy的静态方法获取代理类,将其强转为对应的接口,之后可以同静态代理类同样使用。
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
其中loader为类加载器,interfaces为要代理的接口,h为之前定义的实现InvocationHandler
接口的类的实例。
这个方法会返回一个代理类,其实现的要代理的接口,并持有一个实现InvocationHandler
接口的实例,代理类的代码大致如下:
public final class $Proxy0 extends Proxy implements Hello { ? public $Proxy0(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } ? public final String say() { try { return ((String)this.h.invoke(this, m3, null)); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } ? }
如上代码我们可以将其强转为Hello接口,实际调用的时候会调用InvocationHandler
实现类中的invoke
方法,而其中就是我们实现的代理业务,当然,也包括实际的业务。
这样,对于有相同代理业务的类,我们只要实现InvocationHandler
接口,在其中的invoke
方法实现需要代理的业务,使用时只要使用Proxy.newProxyInstance
静态方法,传入对应得参数即可得到需要的代理类,再将其强转赋值给需要的接口,即可正常使用。