Hello everyone, I am the third.

In this section we will look at a very important design pattern, the proxy pattern, which although we may not use it much in our work, is the cornerstone of many important functions of the framework. Elbow, let’s begin.

The introduction

Holiday subway station, have you ever seen someone take out a computer, in situ output, the original group was crazy @……

Users ask questions, customer service @ we solve, may be many development students have experienced such a scene.

Users find customer service, put forward problems, customer service and find the development students, let the development students to solve the problem, the development students solve, finally feedback to customer service, customer service and then feedback to the user.

From the user’s point of view, the feeling is that customer service solved the problem, which is actually a kind of agency.

Let’s use this example to see how Java implements the proxy pattern.

Java three proxy pattern implementation

Definition of proxy mode:

Provide a surrogate or placeholder for another object to control access to it.

To put it simply, it is to set up an intermediate proxy to control the access to the original object, so as to enhance the function of the original object and simplify the access mode.

Java implementation proxy mode is divided into two classes three, two classes are static proxy and dynamic proxy, dynamic proxy can be divided into JDK dynamic proxy and CGLIB dynamic proxy.

Static agent

Static proxies are simpler; the proxy class needs to implement the same interface as the target interface class.

  • Interface class: ISolver

    public interface ISolver {
        void solve(a);
    }
    Copy the code
  • Target class: Solver

    public class Solver implements ISolver {
        @Override
        public void solve(a) {
            System.out.println("Crazy hair loss solution..."); }}Copy the code
  • Proxy class: SolverProxy, the proxy class also implements the interface and maintains a target object.

    public class SolverProxy implements ISolver {
        // Target object
        private ISolver target;
    
        public SolverProxy(ISolver target) {
            this.target = target;
        }
    
        @Override
        public void solve(a) {
            System.out.println("How can I help you?");
            target.solve();
            System.out.println("Problem solved!"); }}Copy the code
  • The client; Client

    public class Client {
        public static void main(String[] args) {
            // Target object: programmers
            ISolver developer = new Solver();
            // Agent: Customer service sister
            SolverProxy csProxy = new SolverProxy(developer);
            // Target method: solve the problemcsProxy.solve(); }}Copy the code
  • The results

    What can I do for you? Crazy hair loss to solve the problem... Problem solved!Copy the code

We saw that with static proxies, you can extend the functionality of the target object without modifying it.

However, it also has some problems:

  • Redundancy: There are too many proxy classes because the proxy object implements the same interface as the target object.
  • Poor maintenance: Once the interface adds methods, both the target object and the proxy object are modified.

JDK dynamic proxy

JDK dynamic proxy makes use of JDK reflection mechanism to dynamically build proxy objects in memory, thus realizing proxy function on target objects.

It mainly uses two reflection apis:

  • java.lang.reflect Proxy| static Object newProxyInstance(ClassLoader loader,Class
    [] interfaces, InvocationHandler h) : Returns an instance of a proxy class specifying the interface that assigns method calls to the specified call handler
  • Java.lang.reflect InvocationHandler | Object Invoke (Object Proxy, Method Method, Object[] args) : Invoke the target Method on the proxy instance and return the result.

Let’s take a look at the customer service agent scenario using the JDK dynamic agent.

  • Interface class: ISolver

    public interface ISolver {
        void solve(a);
    }
    Copy the code
  • Target class: Solver, which needs to implement the interface class.

    public class Solver implements ISolver {
        @Override
        public void solve(a) {
            System.out.println("Crazy hair loss solution..."); }}Copy the code
  • Dynamic ProxyFactory :ProxyFactory, the dynamic ProxyFactory, does not need to implement the interface, directly uses reflection to generate a target object proxy object instance.

    Ps: This uses the method of an anonymous inner class, and there is another method, the dynamic proxy class that implements the InvocationHandler interface, which is more or less similar, but I won’t give you any more examples.

    public class ProxyFactory {
    
        // Maintain a target object
        private Object target;
    
        public ProxyFactory(Object target) {
            this.target = target;
        }
    
        // Generate a proxy object for the target object
        public Object getProxyInstance(a) {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("May I help you?");
    
                            // Call the target object method
                            Object returnValue = method.invoke(target, args);
    
                            System.out.println("Problem solved!");
                            return null; }}); }}Copy the code
  • Client: Client

    The client generates an instance of the proxy object, and when it calls the target object’s methods from the proxy object, it enters the invoke() method, and finally calls the target object’s methods by reflection.

    public class Client {
        public static void main(String[] args) {
            // Target object: programmers
            ISolver developer = new Solver();
            // Agent: Customer service sister
            ISolver csProxy = (ISolver) new ProxyFactory(developer).getProxyInstance();
            // Target method: solve the problemcsProxy.solve(); }}Copy the code
  • Running results:

    What can I do for you? Crazy hair loss to solve the problem... Problem solved!Copy the code

Let’s briefly summarize the main differences between static and dynamic proxies:

  • The static proxy is implemented at compile time, when the proxy class is an actual class file
  • Dynamic proxies are dynamically generated at run time, meaning that there are no actual class files after compilation, but instead, class bytecodes are dynamically generated at run time and loaded into the JVM

We also observed that JDK dynamic proxies, the target object must implement the interface, that is, it is interface oriented, what if we do not want the interface?

Cglib dynamic proxy

CGLIB(Code Generation Library) is a bytecode Generation Library based on ASM, which allows us to modify and dynamically generate bytecode at run time. It is implemented through inheritance.

Let’s take a look at what our customer service agent looks like with Cglib:

  • Importing dependencies: Cglib is a third-party class library that needs to import dependencies

            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>3.2. 5</version>
            </dependency>
    Copy the code
  • Target class: Solver, where the target class no longer implements the interface.

    public class Solver {
    
        public void solve(a) {
            System.out.println("Crazy hair loss solution..."); }}Copy the code
  • Dynamic proxy factory:

    public class ProxyFactory implements MethodInterceptor {
    
       // Maintain a target object
        private Object target;
    
        public ProxyFactory(Object target) {
            this.target = target;
        }
    
        // Generate a proxy object for the target object
        public Object getProxyInstance(a) {
            / / tools
            Enhancer en = new Enhancer();
            // Set the parent class
            en.setSuperclass(target.getClass());
            // Set the callback function
            en.setCallback(this);
            // Create a subclass object proxy
            return en.create();
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("May I help you?");
            // Execute the target object's method
            Object returnValue = method.invoke(target, args);
            System.out.println("Problem solved!");
            return null; }}Copy the code
  • Client: Client

    public class Client {
        public static void main(String[] args) {
            // Target object: programmers
            Solver developer = new Solver();
            // Agent: Customer service sister
            Solver csProxy = (Solver) new ProxyFactory(developer).getProxyInstance();
            // Target method: solve the problemcsProxy.solve(); }}Copy the code
  • The results

    What can I do for you? Crazy hair loss to solve the problem... Problem solved!Copy the code

The main differences between Cglib dynamic proxies and JDK dynamic proxies are:

  • Objects that use JDK dynamic proxies must implement one or more interfaces
  • Cglib dynamic proxy objects do not need to implement interfaces, so that the proxy class is invasive-free.

We also need to note:

  • CGLib cannot proxy methods that are declared final because they are implemented by inheriting from their parent class, which cannot be inherited if the parent class is final.

Extension: application of dynamic proxy

As mentioned in the title, the proxy pattern is used by all open source frameworks. Where do the main open source frameworks use the proxy pattern? — Dynamic proxies, exactly?

Such as:

  • Spring AOP abstracts some non-core business processes into facets to help us deal with non-mainline processes. How does it work?
  • MyBatis, which you usually use, writes so many Mapper interfaces, why it can execute SQL without implementing classes?

The next two installments of our long-awaited series will reveal the answers to these two questions.


Do simple things repeatedly, do repetitive things seriously, do serious things creatively!

I am three points evil, a general development of literary and military skills. Like, pay attention to not get lost, let’s see you next time!



Reference:

[1]. Zen of Design Patterns

[2].Java three proxy modes: static proxy, dynamic proxy and Cglib proxy