The proxy pattern is a design pattern that controls access to real objects by generating a placeholder in place of real objects. In real life, agents are easy to understand. Suppose a scenario like this: your company is a clothing company, the customer to customize clothes, not directly to the company’s tailor to talk, but to talk to the business, then the customer will think that the business represents the company.

So this is an indirect process, so what is the point of commerce (proxy object)? The business can carry out some additional logic such as negotiation, price, etc. That is to say, the role of the agent is to add corresponding logic before or after the client visits, or decide whether to access the client according to other conditions. In this scenario, it is obvious that the business controls the client’s access to the tailor.

Our code examples will use this as a scenario to walk through each proxy pattern.

Before explaining, we first define the clothes entity class, clothes making interface, tailor class (implement clothes making interface) these basic classes:

Clothing entity category:

package com.example.javaproxy.model; import lombok.Getter; import lombok.Setter; /** * clothingclass */ @get@setter public class clothingclass {** * clothingclass */ private String name; /** * size (S,M,L,XL,XXL) */ private String size; }Copy the code

Clothes making interface:

package com.example.javaproxy.service; import com.example.javaproxy.model.Clothing; / * * * clothing production interface * / public interface ClothesMaking {/ clothes make * * * * @ param name * @ param name clothes size clothes size * @ * / return clothes instance Clothing makeClothing(String name, String size); }Copy the code

Tailor class (implements the clothing interface and is the delegate object) :

package com.example.javaproxy.service.impl; import com.example.javaproxy.model.Clothing; import com.example.javaproxy.service.ClothesMaking; /** * public class Tailor implements ClothesMaking {@override public Clothing makeClothing(String name, String size) { Clothing clothing = new Clothing(); clothing.setName(name); clothing.setSize(size); return clothing; }}Copy the code

1. Static proxy

The static proxy is actually quite simple, equivalent to creating the proxy human directly and indirectly accessing the tailor class.

Let’s go straight to the code, the agent (class) :

package com.example.javaproxy.proxy.staticproxy; import com.example.javaproxy.model.Clothing; import com.example.javaproxy.service.ClothesMaking; import com.example.javaproxy.service.impl.Tailor; /** * tailor agent (agent class, */ Public class TailorStaticAgent implements ClothesMaking {/** * private ClothesMaking Tailor */ = new Tailor(); /** * Override public Clothing makeClothing(String Name, String size) {system.out.println ("[static proxy logic]"); System.out.println(" Tailor's agent received demand!" ); return tailor.makeClothing(name, size); }}Copy the code

Then test in the main method:

ClothesMaking agent = new TailorStaticAgent(); Clothing customClothing = agent. MakeClothing (" shirt ", "XL"); Println (" getName: "+ CustomClothing.getName () +"; "+ CustomClothing.getSize ());Copy the code

Results:

Static proxy is very easy to understand, is to create a new proxy class, the use of this proxy class (tailor’s agent) to access the delegate class (tailor), the customer only need to find an agent can indirectly access the tailor.

In static proxy, the proxy class and the delegate class need to implement the same interface, and static proxy is a compiled and then proxy mode, which is not conducive to expansion.

2. Dynamic proxy

As you can see from the above examples, the static proxy’s proxy classes are also compiled into class files, while the dynamic proxy dynamically generates proxy objects at run time, generates class bytecodes at run time, and loads them into the JVM.

(1) JDK dynamic proxy

Use JDK API to create dynamic proxy objects in memory, we do not need to create the same static proxy class as above, just need to create a proxy logic class, this logic class needs to use the InvocationHandler interface, write the proxy method logic in the logic class, The proxy logic class can then generate dynamic proxy objects.

Here is the code for the agent logic class:

package com.example.javaproxy.proxy.jdkproxy; import com.example.javaproxy.service.ClothesMaking; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * public class JDKProxyBind implements InvocationHandler {/** * private ClothesMaking tailor; Public Object bind(ClothesMaking Origin) {this.tailor = /** @param Origin delegate * @return proxy Object */ Public Object bind(ClothesMaking Origin) {this.tailor = origin; return Proxy.newProxyInstance(tailor.getClass().getClassLoader(), tailor.getClass().getInterfaces(), this); } /** * proxy logical method ** @param proxy Proxy object * @param method Current scheduling method * @param args Current method parameter * @return proxy result * @throws Throwable exception */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {system.out.println ("[enter JDK dynamic proxy logic]"); System.out.println(" Tailor's agent received demand!" ); Object Result = method.invoke(Tailor, args); System.out.println(" Custom clothes complete!" ); return result; }}Copy the code

Then go to the main method test:

