Proxy pattern of design pattern

There are six principles and twenty-three design patterns in the design pattern.
The six principles are: single responsibility principle, opening and closing principle, Richter substitution principle, dependency inversion principle, interface isolation principle and Dimitri principle.
Twenty three design patterns: Singleton pattern, Builder pattern, prototype pattern, factory method pattern, abstract factory pattern, policy pattern, state pattern, responsibility chain pattern, Interpreter pattern, command pattern, observer pattern, memo pattern, iterator pattern, template method pattern, visitor pattern, mediation pattern, agent pattern, composition pattern, adapter pattern Decoration mode, element sharing mode, appearance mode and bridging mode.
Now let's introduce the Proxy Pattern.

definition

Provide a proxy to other objects to control access to this object.
Explanation: I just don't want to expose myself to others and entrust others to help me realize some functions.

In my opinion, the enhancement on the Internet is actually to retain the original content and add their own logic (he can also rewrite the parent method implementation without deleting the super method).

Usage scenario

When an object cannot or does not want to be accessed directly, or an object is inconvenient to access, it can be accessed indirectly through a proxy object. To implement an agent, you need to implement the same interface as the delegate and the agent. The most typical proxy pattern is Spring's AOP (aspect)
For example, Xiaoming beat Xiaogang and Xiaogang sued Xiaoming. At this time, Xiaogang asked a lawyer to handle the matter on his behalf. If Xiaogang handles it by himself, he needs to submit an application, provide evidence, defend and other litigation processes. After entrusting a lawyer, Xiao Gang entrusts these materials to a lawyer, and the lawyer can handle the matter instead of Xiao Gang. In dealing with these processes, lawyers have added some things of their own for the success of litigation (this is what I think [enhancement]).

Proxy mode implementation:

Implementation mode of agent mode: static agent and dynamic agent, and dynamic agent is divided into jdk agent and Cglib agent.

Static proxy

Static proxy is to define two implementation classes a and B, both of which implement interface C. class B constructor passes in a, and then B can do something instead of A.

  • UML diagram

  • Sample code

  • Litigation process interface (define business interface)

package com.example.pattern.proxy.static
import org.slf4j.Logger
import org.slf4j.LoggerFactory

/**
 * Litigation process
 */
interface LitigationProcessInterface {
    // Filing a lawsuit
    fun submitLawsuit();
    // Proof
    fun proof();
    // defend
    fun defend();

    fun finish(){
        val logger: Logger = LoggerFactory.getLogger(this::class.java)
        logger.info("----- LitigationProcessInterface::finish   end");
    }
}
  • Materials provided by Xiao Gang (represented object)
package com.example.pattern.proxy.static
import org.slf4j.Logger
import org.slf4j.LoggerFactory
class XiaoGang : LitigationProcessInterface {
    private val logger: Logger = LoggerFactory.getLogger(this::class.java)

    override fun submitLawsuit() {
        logger.info("----- XiaoGang::::::submitLawsuit  Xiao Gang filed a lawsuit");
    }

    override fun proof() {
        logger.info("----- XiaoGang::::::proof  Xiao Gang adduces evidence");
    }

    override fun defend() {
        logger.info("----- XiaoGang::::::defend  Xiaogang defense");
    }
}
  • Attorney (agent)
package com.example.pattern.proxy.static
import org.slf4j.Logger
import org.slf4j.LoggerFactory
/**
 * lawyer
 */
class Lawyer(private val xiaoGang: XiaoGang) : LitigationProcessInterface {
    private val logger: Logger = LoggerFactory.getLogger(this::class.java)
    override fun submitLawsuit() {
        logger.info("----- Lawyer::submitLawsuit1  Before the lawyer replaces Xiao Gang's lawsuit application")
        // Application submitted by Xiao Gang
        xiaoGang.submitLawsuit()
        logger.info("----- Lawyer::submitLawsuit2  After the lawyer applied for the lawsuit instead of Xiao Gang")
    }
    override fun proof() {
        logger.info("----- Lawyer::proof1  The lawyer before Xiao Gang's proof")
        // Evidence submitted by Xiao Gang
        xiaoGang.proof()
        logger.info("----- Lawyer::proof2  After Xiao Gang provided evidence")
    }
    override fun defend() {
        logger.info("----- Lawyer::defend1  The lawyer is in front of Xiaogang's defense")
        // Xiao Gang's defense
        xiaoGang.defend();
        logger.info("----- Lawyer::defend2  After Xiao Gang's defense")
    }
}
  • Process implementation (Implementation)
