Structural pattern - proxy pattern

Structural patterns describe how to organize classes or objects into larger structures in a certain layout. It is divided into class structure pattern and object structure pattern. The former uses inheritance mechanism to organize interfaces and classes, and the latter uses combination or aggregation to combine objects.

Since the combination or aggregation relationship is less coupled than the inheritance relationship and satisfies the "principle of composite reuse", the object structure model has greater flexibility than the class structure model.

Table of contents

1. Proxy mode

1.1 Overview

1.2 Structure

1.3 Static Proxy

1.4 JDK dynamic proxy

1.5 CGLIB dynamic proxy

1.7 Advantages and disadvantages

1.8 Usage Scenarios


1. Proxy mode

1.1 Overview

For some reason it is necessary to provide an object with a proxy to control access to that object. At this time, the access object is not suitable or cannot directly refer to the target object, and the proxy object acts as an intermediary between the access object and the target object.

Proxies in Java are divided into static proxies and dynamic proxies according to the generation time of proxy classes . The static proxy class is generated at compile time, while the dynamic proxy class is dynamically generated at Java runtime. There are two types of dynamic proxy: JDK proxy and CGLib proxy.

1.2 Structure

The Proxy mode is divided into three roles:

  • Abstract subject (Subject) class: Declare the business methods implemented by real subjects and proxy objects through interfaces or abstract classes.
  • Real Subject (Real Subject) class: realizes the specific business in the abstract subject, is the real object represented by the proxy object, and is the final object to be referenced.
  • Proxy (Proxy) class: Provides the same interface as the real theme, which contains references to the real theme, which can access, control or extend the functions of the real theme.

1.3 Static Proxy

Let's take a look at the static proxy through a case.

[Example] selling tickets at a train station

If you want to buy a train ticket, you need to go to the train station to buy the ticket, take the train to the train station, wait in line and wait for a series of operations, which is obviously more troublesome. And the railway station has sales offices in many places, so it is much more convenient for us to go to the sales offices to buy tickets. This example is actually a typical agent model, the train station is the target object, and the sales agency is the agent object.

 

//卖票接口
public interface SellTickets {
    void sell();
}

//火车站  火车站具有卖票功能,所以需要实现SellTickets接口
public class TrainStation implements SellTickets {

    public void sell() {
        System.out.println("火车站卖票");
    }
}

//代售点
public class ProxyPoint implements SellTickets {

    private TrainStation station = new TrainStation();

    public void sell() {
        System.out.println("代理点收取一些服务费用");
        station.sell();
    }
}

//测试类
public class Client {
    public static void main(String[] args) {
        ProxyPoint pp = new ProxyPoint();
        pp.sell();
    }
}

 It can be seen from the above code that the test class directly accesses the ProxyPoint class object, that is to say, the ProxyPoint acts as an intermediary between the access object and the target object. At the same time, the sell method has also been enhanced (agents charge some service fees).

1.4 JDK dynamic proxy

Next, we use dynamic proxies to implement the above case. First, let's talk about the dynamic proxies provided by JDK. Java provides a dynamic proxy class Proxy. Proxy is not the class of the proxy object we mentioned above, but provides a static method (newProxyInstance method) to create a proxy object to obtain the proxy object.

//卖票接口
public interface SellTickets {
    void sell();
}

//火车站  火车站具有卖票功能,所以需要实现SellTickets接口
public class TrainStation implements SellTickets {

    public void sell() {
        System.out.println("火车站卖票");
    }
}

//代理工厂,用来创建代理对象
public class ProxyFactory {

    private TrainStation station = new TrainStation();

