静态代理 VS 动态代理
什么是代理模式?
代理模式是一种结构型模式,当需要控制和管理受保护对象的访问的时候,我们就需要创建一个代理类。代理模式不仅广泛运用在程序世界中,现实生活中也有广泛运用,比如我们通过第三方应用软件购买火车票,通过中介租房,银行卡作为银行账户的代理等等。代理模式中,代理对象和被代理的对象拥有相同的方法,客户端不是直接调用对象方法而是调用代理对象方法,通过代理模式我们可以方便的实现以下功能:
- 在方法执行前后打印日志
- 记录方法的执行时间
- 对方法参数进行检查等等
静态代理
下面以互联网访问的例子来演示静态代理的功能,通过静态代理模式控制对互联网的访问。
- step one
创建internet接口
public interface Internet { void connectTo(String hostname); }
- step two
创建真实对象
public class RealInternet implements Internet { @Override public void connectTo(String hostname) { System.out.println("Connecting to " + hostname + " ......"); System.out.println("You have successed to connect " + hostname); } }
创建代理对象
public class FakeInternetProxy implements Internet { private Internet internet = new RealInternet(); private static List<String> banList; static { banList = new ArrayList<>(); banList.add("www.google.com"); banList.add("www.facebook.com"); banList.add("www.youtube.com"); } @Override public void connectTo(String hostname) { if (banList.contains(hostname)) { System.out.println("404 page not found"); } else { System.out.println("Welcome to the real internet!"); internet.connectTo(hostname); } } }
- step three
客户端通过代理对象访问互联网
public class Client { public static void main(String[] args) { Internet internet = new FakeInternetProxy(); internet.connectTo("www.google.com"); internet.connectTo("www.baidu.com"); } /* 404 page not found Welcome to the real internet! Connecting to www.baidu.com ...... You have successed to connect www.baidu.com */ }
以上例子演示了静态代理模式控制和管理对受保护对象的访问。动态代理模式也是代理模式的一种实现,动态代理类是在运行时创建的,而静态代理类则是在编译时就已经创建。
动态代理
静态代理模式在实现特定目标时很适用,但设想有这样一个通用目标:我们需要记录客户端调用的任意对象的任意方法的运行时间。如果使用静态代理来实现该功能,我们需要为每一个对象创建一个代理类。当客户端操作的类数量过多时,该功能实现将是一个繁琐的工程,同时需要消耗大量内存。Don't Repeat Yourself,此时使用动态代理则是一个更加合适的选择。
下面是用动态代理的方法来实现该功能:
- step one
首先创建invocation handler的子类,实现执行时间记录的功能。
public class TimingInvocationHandler implements InvocationHandler { private Object target; TimingInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long startTime = System.nanoTime(); Object result = methods.get(method.getName()).invoke(target, args); long duration = System.nanoTime() -startTime; System.out.println("Executing " + method.getName() + " finished in " + duration + " ns"); return result; } }
- step two
创建代理类,通过代理类调用相应的方法。以下创建了HashMap的代理类。
public class Client { public static void main(String[] args) { Map mapProxy = (Map) Proxy.newProxyInstance( Client.class.getClassLoader(), new Class[] {Map.class}, new TimingInvocationHandler(new HashMap<String, String>()) ); mapProxy.put("test", "test"); mapProxy.get("test"); mapProxy.put("hello", "world"); /* Executing put finished in 540281 ns Executing get finished in 18928 ns Executing put finished in 4208 ns */ } }
总结
本文介绍了代理模式,并介绍了代理模式的两种实现方式:静态代理和动态代理。一般来说需要当实现一些通用目标时应该优先考虑使用动态代理,除了上述方法执行时间记录外,像日志记录,事务管理都应该优先考虑动态代理。
- 代理模式主要用来控制和管理受保护对象的访问
- 静态代理代理类在编译期创建,动态代理则在运行期创建。