package com.example.pattern.proxy.static
object Client {
    @JvmStatic fun main(args: Array<String>) {
        val xiaoGang:LitigationProcessInterface = XiaoGang();
        val lawyer = Lawyer(xiaoGang as XiaoGang)
        lawyer.submitLawsuit()
        lawyer.proof()
        lawyer.defend()
        lawyer.finish()
    }
}
  • Log information:
12:22:58.491 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::submitLawsuit1  Before the lawyer replaces Xiao Gang's lawsuit application
12:22:58.496 [main] INFO com.example.pattern.proxy.static.XiaoGang - ----- XiaoGang::::::submitLawsuit  Xiao Gang filed a lawsuit
12:22:58.496 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::submitLawsuit2  After the lawyer applied for the lawsuit instead of Xiao Gang
12:22:58.496 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::proof1  The lawyer before Xiao Gang's proof
12:22:58.496 [main] INFO com.example.pattern.proxy.static.XiaoGang - ----- XiaoGang::::::proof  Xiao Gang adduces evidence
12:22:58.496 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::proof2  After Xiao Gang provided evidence
12:22:58.496 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::defend1  The lawyer is in front of Xiaogang's defense
12:22:58.496 [main] INFO com.example.pattern.proxy.static.XiaoGang - ----- XiaoGang::::::defend  Xiaogang defense
12:22:58.496 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- Lawyer::defend2  After Xiao Gang's defense
12:22:58.497 [main] INFO com.example.pattern.proxy.static.Lawyer - ----- LitigationProcessInterface::finish   end

Although the static proxy is very efficient, it needs to be used in combination with the business. The implementation classes need to implement the business interface to realize the function. This also limits that a proxy object needs to be implemented once. If there is another proxy object, it also needs to implement another proxy object. At this time, it is not very convenient to use. Then dynamic agent solves this problem.

The above functions are implemented by using interface. In fact, they can also be implemented by using abstract. It's just that implements has been changed to extends

Dynamic agent

The above static agent is compiled by the code generated by the programmer or tool, that is, the compiled file already exists when the code is run. On the contrary, dynamic proxy dynamically generates proxy objects through reflection mechanism.
Java has provided us with a convenient proxy interface InvocationHandler. Just implement it.

  • Sample code
  • Litigation process interface (define business interface)
package com.example.pattern.proxy.dynamic;
/**
 * Litigation process
 */
public interface LitigationProcessInterface {
    // Filing a lawsuit
    void submitLawsuit();
    // Proof
    void proof();
    // defend
    void defend();
    // end
    void finish();
}
  • Materials provided by Xiao Gang (represented object)
package com.example.pattern.proxy.dynamic;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * What Xiaogang needs to provide
 */
public class XiaoGang implements LitigationProcessInterface {
    private Logger logger = LoggerFactory.getLogger( this.getClass());
    public void submitLawsuit() {
        logger.info("----- XiaoGang::::::submitLawsuit  Xiao Gang filed a lawsuit");
    }

    public void proof() {
        logger.info("----- XiaoGang::::::proof  Xiao Gang adduces evidence");
    }

    public void defend() {
        logger.info("----- XiaoGang::::::defend  Xiaogang defense");
    }

    public void finish() {
        logger.info("----- XiaoGang::::::finish   end");
    }
}
  • Proxy object
package com.example.pattern.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * proxy class
 */
public class DynamicProxy implements InvocationHandler {
    private Object object;
    public DynamicProxy(Object o) {
        this.object = o;
    }

