代理是一种常用的设计模式,给某个对象提供一个代理,并由代理对象来控制对真实对象的访问。它的方式就是让代理类持有目标类的实例,并在对目标类进行操作的前后加入一层代理操作。
1. 静态代理
静态代理类在编译时就实现好了,它在Java编译完成后就是一个确定的class文件
- Proxy:代理主题,用来代理和封装真实主题;
- Subject:抽象主题,定义代理类和真实主题的公共对外方法,即需要代理的方法。
- RealSubject:真实主题,真正实现业务逻辑的类;
客户端直接面对的是代理类,通过代理类对目标类进行访问。这样当需要在访问过程中进行一些操作时,就可以不用修改已有的实现类,只需将操作加到代理类中。
示例
:Subject1 2 3 4
| public interface Handler {
void handle(String arg); }
|
:RealSubject1 2 3 4 5 6 7
| public class HandlerImpl implements Handler {
@Override public void handle(String arg) { System.out.println("handle[" + arg + "]"); } }
|
:Proxy1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class HandlerProxy implements Handler {
private Handler handler;
public HandlerProxy(Handler target){ this.handler = target; }
@Override public void handle(String arg) { System.out.println("before..."); handler.handle(arg); System.out.println("after..."); } }
|
:Client mark:3,4,51 2 3 4 5 6 7
| public class Client { public static void main(String[] args) { Handler handler = new HandlerImpl(); HandlerProxy handlerProxy = new HandlerProxy(handler); handlerProxy.handle("test"); } }
|
通过代理类可以避免对目标类的修改,并增加了期望的行为,同时保证了调用者所调用的类型不变。但是这种代理方式显得不够灵活,如果接口发生了改变,必须同时改变目标类和代理类。
假设现在有一个明确的代理操作,比如打印方法的执行耗时,那么如果目标接口的方法非常多,代理类就会显得很臃肿。如果希望将这个代理操作应用到更多不同的接口实现类上,那就更麻烦了,要给每个接口都定制一个代理类,可能你会想到用工厂模式来让代码结构更新清晰一点,但如此臃肿又麻烦的方式,显然早已违背了设计原则的初衷。
2. 动态代理
2.1. JDK动态代理
对于上面代理模式存在的问题,JDK肯定也想到了,于是提供了一种动态的方式来创建代理类,利用反射在运行时进行创建。
它提供了InvocationHandler
让你来定义期望的代理行为
:java.lang.reflect.InvocationHandler1 2 3
| public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
|
并提供了一个工厂方法,给你来创建代理类,这样你就可以将代理行为应用到任何接口上了
:java.lang.reflect.Proxy1
| public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
|
示例
mark:121 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class ProxyHandler implements InvocationHandler {
Object target;
public ProxyHandler(Object target){ this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(method, args); Object result = method.invoke(target, args); after(method, args); return result; }
private void before(Method method, Object[] args) { System.out.println("before " + method.getName()); }
private void after(Method method, Object[] args) { System.out.println("after " + method.getName()); } }
|
mark:81 2 3 4 5 6 7 8 9 10
| public class ProxyFactory { public static <T> T createProxy(T target){ ClassLoader classLoader = target.getClass().getClassLoader(); Class<?>[] interfaces = target.getClass().getInterfaces(); ProxyHandler proxyHandler = new ProxyHandler(target); return (T) Proxy.newProxyInstance(classLoader, interfaces, proxyHandler); } }
|
1 2 3 4 5 6 7 8 9
| public class Client { public static void main(String[] args) { System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Handler proxy = ProxyFactory.createProxy(new HandlerImpl()); proxy.handle("xxx"); } }
|
所以,利用JDK动态代理,只要在InvocationHandler
中定义好代理行为,然后在创建代理类再决定将代理行为添加到哪些接口上。只要代理行为是确定的,那么不管要对多少目标类进行代理,以及目标类如何变化,都可以轻松应付了,我们还可以在定义代理行为时根据method
来进行判断取舍
实现
通过工具jad -sjava $Proxy0.class
可以看一下生成的代理类class:
它实现了目标类的所有接口,并持有一个InvocationHandler
的实例。而InvocationHandler
中又持有了目标类的实例,并定义了代理行为。这样代理类最终将操作全部委托给了InvocationHandler
,也就不直接依赖于目标类了,实现了解耦。
这样的好处是很明显的,你只要定义好了代理行为,并给定接口,就能创建代理类实例进行执行,这也是Mybatis、Feign等能够实现接口注解的基础。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| public final class $Proxy0 extends Proxy implements Handler{
public $Proxy0(InvocationHandler invocationhandler){ super(invocationhandler); }
public final boolean equals(Object obj){ try{ return ((Boolean)super.h.invoke(this, m1, new Object[] {obj})).booleanValue(); }catch(Error _ex) { }catch(Throwable throwable){ throw new UndeclaredThrowableException(throwable); } }
public final void query(String s){ try{ super.h.invoke(this, m4, new Object[] {s}); return; }catch(Error _ex) { }catch(Throwable throwable){ throw new UndeclaredThrowableException(throwable); } }
public final String toString(){ try{ return (String)super.h.invoke(this, m2, null); }catch(Error _ex) {
}catch(Throwable throwable){ throw new UndeclaredThrowableException(throwable); } }
public final void delete(String s){ try{ super.h.invoke(this, m3, new Object[] {s}); return; }catch(Error _ex) {
}catch(Throwable throwable){ throw new UndeclaredThrowableException(throwable); } }
public final int hashCode(){ try{ return ((Integer)super.h.invoke(this, m0, null)).intValue(); }catch(Error _ex) {
}catch(Throwable throwable){ throw new UndeclaredThrowableException(throwable); } }
private static Method m1; private static Method m4; private static Method m2; private static Method m3; private static Method m0;
static { try{ m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m4 = Class.forName("org.eto.essay.Handler").getMethod("query", new Class[] { Class.forName("java.lang.String") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("org.eto.essay.Handler").getMethod("delete", new Class[] { Class.forName("java.lang.String") }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); }catch(NoSuchMethodException nosuchmethodexception){ throw new NoSuchMethodError(nosuchmethodexception.getMessage()); }catch(ClassNotFoundException classnotfoundexception){ throw new NoClassDefFoundError(classnotfoundexception.getMessage()); } } }
|
首先检查调用者的访问权限,然后尝试从缓存中检查代理类Class是否创建过,如果没有就新建一个,最后反射调用Class的构造器创建实例
:java.lang.reflect.Proxy1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| public class Proxy implements java.io.Serializable {
@CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)throws IllegalArgumentException{ Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); }
Class<?> cl = getProxyClass0(loader, intfs);
try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); }
final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
}
|
2.2. CGLIB动态代理
CGLIB是Spring提供的一种动态代理创建方式,与JDK动态代理类似,它也提供了MethodInterceptor
用来定义代理行为
:org.springframework.cglib.proxy.MethodInterceptor1 2 3
| public interface MethodInterceptor extends Callback { Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable; }
|
也提供了一个工厂,给你来创建代理类
:org.springframework.cglib.proxy.Enhancer1 2 3 4 5 6 7 8 9 10
| public class Enhancer extends AbstractClassGenerator {
private Class superclass;
private Callback[] callbacks;
public Object create(); }
|
不同的是CGLIB不再要求你提供目标类实现的接口了,因为它是通过继承的方式来进行代理,所以要代理的类或方法不能声明为final
示例
mark:151 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class ProxyInterceptor implements MethodInterceptor { private void before(Object object, Method method, Object[] objects) { System.out.println("before " + method.getName()); }
private void after(Object object, Method method, Object[] objects) { System.out.println("after " + method.getName()); }
@Override public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { before(object, method, args); Object result = methodProxy.invokeSuper(object, args); after(object, method, args); return result; } }
|
mark:81 2 3 4 5 6 7 8 9
| public class ProxyFactory { public static <T> T createProxy(Class<T> clazz){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(new ProxyInterceptor()); return (T)enhancer.create(); } }
|
1 2 3 4 5 6 7 8 9
| public class Client { public static void main(String[] args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/shanhuiming/");
Handler proxy = ProxyFactory.createProxy(Handler.class); proxy.handle("xxx"); } }
|