“This is my first day of the Novembermore Challenge.The final text challenge in 2021”

In this part, I will briefly learn the agent mode of Retrofit

  1. With regard to the proxy pattern, simply provide a proxy for other objects to control access to that object
  2. From literal meaning can be understood as so, the so-called proxy agent, is instead of others to do what we want to do, for example, we need overseas shopping at home for a variety of reasons, and this time will need to find some friends or act as purchasing agency, the agent is the process of how to buy things we are not concerned, Only care about the result of what you receive.

There are two types of proxy mode, static proxy or dynamic proxy. Let’s look at them in detail

Static agent

Introduction to Static Proxy

First look at the following class diagram. A static proxy needs three roles: AbstractObject, ProxyObject and RealObject.

  • For the abstract object role, declare the common interface of the target class and the ProxyObject, define a method that is the common inheritance of the ProxyObject and the target object, so that the target object can be used anywhere, it is defined by using the ProxyObject, which reflects the significance of proxy
  • For the target object role, the target object represented by the proxy object is defined
  • For the role of proxyObject, the key role of the whole static proxy mode is connected in series. In this proxyObject, a reference of realObject is held inside, and realObject can be operated at any time. Since proxyObject and realObject have a unified interface, it is convenient to replace the target object at any time

Static proxy implementation

Let’s take a quick look at the code implementation of the static proxy pattern

  1. The first thing we need to do is define an abstract object role that AdstractObject represents
abstract class AbstractObject {

    abstract fun operation(a)
}
Copy the code
  1. Define the target implementation class RealObject
class RealObject : AbstractObject() {
    override fun operation(a) {
        println("do operation....")}}Copy the code

Abstract methods are implemented by inheriting abstract classes

  1. The third step is to define our proxy class, ProxyObject, which also inherits the abstract class and implements the abstract method
class ProxyObject(private var realObject: RealObject) : AbstractObject() {


    override fun operation(a) {
        println("do something before real operation...")
        realObject = RealObject()
        realObject.operation()
        println("do something after real")}}Copy the code

Here, unlike the target implementation class, this abstract method can do some additional logic, which is one of the purposes of static proxies

From this example, you can see that the proxy object delegates our client calls to the target object, and we can add our own judgments before and after calling the target object

A dynamic proxy

Dynamic proxy mode

Let’s take a look at the dynamic proxy pattern used in Retrofit, which has the following salient features:

  • Non-intrusive extension of code
  • In layman’s terms, you can do whatever you want before and after a method is executed by enhancing some method or function without modifying the original code

So what is a dynamic proxy?

To put it simply, the proxy is created when the program is running. Compared with static proxy, it has a great advantage. It is very convenient to handle the unified processing of our proxy class functions, instead of each function for separate processing

Two dynamic proxy writing methods

  • JDK dynamic proxy
  1. This writing method requires its own client-side write assistant interface to operate

  2. Is implemented by Java’s internal reflection mechanism, which is more efficient when generating classes

  • CGLIB
  1. Directly modify bytecode to do

Dynamic proxy implementation

JDK dynamic proxies (JDK dynamic proxies can only create proxies for interfaces)

  1. The first step is to create a Subject interface that defines a shopping method
interface Subject {
    fun shopping(a)
}
Copy the code
  1. The second step implements a proxied class that implements the Subject interface method
class Man : Subject {
    override fun shopping(a) {
        println("Jacky went shopping.")}}Copy the code
  1. The third step is to implement the Proxy Proxy class and implement an InvocationHandler interface
class Proxy(private var target: Any) : InvocationHandler {


    override fun invoke(proxy: Any? , method:Method? , args:Array<out Any>?: Any? {
        println("proxy:${proxy? .javaClass? .name}")
        println("before...") method? .invoke(target,args) println("after...")
        return null}}Copy the code
  • Each dynamic proxy needs to implement the Invoke method of the InvocationHadnler interface
  • The three parameters of the invoke method are represented as: proxy refers to the real object of the proxy, method refers to a method of the real object, and args refers to a parameter of a method of the real object

About InvocationHandler

Why use these three parameters? Let’s reserve the question for now and explore further. Here’s a brief summary of InvocationHandler. After checking and learning, I found that the concept of interception is designed in it

  • The object of each proxy class is associated with an implementation of the InvocationHandler interface that represents the internal processing logic. When a consumer calls a method of the interface that the proxy object represents, this call information is passed to the Invoke method

  • Parameters (proxy object, method object corresponding to the method, and parameters in the method) can be retrieved from the parameters of the Invoke method

  • The return value of the Invoke method is returned to the user, indicating that the method call was intercepted, and all procedures are transparent to the user, as is often the case in Retrofit

  1. The fourth step is to implement the Client class
object Client {
    @JvmStatic
    fun main(args: Array<String>) {

        val man = Man()
        val p = Proxy(man)

        / / by Java. Lang. Reflect. NewProxyInstance () method to obtain the real object agent
        val subject:Subject = java.lang.reflect.Proxy.newProxyInstance(
            man.javaClass.classLoader,man.javaClass.interfaces,p) as Subject
        // Invoke real object interface implementation methods via proxy objects
        subject.shopping()
        // Get the name of the Class object corresponding to the proxy object of the real object, expressed as a string
        println(subject.javaClass.name)
    }
}
Copy the code
  • Here call proxy. newProxyInstance method, is used to create the Proxy object we need, can look at its source
 public static Object newProxyInstance(ClassLoader loader, Class
       [] interfaces, InvocationHandler h)
        throws IllegalArgumentException
    { Objects.requireNonNull(h); · · · · · ·Copy the code

The next three parameters are ClassLoader, Class<? >[],InvocationHandler

  • ClassLoader: a ClassLoader that loads the bytecode files of our dynamically generated proxy classes into the JVM
  • Class
    []: interface to an array of classes, providing an interface for proxy objects
  • The most important argument, InvocationHandler, is to associate the object with calling its Invoke method

Back to the Client class, let’s take a look at the dynamic proxy process

  1. First call the proxy.newProxyInstance method to get the Proxy object for the real object
  2. Whenever the proxy object executes a method, the invoke method in the handler associated with the proxy object is invoked to perform the corresponding operation

In fact, this is the full JDK dynamic proxy process

Dynamic proxy summary

  1. Runtime (dynamic association is implemented at run time when the dynamic proxy is not directly related to the interface through the proxy class)
  2. The InvocationHandler interface and the Proxy class in reflection packages
  3. The biggest difference between dynamic proxy and static proxy is that the proxy classes for dynamic proxy do not need to be generated manually, but are generated dynamically at runtime

conclusion

  • That’s a brief analysis of the proxy patterns commonly used in Retrofit (dynamic versus static). Retrofit doesn’t have a lot of code for simplicity, but it does have a lot of design patterns that need to be understood
  • Next, I studied Retrofit’s source code analysis, analyzed its overall process and learned its core source code, paving the way for a long time. Welcome to study with me