    public SellTickets getProxyObject() {
        //使用Proxy获取代理对象
        /*
            newProxyInstance()方法参数说明:
                ClassLoader loader : 类加载器,用于加载代理类,使用真实对象的类加载器即可
                Class<?>[] interfaces : 真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口
                InvocationHandler h : 代理对象的调用处理程序
         */
        SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(),
                station.getClass().getInterfaces(),
                new InvocationHandler() {
                    /*
                        InvocationHandler中invoke方法参数说明:
                            proxy : 代理对象
                            method : 对应于在代理对象上调用的接口方法的 Method 实例
                            args : 代理对象调用接口方法时传递的实际参数
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        System.out.println("代理点收取一些服务费用(JDK动态代理方式)");
                        //执行真实对象
                        Object result = method.invoke(station, args);
                        return result;
                    }
                });
        return sellTickets;
    }
}

//测试类
public class Client {
    public static void main(String[] args) {
        //获取代理对象
        ProxyFactory factory = new ProxyFactory();
        
        SellTickets proxyObject = factory.getProxyObject();
        proxyObject.sell();
    }
}

1.5 CGLIB dynamic proxy

In the same case as above, we again use CGLIB proxy implementation.

If the SellTickets interface is not defined, only TrainStation (the train station class) is defined. Obviously, the JDK proxy cannot be used, because the JDK dynamic proxy requires that the interface must be defined to proxy the interface.

CGLIB is a powerful, high-performance code generation package. It provides proxies for classes that do not implement interfaces, and provides a good complement to JDK's dynamic proxies.

CGLIB is a package provided by a third party, so the coordinates of the jar package need to be introduced:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>
//火车站
public class TrainStation {

    public void sell() {
        System.out.println("火车站卖票");
    }
}

//代理工厂
public class ProxyFactory implements MethodInterceptor {

    private TrainStation target = new TrainStation();

    public TrainStation getProxyObject() {
        //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
        Enhancer enhancer =new Enhancer();
        //设置父类的字节码对象
        enhancer.setSuperclass(target.getClass());
        //设置回调函数
        enhancer.setCallback(this);
        //创建代理对象
        TrainStation obj = (TrainStation) enhancer.create();
        return obj;
    }

    /*
        intercept方法参数说明:
            o : 代理对象
            method : 真实对象中的方法的Method实例
            args : 实际参数
            methodProxy :代理对象中的方法的method实例
     */
    public TrainStation intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理点收取一些服务费用(CGLIB动态代理方式)");
        TrainStation result = (TrainStation) methodProxy.invokeSuper(o, args);
        return result;
    }
}

//测试类
public class Client {
    public static void main(String[] args) {
        //创建代理工厂对象
        ProxyFactory factory = new ProxyFactory();
        //获取代理对象
        TrainStation proxyObject = factory.getProxyObject();

        proxyObject.sell();
    }
}

1.6 Comparison of three agents

  • jdk proxy and CGLIB proxy

    Use CGLib to implement dynamic proxy. The bottom layer of CGLib adopts ASM bytecode generation framework, and bytecode technology is used to generate proxy classes. Before JDK1.6, it is more efficient than using Java reflection. The only thing to note is that CGLib cannot proxy a class or method declared as final, because the principle of CGLib is to dynamically generate subclasses of the proxy class.

    After JDK1.6, JDK1.7, and JDK1.8 gradually optimized JDK dynamic proxy, the efficiency of JDK proxy is higher than that of CGLib proxy when the number of calls is small. Only when a large number of calls are made, JDK1.6 and JDK1.7 is a little less efficient than CGLib proxy, but when it comes to JDK1.8, JDK proxy is more efficient than CGLib proxy. So if there is an interface use JDK dynamic proxy, if there is no interface use CGLIB proxy.

  • Dynamic Proxy and Static Proxy

    Compared with static proxy, the biggest advantage of dynamic proxy is that all methods declared in the interface are transferred to a centralized method of invocation handler (InvocationHandler.invoke). In this way, when there are a large number of interface methods, we can handle them flexibly, without needing to transfer each method like a static proxy.

    If the interface adds a method, in addition to all implementation classes needing to implement this method in the static proxy mode, all proxy classes also need to implement this method. Increased the complexity of code maintenance. This problem does not occur with dynamic proxies

1.7 Advantages and disadvantages

advantage:

  • The proxy mode plays an intermediary role between the client and the target object and protects the target object;
  • The proxy object can extend the functionality of the target object;
  • The proxy mode can separate the client from the target object, reducing the coupling of the system to a certain extent;

shortcoming:

  • Increased system complexity;

1.8 Usage Scenarios

  • Remote proxy

    Local services request remote services over the network. In order to achieve local-to-remote communication, we need to implement network communication and handle possible exceptions. For good code design and maintainability, we hide the network communication part and only expose an interface to the local service, through which the functions provided by the remote service can be accessed without paying too much attention to the details of the communication part.

  • Firewall proxy

    When you configure your browser to use the proxy function, the firewall forwards your browser's request to the Internet; when the Internet returns a response, the proxy server forwards it to your browser.

  • Protect or Access agent

    Control access to an object and, if desired, provide different levels of access to different users.

Guess you like

Origin blog.csdn.net/qq_62799214/article/details/128894271