preface

Proxy pattern is a structural design pattern that provides additional access to the target object. That is, the target object is accessed through a proxy object.

The advantage of this is that you can augment the implementation of the target object with additional functional operations, that is, extending the functionality of the target object.

Here’s an idea that applies to programming: Don’t modify code or methods that someone else has already written, and if you need to modify them, extend the method by proxy.

There are roughly three roles in the proxy pattern:

  • Real Subject: real class, that is, proxyed class, delegate class. To truly complete the business service function;
  • Proxy: proxy class, uses its own request corresponding to the Real Subject function to implement, proxy class object does not really implement its business function;
  • Subject: Defines the interface that both the RealSubject and Proxy roles should implement.

There are three types of proxy modes: static proxy, dynamic proxy (JDK proxy, interface proxy), and Cglib proxy (dynamically subclasses the target object in memory).

The body of the

Static agent

A static proxy needs to define an interface, implement the same interface with the propped object, and then call the target object’s methods by calling the same methods.

As you can see, the proxy class simply adds some operations before and after calling the delegate class method. Different delegate classes lead to different proxy classes.

A company produces television sets and needs to find an agent to sell them locally. When the customer needs to buy a TV set, he can buy it directly through an agent.

Code examples:

public class TV {

    private String name;/ / name

    private String address;/ / producer

    public TV(String name, String address) {
        this.name = name;
        this.address = address;
    }

    public String getName(a) {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress(a) {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString(a) {
        return "TV{" +
                "name='" + name + '\' ' +
                ", address='" + address + '\' ' +
                '} '; }}Copy the code

Create a corporate interface:

public interface TVCompany {

    /** ** * @return TV */
    public TV produceTV(a);
}

Copy the code

The company’s factories produce television sets:

public class TVFactory implements TVCompany {
    @Override
    public TV produceTV(a) {
        System.out.println("TV factory produce TV...");
        return new TV("Xiaomi TV".Hefei ""); }}Copy the code

Agent to place an order to take goods (static agent class) :

public class TVProxy implements TVCompany{

    private TVCompany tvCompany;

    public TVProxy(a){} @Override
    public TV produceTV(a) {
        System.out.println("TV proxy get order .... ");
        System.out.println("TV proxy start produce .... ");
        if(Objects.isNull(tvCompany)){
            System.out.println("machine proxy find factory .... ");
            tvCompany = new TVFactory();
        }
        returntvCompany.produceTV(); }}Copy the code

The consumer takes goods through an agent:

public class TVConsumer {

    public static void main(String[] args) {
        TVProxy tvProxy = newTVProxy(); TV tv = tvProxy.produceTV(); System.out.println(tv); }}Copy the code

Output result:

TV proxy get order .... 
TV proxy start produce .... 
machine proxy find factory .... 
TV factory produce TV...
TV{name='Xiaomi TV', address='hefei'}

Process finished with exit code 0

Copy the code

Summary:

  • Advantages: The static proxy mode extends the function of the target object without changing the target object.

  • Disadvantages: Static proxies implement all methods of the target object. Once the target interface adds methods, both the proxy object and the target object need to be modified accordingly, increasing maintenance costs.

How do you address the disadvantages in static proxies? The answer is to use dynamic proxies

A dynamic proxy

Dynamic proxy has the following characteristics:

  1. JDK dynamic proxy objects do not need to implement interfaces, only target objects.

  2. Implementing interface-based dynamic proxies requires dynamically building Proxy objects in JVM memory using JDK apis.

  3. You need to use java.lang.reflect.Proxy and its newProxyInstance method, but the method takes three arguments.

Note that this method is static in the Proxy class and receives three parameters in order:

  • ClassLoader loader: specifies that the current target object uses a class loader. The method of obtaining the loader is fixed.
  • Class<? >[] interfaces: Specifies the type of the interface implemented by the target object, using generics to confirm the type.
  • InvocationHandler h: event handling. When a method of the target object is executed, the method of the event handler is fired, passing in the currently executing method of the target object as an argument.

One day the company increased its business, sold more and more products, and needed better after-sales service. However, the company found that the original agent needed retraining to complete all the business, so it turned to another dynamic agent B. Agent B promises to seamlessly connect all the business of the company, and no matter what new business is added, it can be completed without additional training.

Code examples:

The company has increased its maintenance business:

public interface TVCompany {

    /** ** * @return TV */
    public TV produceTV(a);

    /** * repair TV * @param TV * @return TV */
    public TV repair(TV tv);
}

Copy the code

Factories also have to get their maintenance business going:

public class TVFactory implements TVCompany {
    @Override
    public TV produceTV(a) {
        System.out.println("TV factory produce TV...");
        return new TV("Xiaomi TV".Hefei "");
    }

    @Override
    public TV repair(TV tv) {
        System.out.println("tv is repair finished...");
        return new TV("Xiaomi TV".Hefei ""); }}Copy the code

B Agent represents all the business of the company. Use the proxy.newProxyInstance method to generate a Proxy object and implement the Invoke method in InvocationHandler, which calls the Proxy class’s methods through reflection and provides enhanced methods.

public class TVProxyFactory {

    private Object target;

    public TVProxyFactory(Object o){
        this.target = o;
    }

    public Object getProxy(a){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("TV proxy find factory for tv.... ");
                Object invoke = method.invoke(target, args);
                returninvoke; }}); }}Copy the code

