0 x0, introduction

I read The Beauty of Design Patterns in my spare time for daily updates. This paper corresponds to design patterns and paradigms: structural (48) and Proxy Pattern.

Follow the creative design patterns we learned earlier:

To solve object creation problems (encapsulate complex creation processes, decouple object creation code from usage code)

Different structural design patterns:

Summarizes some classical structures for classes or objects grouped together (to solve problems in specific application scenarios)

This section to explore the agent mode, know what the mode is, application scenarios, static proxy and dynamic proxy about ~

Secondary knowledge processing inevitably has mistakes, interested in the time can refer to the original, thank you.


0x1. Definitions and Application Scenarios

The concept of agent is easy to understand. Here’s a visual example:

In the past, Android official website could not be accessed in China, and developers needed scientific Internet access to access, using the intermediate bridge is the proxy server.

Through the middleman → proxy server, we went from direct access to the Android official to indirect access.

The proxy pattern in development: without changing the original class (proxied class), by introducing the proxy class to add functionality to the original class.

There are three roles:

  • Abstract topic classes: declare common methods and implement them for the next two roles, usually in the form of interfaces.
  • Implement topic class: implement abstract topic class method, the final operation object;
  • Proxy classes: Implement abstract topic classes, containing operations on implementing topic class objects;

The static proxy mode can be used as a proxy mode. The static proxy mode can be used as a proxy mode.

  • Protection agent → control the access of the target object, to provide different access permissions for different users;
  • Intelligent reference broker → attach additional operations when accessing objects, business systems develop non-functional requirements (monitoring, statistics, logging, etc.)
  • Virtual proxy → delayed initialization, create small objects proxy large objects (containing a large number of IO resources) creation, large objects are really needed to create;
  • Remote proxy → scenarios requiring local execution of remote service code, middleware fields, such as remote proxy framework gRpc, Dubbo, etc.;
  • Cache proxy → cache client request results and manage the cache life cycle;

After the scenario, let’s talk about the difference between static and dynamic proxies:

  • Static proxy → The bytecode of the proxy class is compiled before running;
  • Dynamic proxy → Bytecodes for proxy classes are created automatically at run time by programs in the VIRTUAL machine;

0x2. Static Proxy Example

The proxy mode is usually implemented using static proxies. Here is a simple daigou example:

// Abstract theme
public interface ShoppingOverseas {
    void shopping(String thing);
}

// Topic implementation class
public class OverseasShop implements ShoppingOverseas {
    @Override
    public void shopping(String thing) {
        System.out.println("Overseas merchant sells one:"+ thing); }}/ / the proxy class
public class ShoppingProxy implements ShoppingOverseas {
    OverseasShop shop = new OverseasShop();

    @Override
    public void shopping(String thing) {
        System.out.println("Daigou received your order information");
        shop.shopping(thing);
        System.out.println("Daigou help you buy from overseas merchants:"+ thing); }}// Test case
public class ProxyTest {
    public static void main(String[] args) {
        ShoppingProxy proxy = new ShoppingProxy();
        proxy.shopping("Shoes"); }}Copy the code

The following output is displayed:

This is a well-behaved proxy pattern: both the original class and the proxy class implement the same interface. In practice, it is not that good in development. Most of the code is not developed and maintained by us (such as a third party library), so it is not possible to modify the original class and redefine an interface for it.

If you really want to use proxy mode, you can override the original class method or add new methods by inheriting the original class overriding method.

There is also a problem with static proxies: the Double class explodes when there are too many original classes to add additional functionality, and a lot of “duplicate” code is written, adding unnecessary development costs. This situation can be solved by Dynamic Proxy:

At run time, the corresponding proxy class is created dynamically, and the original class is replaced with the proxy class in the system.


0x2 dynamic Proxy in Java

In Java, there are two common dynamic proxy implementations:

  • JDK dynamic proxy → the underlying dependency reflection mechanism, the proxy class to implement the interface method, through invokeHandler to enhance the required method;
  • CGLIB agent → using ASM framework, modify bytecode generation subclass to process;

â‘  JDK dynamic proxy example

The gameplay is as follows:

  • â‘  Define proxy interface;
  • â‘¡ Real objects implement this proxy interface;
  • (3) Define a dynamic proxy class, implement the InvocationHandler interface, rewrite invoke() method, do small actions;
  • â‘£ Invoke the Proxy class created by Proxy class;

A code example is as follows:

// Proxy interface
public interface Shopping {
    void shopping(String thing);
    void pay(a);
}

// Real objects that implement the proxy interface (2 are defined here)
public class FirstShoppingImpl implements Shopping {
    @Override
    public void shopping(String thing) { System.out.println("Buy at Merchant Number one:" + thing); }

