Design Patterns (Handling)

5. Structural mode
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.
Structural patterns are divided into the following seven types:
Proxy mode
adapter pattern
decorator pattern
bridge mode
appearance mode
combination mode
Flyweight mode

5.1 Proxy mode
5.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. Static proxy proxy class is generated at compile time
into, while the dynamic proxy proxy class is dynamically generated at Java runtime. There are two types of dynamic proxy: JDK proxy and CGLib proxy

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.

5.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. The class diagram is as follows:

code show as below:

//Ticket interface
public interface SellTickets {
void sell();
}
//Railway station The railway station has the function of selling tickets, so it needs to implement the SellTickets interface
public class TrainStation implements SellTickets {
public void sell() {
System.out.println("train station ticket");
}
}
//Outlet
public class ProxyPoint implements SellTickets {
private TrainStation station = new TrainStation();
public void sell() {
System.out.println("Agents charge some service fees");
station.sell();
}
}
//test class
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).

5.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.
code show as below:

//Ticket interface
public interface SellTickets {
void sell();
}
//Railway station The railway station has the function of selling tickets, so it needs to implement the SellTickets interface
public class TrainStation implements SellTickets {
public void sell() {
System.out.println("train station ticket");
}
}
//Proxy factory, used to create proxy objects
public class ProxyFactory {
private TrainStation station = new TrainStation();
public SellTickets getProxyObject() {
//Use Proxy to get the proxy object
/*
newProxyInstance()Method parameter description:
ClassLoader loader :  Class loader, used to load proxy classes, using real object class loading
 device
Class<?>[] interfaces :  The interface implemented by the real object, the proxy mode real object and proxy
 Objects implement the same interface
InvocationHandler h :  The call handler for the proxy object
*/
SellTickets sellTickets = (SellTickets)
Proxy.newProxyInstance(station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler() {
/*
InvocationHandler invoke method parameter description:
proxy :  proxy object
method :  the Method instance corresponding to the interface method invoked on the proxy object
 example
args :  The actual parameters passed when the proxy object calls the interface method
*/
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
System.out.println("Agents charge some service fees(JDK dynamic agent
 Mode)");
//execute real object
Object result = method.invoke(station, args);
return result;
}
});
return sellTickets;
}
}
//test class
public class Client {
public static void main(String[] args) {
//Get proxy object
ProxyFactory factory = new ProxyFactory();
SellTickets proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}

principle:
Using dynamic proxy, we think about the following questions:
Is ProxyFactory a proxy class?
ProxyFactory is not the proxy class mentioned in the proxy mode, but the proxy class is a class dynamically generated in memory during the running of the program. View the structure of the proxy class through Alibaba's open source Java diagnostic tool (Arthas [Alsace]):

package com.sun.proxy;
import com.itheima.proxy.dynamic.jdk.SellTickets;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements SellTickets {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
Class.forName("java.lang.Object"));
m2 =
Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 =
Class.forName("com.itheima.proxy.dynamic.jdk.SellTickets").getMethod("s
ell", new Class[0]);
m0 =
Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException noSuchMethodException) {
throw new
NoSuchMethodError(noSuchMethodException.getMessage());
}
catch (ClassNotFoundException classNotFoundException) {
throw new
NoClassDefFoundError(classNotFoundException.getMessage());
}
}
public final boolean equals(Object object) {
try {
return (Boolean)this.h.invoke(this, m1, new Object[]
{object});
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return (Integer)this.h.invoke(this, m0, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void sell() {
try {
this.h.invoke(this, m3, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}

From the above class, we can see the following information:
Proxy class ( P r o x y 0 )Achieved S e l l T i c k e t s . This also confirms what we said before that the real class and the proxy class implement the same interface. Proxy class ( Proxy0) implements SellTickets. This also confirms what we said before that the real class and the proxy class implement the same interface. Proxy class ( Proxy0) implements SellTickets. This also confirms what we said before that the real class and the proxy class implement the same interface. The proxy class (Proxy0) passes the anonymous inner class object we provided to the parent class.
What is the execution process of dynamic proxy?
The following is the key code extracted:

//Proxy class dynamically generated during program running
public final class $Proxy0 extends Proxy implements SellTickets {
private static Method m3;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
m3 =
Class.forName("com.itheima.proxy.dynamic.jdk.SellTickets").getMethod("s
ell", new Class[0]);
}
public final void sell() {
this.h.invoke(this, m3, null);
}
}
//Dynamic proxy related classes provided by Java
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
this.h = h;
}
}
//proxy factory class
public class ProxyFactory {
private TrainStation station = new TrainStation();
public SellTickets getProxyObject() {
SellTickets sellTickets = (SellTickets)
Proxy.newProxyInstance(station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
System.out.println("Agents charge some service fees(JDK dynamic generation
 Management)");
Object result = method.invoke(station, args);
return result;
}
});
return sellTickets;
}
}
//test access class
public class Client {
public static void main(String[] args) {
//Get proxy object
ProxyFactory factory = new ProxyFactory();
SellTickets proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}

The execution process is as follows:

  1. Call the sell() method through the proxy object in the test class
  2. According to the characteristics of polymorphism, the sell() method in the proxy class ($Proxy0) is executed
  3. The sell() method in the proxy class ($Proxy0) calls the invoke method of the sub-implementation class object of the InvocationHandler interface
  4. The invoke method executes the sell() method in the class to which the real object belongs (TrainStation) through reflection

5.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 (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>
//TRAIN STATION
public class TrainStation {
public void sell() {
System.out.println("train station ticket");
}
}
/*
intercept Method parameter description:
o :  proxy object
method :  Method instance of the method in the real object
args :  actual parameters
methodProxy : The method instance of the method in the proxy object
*/
public TrainStation intercept(Object o, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("Agents charge some service fees(CGLIB Dynamic Proxy)");
TrainStation result = (TrainStation) methodProxy.invokeSuper(o,
args);
return result;
}
}
//test class
public class Client {
public static void main(String[] args) {
//Create a proxy factory object
ProxyFactory factory = new ProxyFactory();
//Get proxy object
TrainStation proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}

5.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 the JDK dynamic proxy, the JDK proxy efficiency is higher than the CGLib proxy efficiency when the number of calls is small. Only when a large number of calls are made, JDK1.6 and JDK1.7 is more efficient than CGLib proxy
A little lower, but by the time of JDK1.8, the JDK agent is more efficient than the CGLib agent. 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

5.1.7 Advantages and disadvantages
advantage:
The proxy mode acts as an intermediary 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;

Framework example:

Tags: Java Design Pattern Proxy Pattern

Posted by gardan06 on Sat, 04 Mar 2023 10:11:58 +1030