B agent can directly handle the purchase and maintenance of these two businesses. After the company increases business, B agent can also handle the same.

public class TVConsumer {

    public static void main(String[] args) {
        TVCompany target = new TVFactory();
        TVCompany tvCompany = (TVCompany) newTVProxyFactory(target).getProxy(); TV tv = tvCompany.produceTV(); tvCompany.repair(tv); }}Copy the code

Output result:

TV proxy find factory for tv.... 
TV factory produce TV...
TV proxy find factory for tv.... 
tv is repair finished...

Process finished with exit code 0

Copy the code

Summary:

  1. The proxy object does not need to implement the interface, but the target object must implement the interface otherwise dynamic proxies cannot be used.

  2. In the dynamic proxy approach, all function calls are eventually forwarded by the Invoke function, so we can do whatever we want here, such as logging, transactions, interceptors, permission controls, etc.

One of the most deadly problems with JDK dynamic proxies is that they can only proxy classes that implement an interface, and that proxy classes can only proxy methods that are implemented in the interface. If the implementation class has its own private methods that are not in the interface, that method cannot make proxy calls.

How to solve this problem? We can use CGLIB dynamic proxy mechanism.

Additional agent

Both static proxies and JDK proxies require an object to implement an interface, and sometimes the proxy object is just a single object, in which case the Cglib proxy can be used.

A Cglib proxy, which can be called a subclass proxy, builds a subclass object in memory to extend the functionality of the target object.

C agent not only wants to represent the company, but also wants to represent the products of multiple factories.

Cglib generates a proxy class through Enhancer by implementing the MethodInterceptor interface and implementing the Intercept Method in it, where enhanced methods can be added and the original Method can be called using the reflection Method or MethodProxy inheritance class.

Agent B can only act as an agent for the products of a certain company. However, I not only want to act as an agent for the products of our company, but also connect with different factories, so that we can get more goods and make more money. That’s where Cglib comes in.

Code examples:

public class TVProxyCglib implements MethodInterceptor {

    // Create a proxy object for the target object
    public Object getProxyInstance(Class c){
        / / 1. Utility class
        Enhancer enhancer = new Enhancer();
        //2. Set the parent class
        enhancer.setSuperclass(c);
        // set the callback function
        enhancer.setCallback(this);
        //4. Create subclasses (proxy object)
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("TVProxyFactory enhancement.....");
        Object object = methodProxy.invokeSuper(o, objects);
        returnobject; }}Copy the code

New agent B factory

public class TVFactoryB {

    public TV produceTVB(a) {
        System.out.println("tv factory B producing tv.... ");
        return new TV("Huawei TV"."Nanjing");
    }

    public TV repairB(TV tv) {
        System.out.println("tv B is repair finished.... ");
        returntv; }}Copy the code

C agent can directly cooperate with the company or deal with the factory. And can be the agent of any factory products.

public class TVConsumer {

    public static void main(String[] args) {
        TVCompany tvCompany = (TVCompany) new TVProxyCglib().getProxyInstance(TVFactory.class);
        TV tv = tvCompany.produceTV();
        tvCompany.repair(tv);
        System.out.println("= = = = = = = = = = = = = = = = = = = = = = = = = = = = = =");

        TVFactoryB tvFactoryB = (TVFactoryB) newTVProxyCglib().getProxyInstance(TVFactoryB.class); TV tv = tvFactoryB.produceTVB(); tvFactoryB.repairB(tv); }}Copy the code

Output result:

TVProxyFactory enhancement.....
TV factory produce TV...
TVProxyFactory enhancement.....
tv is repair finished...
==============================
TVProxyFactory enhancement.....
tv factory B producing tv.... 
TVProxyFactory enhancement.....
tv B is repair finished.... 

Process finished with exit code 0

Copy the code

AOP in Spring uses proxies

There are two implementations of AOP in Spring: JDK and Cglib.

If the target object needs to implement the interface, use a JDK proxy.

If the target object does not need to implement the interface, the Cglib proxy is used.

conclusion

  1. Static proxy: A method that requires both the proxy class and the target class to implement the interface for the proxy to enhance its functionality.

  2. JDK dynamic Proxy: You need a Proxy class to implement an interface, use the proxy.newProxyInstance method to generate the Proxy class, and implement the invoke method in InvocationHandler to implement enhanced functionality.

  3. Cglib dynamic Proxy: Instead of implementing the proxy class interface, use Enhancer in Cblib to subclass the proxy object and implement the Intercept method in MethodInterceptor, where enhancements can be implemented.

Tags: [Java]