    @Override
    public void pay(a) { System.out.println("Pay at merchant Number one"); }}public class SecondShoppingImpl implements Shopping {
    @Override
    public void shopping(String thing) { System.out.println("Purchase at Merchant No. 2:" + thing); }

    @Override
    public void pay(a) { System.out.println("Pay at merchant Number two."); }}// Dynamic proxy class
public class DynamicShoppingProxy implements InvocationHandler {
    private Shopping object;  // Delegate class object

    public DynamicShoppingProxy(Shopping object) {
        this.object = object;
    }

    / * * *@paramProxy Proxy object *@paramMethod Method of the proxied object *@paramThe argument to the args method * */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("shopping")) {
            System.out.println("Start shopping...");
            Reflection calls the actual method in the class and returns the method's return value, or null if there is no return value
            method.invoke(object, args);    
            System.out.println("Finished shopping...");
        } else if (method.getName().equals("pay")) {
            System.out.println("Start payment...");
            method.invoke(object, args);
            System.out.println("Close payment...");
        }
        return null; }}// Test case: Invoke the Proxy class created by the Proxy class
public class DynamicProxyTest {
    public static void main(String[] args) {
        // The generated dynamic proxy file is saved in the current project's com/sun/proxy directory
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles"."true");

        // Create a dynamic proxy instance
        DynamicShoppingProxy proxy1 = new DynamicShoppingProxy(new FirstShoppingImpl());
        DynamicShoppingProxy proxy2 = new DynamicShoppingProxy(new SecondShoppingImpl());

        // newProxyInstance() dynamically generates the proxy class, taking the class loader (to be proxy-only), the interface implemented by the proxy class, and the dynamic proxy instance
        Shopping s1 = (Shopping) (Proxy.newProxyInstance(Shopping.class.getClassLoader(),
                new Class[]{Shopping.class}, proxy1));
        System.out.println(s1.getClass().getName());
        s1.shopping("Shoes");

        Shopping s2 = (Shopping) (Proxy.newProxyInstance(Shopping.class.getClassLoader(),
                new Class[]{Shopping.class}, proxy2));
        System.out.println(s2.getClass().getName());
        s2.shopping("Clothes"); s2.pay(); }}Copy the code

The running results are as follows:

Then you can open the $Proxy0 dynamic proxy class:

The static code block gets the proxy method and then uses reflection to call the methods of the dynamic proxy class. It looks simple, but let’s see how it works.

The implementation principle of JDK dynamic proxy

Follow the Proxy newProxyInstance() method:

Follow getProxyClass0() to find or generate methods for the proxy class:

Get () → weakCache.get () → proxyClassFactory.apply ()

The method of positioning to generate bytecode: ProxyGenerator. GenerateProxyClass () :

Locate generateClassFile() in three steps:

  1. Generate proxy scheduling code for all methods, which aggregates proxy method objects

2) Generate field information and method information for methods in the class

3) Generate the final class file

$Proxy0 = $Proxy0;


Example of CGLIB Dynamic Proxy

JDK dynamic proxies need to define an interface that implements the methods in the interface. If the implementation class does not implement the interface, it cannot do so.

The CGLIB dynamic proxy uses bytecode enhancement techniques to generate subclasses of a compiled class bytecode file, taking advantage of polymorphism, calling methods in the parent class, and actually calling corresponding methods in the subclass. Proxied classes or methods should not be decorated with the final keyword because they need inheritance.

Jar → CGLIB. Jar → CGLIB. Jar → CGLIB. Jar → CGLIB.

A code example is as follows:

// Proxy class
public class Store {
    public void sell(String thing) {
        System.out.println("The merchant sells:" + thing);
    }

    public void close(a) {
        System.out.println("Go out of business."); }}// methods enhance classes
public class StoreTrade implements MethodInterceptor {

    / * * *@paramO Objects to be enhanced *@paramMethod Specifies the method to intercept@paramParameter * in the objects method@paramMethodProxy handling of methods */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable{
        System.out.println("Before Method === " + method.getName());
        System.out.println("Method argument passed in:" + Arrays.toString(objects));
        methodProxy.invokeSuper(o, objects);
        System.out.println("After Method === " + method.getName());
        return null; }}// Test case
public class StoreTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer(); // Enhance class objects
        enhancer.setSuperclass(Store.class);   // Set the class to be enhanced
        StoreTrade store = new StoreTrade();
        enhancer.setCallback(store);    // Set the method to be enhanced
        Store s = (Store) enhancer.create();   // Generate the enhanced subclass encodings
        s.sell("Milk"); s.close(); }}Copy the code

Example run result:

Will use almost, the source code is not to see, must be a big bone…


  • JDK Dynamic proxy implementation principle (JDK8)

  • Java dynamic proxy, this article is enough