JDKProxyBind JDKProxyBind = new JDKProxyBind(); ClothesMaking Tailor = new Tailor(); ClothesMaking agent = (ClothesMaking) jdkProxyBind.bind(tailor); Clothing customClothing = agent.makeclothing (" shirt ", "XL"); Println (" getName: "+ CustomClothing.getName () +"; "+ CustomClothing.getSize ());Copy the code

Results:

You can see that JDK dynamic proxy does not manually create the proxy class, just write the proxy logic part of the agent. There are two major steps:

  1. Establish a relationship between proxy objects and delegate objects: In the above we establishedProxy logic class, which definesbindMethod, in which the class attributes are used firsttailorThe delegate object is saved and passedProxyOf the classnewProxyInstanceMethod to create a proxy object. This method takes three arguments:
    • The first is the classloader, which uses a delegate class loader
    • The second is the interface to mount the generated dynamic proxy object, which is obviously the interface of the delegate class
    • The third is to define the proxy class that implements the method logic,thisRepresents the current object.The proxy logic class must be implementedInvocationHandlerOf the interfaceinvokeMethod, which is an implementation of the proxy logic method.We’ll put the proxy logic in this class
  2. Implementing proxy methods: in the proxy logic classinvokeMethod to implement proxy logic, the three parameters of this method are as follows:
    • Proxy An object generated by the bind method
    • Method Current scheduling method
    • Parameters of the ARGS scheduling method

Once we have used the proxy method to schedule the method, we will enter the Invoke method.

Implementation of proxy logic class, when used in the main method, it can be seen that only need to use proxy logic class tailor (delegate class) binding relationship, you can directly generate proxy objects, the implementation of proxy logic.

(2) CGLib dynamic proxy

We found that both static and JDK dynamic proxies need to provide interfaces. In environments where interfaces are not available, we can use the CGLib proxy. It only needs to provide a delegate class to dynamically generate the proxy object to implement the proxy.

First we need to introduce CGLib dependencies in Maven:

<! --> <dependency> <groupId> CGLib </groupId> <artifactId> CGLib </artifactId> </dependency>Copy the code

Then create a proxy logic class, which is similar to JDK dynamic proxies.

Again, paste the code for the agent logic class:

package com.example.javaproxy.proxy.cglibproxy; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * public class CGLibProxyBind implements MethodInterceptor {/** * implements MethodInterceptor ** @param CLS Public Object getProxy(Class CLS) {// CGLib Enhancer = new Enhancer(); // Set the delegate class as the parent class enhancer.setSuperclass(CLS); // Set the proxy logic class to this class, enhancer.setcallback (this); // Generate and return the proxy object return enhancer.create(); } /** * proxy logic method ** @param proxy proxy object * @param method method * @param args method parameter * @param methodProxy method proxy * @return proxy logic returns * @override public Object Intercept (Object proxy, Method Method, Object[] args, MethodProxy MethodProxy) throws Throwable {system.out.println ("[enter CGLib dynamic proxy logic]"); System.out.println(" Tailor's agent received demand!" ); // Call tailor (delegate) method Object result = methodProxy.invokeSuper(proxy, args); System.out.println(" Custom clothes complete!" ); return result; }}Copy the code

Then test in the main method:

Bind CGLibProxyBind = new CGLibProxyBind(); Tailor Agent = (Tailor) cgLibProxyBind. GetProxy (Tailor. Class); Tailor Clothing customClothing = Agent.makeclothing (" shirt ", "XL"); Println (" getName: "+ CustomClothing.getName () +"; "+ CustomClothing.getSize ());Copy the code

Results:

CGLib’s Enhancer is used to generate dynamic proxy objects by setting the superclass as a delegate class and this class as a proxy logical class (the proxy logical class needs to implement the MethodInterceptor interface).

CGLib dynamic proxies are very similar to JDK dynamic proxies.

But need a reminder, here use JDK9 and above and perform additional dynamic proxy throws an exception. Java lang. ExceptionInInitializerError, because of the high in JDK version, – illegal – access options default to deny, This causes deep reflection to fail.

You can use the JDK8 version to implement the CGLib agent.

To use JDK9 and above, change the value of the running JVM parameter –illegal-access to WARN (although this will still pop up warnings).

Command line run with arguments:

java --illegal-access=warn -jar xxx.jar
Copy the code

With IDEA, run parameters are set in run configuration:

The final application, can determine!

3,

The proxy pattern is an indirect access pattern that is actually used in many places.

Static proxies are simple, but require manual definition of proxy classes, which is not conducive to business expansion.

Dynamic proxy only needs to implement the proxy method logic, during the running process will dynamically generate proxy objects, commonly used is JDK dynamic proxy and CGLib dynamic proxy.

The biggest difference between JDK dynamic proxies and CGLib dynamic proxies is that the former relies on interfaces while the latter does not. The essence of JDK dynamic proxies is through reflection proxy methods, whereas CGLib implements proxies by generating bytecode like.

Sample repository address