    /**
     * reflex
     *
     * @param proxy  Proxy instance
     * @param method method
     * @param args   Method parameters
     * @return result
     * @throws Throwable abnormal
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(this.object, args);
    }
}
  • realization
package com.example.pattern.proxy.dynamic;

import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {
        LitigationProcessInterface xiaoGang = new XiaoGang();
        DynamicProxy dynamicProxy= new DynamicProxy(xiaoGang);
        //Get the ClassLoader of the proxy object Xiao Gang
        ClassLoader xiaoGangClassLoader = xiaoGang.getClass().getClassLoader();
        // Dynamically construct an attorney
        LitigationProcessInterface lawyer = (LitigationProcessInterface) Proxy.newProxyInstance(xiaoGangClassLoader, new Class[]{LitigationProcessInterface.class}, dynamicProxy);
        lawyer.submitLawsuit();
        lawyer.proof();
        lawyer.defend();
        lawyer.finish();
        
    }
}
  • Log
13:23:01.047 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::submitLawsuit  Xiao Gang filed a lawsuit
13:23:01.050 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::proof  Xiao Gang adduces evidence
13:23:01.050 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::defend  Xiaogang defense
13:23:01.050 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::finish   end

The above code is very similar to the static proxy, but when another proxy object is added, the static proxy needs to re implement a class (limitationprocessinterface), while the dynamic proxy does not need to. Just use the proxy under the instance.

In fact, defining a proxy object DynamicProxy alone is also limited. It cannot add additional things (that is, the so-called enhancement). If additional things need to be added, each proxy object needs to be redefined, which is no different from static proxy.

Then we can also directly create an InvocationHandler object, so that we can add additional things.

package com.example.pattern.proxy.dynamic;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {
        final Logger logger = LoggerFactory.getLogger( Client.class);
        final LitigationProcessInterface xiaoGang = new XiaoGang();
        // Method 2
        InvocationHandler invocationHandler = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if ("submitLawsuit".equals(method.getName())){
                    logger.info("----- Lawyer::submitLawsuit1  Before the lawyer replaces Xiao Gang's lawsuit application");
                    method.invoke(xiaoGang, args);
                    logger.info("----- Lawyer::submitLawsuit2  After the lawyer applied for the lawsuit instead of Xiao Gang");

                } else if ("proof".equals(method.getName())){
                    logger.info("----- Lawyer::proof1  The lawyer before Xiao Gang's proof");
                    method.invoke(xiaoGang, args);
                    logger.info("----- Lawyer::proof2  After Xiao Gang provided evidence");
                } else if ("defend".equals(method.getName())){
                    logger.info("----- Lawyer::defend1  The lawyer is in front of Xiaogang's defense");
                    method.invoke(xiaoGang, args);
                    logger.info("----- Lawyer::defend2  After Xiao Gang's defense");
                } else if ("finish".equals(method.getName())){
                    logger.info("----- Lawyer::finish1  Before the end");
                    method.invoke(xiaoGang, args);
                    logger.info("----- Lawyer::finish2  After the end");
                }
                return null;
            }
        };
        //Get the ClassLoader of the proxy object Xiao Gang
        ClassLoader xiaoGangClassLoader = xiaoGang.getClass().getClassLoader();
        // Dynamically construct an attorney
        LitigationProcessInterface lawyer = (LitigationProcessInterface) Proxy.newProxyInstance(xiaoGangClassLoader, new Class[]{LitigationProcessInterface.class}, invocationHandler);
        lawyer.submitLawsuit();
        lawyer.proof();
        lawyer.defend();
        lawyer.finish();
    }
}
  • Log
13:50:04.564 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::submitLawsuit1  Before the lawyer replaces Xiao Gang's lawsuit application
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::submitLawsuit  Xiao Gang filed a lawsuit
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::submitLawsuit2  After the lawyer applied for the lawsuit instead of Xiao Gang
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::proof1  The lawyer before Xiao Gang's proof
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::proof  Xiao Gang adduces evidence
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::proof2  After Xiao Gang provided evidence
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::defend1  The lawyer is in front of Xiaogang's defense
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::defend  Xiaogang defense
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::defend2  After Xiao Gang's defense
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::finish1  Before the end
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.XiaoGang - ----- XiaoGang::::::finish   end
13:50:04.567 [main] INFO com.example.pattern.proxy.dynamic.Client - ----- Lawyer::finish2  After the end

Is this kind of Log similar to the first one, and it no longer needs to create multiple proxy objects.

Cglib agent

Both of the above two agents need to implement specific interfaces to realize the agent function or enhance the function. The Cglib package provided by Spring simplifies the implementation interface, and the proxy can be implemented as long as it is a separate object.

  • Define proxied objects
package com.example.pattern.proxy.enhancer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * What Xiaogang needs to provide
 */
public class XiaoGang {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    public void submitLawsuit() {
        logger.info("----- XiaoGang::::::submitLawsuit  Xiao Gang filed a lawsuit");
    }

