Java dynamic proxy details

  • 2020-04-01 03:03:00
  • OfStack

Agents know, you go to buy things there are a lot of agents, they are selling the original factory things. For example, you want to buy meat everyday, the pig is farmer uncle raises, but you buy meat from butcher hand, this butcher can regard as is acting. So why agent for, what's the use of agents, is, of course, I gave him an, to butcher the proxy is understandable, because you can't go to the slaughter pigs, so the agent is to buy pigs, then kill off to sell to you, of course the butcher could give meat water injection, key to see his bad not bad, so the butcher of the entire process is:

How to implement this process in code: we should use the three classes You, Butcher and Farmer to refer to You, Butcher and Farmer respectively. Among them, the farmer uncle also provides a method to buy meat to the butcher call, this method input is the amount of money, return is the amount of meat, using int, the code is as follows:


class Farmer {
    public int buyMeat(int money) {
        int meat = 0;
        // ... meat = ***;
        return meat;
    }
}

Butchers provide a method to buy meat for you to call, the same is input money, return meat, but will be processed meat (kill pigs and shave pigs in the code to save, or have to write a class for pigs), the code is as follows:


class Butcher {
    public int buyMeat(int money) {
        Farmer farmer = new Farmer();            // 1.find a farmer.
        int meat = farmer.buyMeat(money);        // 2.buy meat from the farmer.
        meat += 5;                               // 3.inject 5 pound water into the meat, so weight will increase. 
        return meat;                             // 4.return to you.
    }
}

The code for buying meat from a butcher looks like this:


class You {
    public void work() {
        int youMoney = 10;
        Butcher butcher = new Butcher();        // find a butcher.
        int meat = butcher.buyMeat(youMoney);
        System.out.println("Cook the meat, weight: " + meat);  // you cooked it. 
    }
}

This program we can optimize the, we found that farmers have a butcher the same buy meat method, we can extract an interface, call for the vendors (pedlar), after you buy meat they will take care of he is a butcher or farmer uncle, as long as he has meat is ok, after we extract a interface, the code becomes like this:


class You {
    public void work() {
        int youMoney = 10;
        Peldar peldar= new Butcher();                               // find a peldar.
        int meat = peldar.buyMeat(youMoney);
        System.out.println("Cook the meat, weight: " + meat);        // you cooked it.    
    }
}
interface Peldar {
 int buyMeat(int money);
}
class Butcher implements Peldar {
    @Override
    public int buyMeat(int money) {
        Farmer farmer = new Farmer();            // 1.find a farmer.
        int meat = farmer.buyMeat(money);        // 2.buy meat from the farmer.
        meat += 5;                               // 3.inject 5 pound water into the meat, so weight will increase. 
        return meat;                             // 4.return to you.
    }
}
class Farmer implements Peldar {
    @Override
    public int buyMeat(int money) {
        int meat = 0;
        // ... meat = ***;
        return meat;
    }
}

This is the proxy, and it's worth noting that the general proxy class and the final class implement the same interface, so the caller doesn't have to care whether the current reference is the proxy or the final class.

But this is called a static proxy, because the proxy class (the butcher class) is what you write yourself, and the dynamic proxy is what happens when Java runs and dynamically generates an equivalent proxy class. Although the class is dynamically generated, the code for killing pigs and injecting water should be written, just not a class. Where to write it? Write it to the following interface:


public interface InvocationHandler { 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; 
}

What do I mean by parameter? I'll write it like this, just so you get the idea:


public interface InvocationHandler { 
    public Object invoke(Object butcher, Method buyMeat, Object[] money) throws Throwable; 
}

The first parameter is automatically generated proxy class of an object (to automatically generate the butcher class object), the second parameter is the former is the called method of object (what method and object, see the Java reflection mechanism), we only a method, called buyMeat here, so this parameter represents the affirmation is that it, in front of the third argument is passed to the method, the parameters of the array buyMeat only one parameter, so this array will only have one element. So the code for pig killing and water injection came in like this:


InvocationHandler mInvocationHandler = new InvocationHandler() {  
    @Override
    public Object invoke(Object butcher, Method buyMeat, Object[] args) throws Throwable {
        Farmer farmer = new Farmer();              // 1.find a farmer.
        int meat = (Integer) buyMeat.invoke(farmer, args);      // 2.buy meat from the farmer.
        meat += 5;                                 // 3.inject 5 pound water into the meat, so weight will increase. 
        return meat;                               // 4.return to you.
    }
};

Invoke the farmer uncle to buy meat in this method a bit atypical, here is the reflection mechanism to invoke method, means, such as farmer Object for the receiver to call buyMeat method, to directly call the farmer method is the same, you may ask why not direct call, you may not notice that the first parameter to invoke type is Object, so you can invoke the command to the any Object (but not necessarily will be successful, success in the said), if you have a lot of farmer Object, even not farmer Object, As long as an instance of an interface (which interface, let's call interface A), you can pass it in as A parameter and then make A method call to it. Now let's look at how to generate the Proxy class. It's easy to call the Proxy's factory method as follows:


public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)  
    throws IllegalArgumentException

Interpretation parameters, first this is used for loading the proxy class (about this, this article explain), temporarily don't you know it doesn't matter, the second is an array, each number is an interface, the new generation of agent will realize all these interfaces, InvocationHandler. To invoke the second parameter method, must belong to all of these methods on the interface, the passage says that A interface must be an element of the array, the passage says that calls into failure problem is clear. The third argument, InvocationHandler, which is better understood, is that the InvocationHandler is notified whenever any method in the proxy class is called. Here's the complete code:


class You {
    public void work() {
        int youMoney = 10;

        Peldar peldarProxy = (Peldar) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Peldar.class}, mInvocationHandler);
        int meat = peldarProxy.buyMeat(youMoney);

        System.out.println("Cook the meat, weight: " + meat);    
    }

    InvocationHandler mInvocationHandler = new InvocationHandler() {        
        @Override
        public Object invoke(Object butcher, Method buyMeat, Object[] args)
                throws Throwable {
            Farmer farmer = new Farmer();                           // 1.find a farmer.
            int meat = (Integer) buyMeat.invoke(farmer, args);      // 2.buy meat from the farmer.
            meat += 5;                                              // 3.inject 5 pound water into the meat, so weight will increase. 
            return meat;                                            // 4.return to you.
        }
    };

}
interface Peldar {
    int buyMeat(int money);
}
class Farmer implements Peldar {
    @Override
    public int buyMeat(int money) {
        int meat = 0;
        // ... meat = ***;
        return meat;
    }
}

Here a proxy class is generated in the You class, and when the buyMeat of the proxy class is called, the code is the same as the previous static proxy.


Related articles: