静态代理 VS 动态代理

什么是代理模式?

代理模式是一种结构型模式,当需要控制和管理受保护对象的访问的时候,我们就需要创建一个代理类。代理模式不仅广泛运用在程序世界中,现实生活中也有广泛运用,比如我们通过第三方应用软件购买火车票,通过中介租房,银行卡作为银行账户的代理等等。代理模式中,代理对象和被代理的对象拥有相同的方法,客户端不是直接调用对象方法而是调用代理对象方法,通过代理模式我们可以方便的实现以下功能:

  • 在方法执行前后打印日志
  • 记录方法的执行时间
  • 对方法参数进行检查等等

静态代理

Proxy1-2x.png

下面以互联网访问的例子来演示静态代理的功能,通过静态代理模式控制对互联网的访问。

  • 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
        */
    }
}

总结

本文介绍了代理模式,并介绍了代理模式的两种实现方式:静态代理和动态代理。一般来说需要当实现一些通用目标时应该优先考虑使用动态代理,除了上述方法执行时间记录外,像日志记录,事务管理都应该优先考虑动态代理。

  • 代理模式主要用来控制和管理受保护对象的访问
  • 静态代理代理类在编译期创建,动态代理则在运行期创建。
全部评论

相关推荐

06-12 10:50
门头沟学院 Java
你的不定积分没加C:我怎么在学院群看到了同样的话
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务