    public void proof() {
        logger.info("----- XiaoGang::::::proof  Xiao Gang adduces evidence");
    }

    public void defend() {
        logger.info("----- XiaoGang::::::defend  Xiaogang defense");
    }

    public void finish() {
        logger.info("----- XiaoGang::::::finish   end");
    }
}
  • Proxy object
package com.example.pattern.proxy.enhancer;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class ProxyInterceptor implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("----- ProxyFactory:::::: intercept  start ");
        //Method of executing target object
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("----- ProxyFactory:::::: intercept  end ");
        return result;
    }
}
  • realization
package com.example.pattern.proxy.enhancer;

import org.springframework.cglib.proxy.Enhancer;

public class Client {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        //Inherit the proxied class
        enhancer.setSuperclass(XiaoGang.class);
        //Set callback
        enhancer.setCallback(new ProxyInterceptor());
//        enhancer.setCallback(new MethodInterceptor() {
//            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//                //Can be enhanced
//                return null;
//            }
//        });
//        enhancer.setCallbackFilter(new CallbackFilter() {
//            public int accept(Method method) {
//                return 0;
//            }
//        });
        //Set proxy class object
        XiaoGang xiaoGang = (XiaoGang) enhancer.create();
        //When calling the method in the proxy class, it will be intercepted by the method interceptor we implemented
        xiaoGang.submitLawsuit();
        xiaoGang.proof();
        xiaoGang.defend();
        xiaoGang.finish();
    }
}
  • LOG
----- ProxyFactory:::::: intercept  start 
14:22:11.317 [main] INFO com.example.pattern.proxy.enhancer.XiaoGang$$EnhancerByCGLIB$$4d9ceeba - ----- XiaoGang::::::submitLawsuit  Xiao Gang filed a lawsuit
----- ProxyFactory:::::: intercept  end 
----- ProxyFactory:::::: intercept  start 
14:22:11.320 [main] INFO com.example.pattern.proxy.enhancer.XiaoGang$$EnhancerByCGLIB$$4d9ceeba - ----- XiaoGang::::::proof  Xiao Gang adduces evidence
----- ProxyFactory:::::: intercept  end 
----- ProxyFactory:::::: intercept  start 
14:22:11.320 [main] INFO com.example.pattern.proxy.enhancer.XiaoGang$$EnhancerByCGLIB$$4d9ceeba - ----- XiaoGang::::::defend  Xiaogang defense
----- ProxyFactory:::::: intercept  end 
----- ProxyFactory:::::: intercept  start 
14:22:11.320 [main] INFO com.example.pattern.proxy.enhancer.XiaoGang$$EnhancerByCGLIB$$4d9ceeba - ----- XiaoGang::::::finish   end
----- ProxyFactory:::::: intercept  end 

This kind of agent is simpler and more convenient than dynamic agent. If you want to extend the method, you can directly implement the new MethodInterceptor, which is similar to the dynamic agent new InvocationHandler.

summary

Learning to use design patterns can make our code more beautiful. In our programming process, we often use design patterns in the code we write, but it's not clear.
After checking blogs and books, I have a preliminary understanding of the agent model. If the writing is not good, please correct it.
demo address: https://github.com/wdmxzf/java-example/tree/pattern

Keywords: Java Design Pattern

Added by MrKaraokeSteve on Sat, 06 Nov 2021 15:25:09 +0200