WeChat official account : A good loser . If there is a problem , Please leave a message in the background , I won't listen anyway .

I'm reviewing recently Java relevant , This paper reviews the next agent model . The agent mode is Java There are applications in many areas , It is divided into static agent and dynamic agent , among Spring AOP This is a typical example of dynamic proxy . Dynamic agents are divided into interface agents and cglib ( Subclass proxy ), Combined with my understanding, I wrote a few demo Share with you , This is Xiuxian last night 3 Click on the article you wrote , I'm not looking at it , I don't think it makes sense .

Agent mode is very common in our daily life , Life is full of agents :

  • It's hard to get tickets to see Jacky Cheung's concert , You can line up with cattle to buy
  • It's troublesome to eat out , You can order takeout

Whether it's cattle 、 Take away riders have to help us . But they can't do it all at once ( For example, cattle can't help me eat ), They can only do what we can't or don't want to do .

  • Looking for scalpers can help me queue up to buy tickets for Jacky Cheung's concert
  • The takeaway rider can help me take the meal downstairs

therefore , You see . Proxy mode is actually something that the current object is not willing to do , Entrust to someone else .

Static proxy

I still use the example of looking for scalpers to help me queue up to buy tickets for Jacky Cheung's concert , Write a demo explain . Now there is a Human Interface , Both I and cattle have implemented this interface .

public interface Human {

    void eat();

    void sleep();

    void lookConcert();

}

for example , I like this class , I eat and sleep , Such as the following categories :

public class Me implements Human{

    @Override
    public void eat() {
        System.out.println("eat emat ....");
    }

    @Override
    public void sleep() {
        System.out.println("Go to bed at one o'clock in the morning");
    }

    @Override
    public void lookConcert() {
        System.out.println("Listen to Jacky Cheung's Concert");
    }

}

There are cattle , for example :

public class Me implements Human{

    @Override
    public void eat() {
    }

    @Override
    public void sleep() {
    }

    @Override
    public void lookConcert() {
    }

}

Now the yellow cattle and I are ready , How to connect the two ? What we want to make clear is that scalpers want to buy tickets for me , I have to wait in line to buy a ticket , So there are the following cattle : Notice here that we don't care about , Other behaviors of cattle , We only care if he can line up to buy tickets .

public class HuangNiu implements Human{

    private Me me;

    public HuangNiu() {
        me = new Me();
    }

    @Override
    public void eat() {
    }

    @Override
    public void sleep() {
    }

    @Override
    public void lookConcert() {
        //  Add queuing method 
        this.lineUp();
        me.lookConcert();
    }

    public void lineUp() {

        System.out.println("line up");

    }

}

The final main The method is called as follows :

public class Client {

    public static void main(String[] args) {

        Human human = new HuangNiu();
        human.lookConcert();

    }

}

give the result as follows :

 Static proxy results

thus it can be seen , Cattle just do what we don't want to do ( line up for tickets ), Actually, I'm still the one watching the concert . The client doesn't care which class the proxy class represents , Because the code controls the client's access to the delegate class . The client code is shown as Human human = new HuangNiu();

Because the proxy class implements the interface of the abstract role , The proxy class cannot be generalized . such as , My dog is sick , I want to see a doctor , But it's troublesome to queue up for registration , I also want to have a scalper to help me get in line to see a doctor , But it doesn't understand the characteristics of this dog ( Cattle and dogs are not the same type , Cattle belong to Human But dogs belong to Animal class ) But queuing up for registration and queuing up to buy tickets are two things compared to scalpers , This method is unchangeable , Line up at the scene . Can we find an agent who can help people queue up to buy tickets or dogs to register ?

The answer must be yes , You can use dynamic proxies .

Dynamic agent based on Interface

As the content of the static proxy describes , Static proxies are limited by the implementation of the interface . Dynamic proxy is through the use of reflection , Get the type of abstract interface dynamically , So as to obtain the relevant features for proxy . Because the dynamic agent can act as an agent for all principals , So give the proxy class a generic name HuangNiuHandle. Let's see what cattle can become ?

public class HuangNiuHandle implements InvocationHandler {

    private Object proxyTarget;

    public Object getProxyInstance(Object target) {
        this.proxyTarget = target;
        return Proxy.newProxyInstance(proxyTarget.getClass().getClassLoader(), proxyTarget.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Object methodObject = null;

        System.out.println("line up");
        methodObject = method.invoke(proxyTarget, args);
        System.out.println("go home and sleep");

        return methodObject;
    }

}

At this time, the client code becomes like this

public class Client {

    public static void main(String[] args) {

        HuangNiuHandle huangNiuHandle = new HuangNiuHandle();
        Human human = (Human) huangNiuHandle.getProxyInstance(new Me());

        human.eat();
        human.run();
        human.lookConcert();

        System.out.println("------------------");

        Animal animal = (Animal) huangNiuHandle.getProxyInstance(new Dog());
        animal.eat();
        animal.run();
        animal.seeADoctor();
    }

}

There are three main points to using dynamic proxies ,

  1. Must be realized InvocationHandler Interface , Indicates that the class is a dynamic proxy execution class .

  2. InvocationHandler There is an implementation method in the interface as follows : public Object invoke(Object proxy, Method method, Object[] args) . You need to override this method when you use it

  3. Get proxy class , Need to use Proxy.newProxyInstance(Clas loader, Class<?>[] interfaces, InvocationHandler h) This way to get Proxy object (Proxy Instances of class types ).

be aware Proxy.newProxyInstance This method , It needs to pass in 3 Parameters . The analysis is as follows :

//  The first parameter , Is the loader of the class 
//  The second parameter is the interface type of the delegate class , The certificate proxy class returns the type under the same implementation interface , Keep the behavior of the proxy class consistent with that of the abstract role 
//  The third parameter is the proxy class itself , Tell the proxy class , When a proxy class encounters a method of a delegate class, which class should it call invoke Method 
Proxy.newProxyInstance(Class loader, Class<?>[] interfaces, InvocationHandler h)

Look again. invoke Method , What method does the user call the proxy object , It's essentially a call to the processor
invoke Method , Call the target method through this method , It also has three parameters :

//  The first parameter is zero  Proxy  Class type instances , Like anonymous  $proxy  example 
//  The second parameter is the method object of the delegate class 
//  The third parameter is the method parameter of the delegate class 
//  Returns the execution result of a method of a delegate class 
public Object invoke(Object proxy, Method method, Object[] args)

The output after calling the proxy class :

 A dynamic proxy

From the results , Cattle not only help ( agent ) I'm in line to buy tickets , And helped ( agent ) My dog is in line to register . therefore , You see, static agents need to write their own agent classes ( The proxy class needs to implement the same interface as the target object ), We also need to implement interface methods one by one , But dynamic agents don't need .

Be careful , We don't need the agent of cattle to queue up in all ways . We know that only when I go to the concert and my dog goes to the doctor , That's what we need , If you want to implement the method we want, add a specific proxy to it , Can pass invoke Method reflection in method method Object method name , So dynamic proxy classes can be like this :

public class HuangNiuHandle implements InvocationHandler {

    private Object proxyTarget;

    public Object getProxyInstance(Object target) {
        this.proxyTarget = target;
        return Proxy.newProxyInstance(proxyTarget.getClass().getClassLoader(), proxyTarget.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Object methodObject = null;

        if ("lookConcert".equals(method.getName()) ||
        "seeADoctor".equals(method.getName())) {

            System.out.println("line up");
            //  Calling the target method 
            methodObject = method.invoke(proxyTarget, args);
        } else {
            //  Don't use the first proxy Parameters as parameters , Otherwise, it will cause a dead cycle 
            methodObject = method.invoke(proxyTarget, args);
        }

        return methodObject;
    }

}

give the result as follows : We can see that we only turn to scalpers in specific ways

 A dynamic proxy

thus it can be seen , Dynamic agent is generally used in horizontal business such as logging .

It is worth noting that :

  1. Dynamic proxy pattern based on interface class , You have to have abstract characters 、 Delegation class 、 There are three basic roles of agency . Delegate and proxy classes must be derived from abstract roles , Otherwise, the mode cannot be used .

  2. The dynamic proxy pattern finally returns an abstract role ( Top level interface ) The object of . In the delegate class is private perhaps protected Key decorated methods will not be called , Even if it's allowed to call . You can't convert a proxy class to a subclass interface on the client side , Call the method . That is to say, the above dynamic proxy returns the delegate class (Me) or (Dog) It's the interface object (Human) or (Animal).

  3. stay invoke Why don't you use the first parameter in the method to execute the callback . Use on client getProxyInstance(new Child( )) when ,JDK Will return a proxy Example , In the example, there are InvokecationHandler Objects and dynamically inherited targets . The client called the target method , There are the following operations : First JDK To find the first proxy Instance handler object And then execute handler Internal invoke Method .

according to public Object invoke The first parameter of this method proxy It's corresponding to proxy example . If in invoke Internal use method.invoke(proxy,args) , There will be a chain of methods , The target method →invoke→ The target method →invoke..., Eventually the stack overflows .

Dynamic agent based on subclass

In order to save trouble , I don't inherit the parent class here , But in the actual development, it is more convenient to extend by inheriting the parent class . Unlike interface based implementation classes :

  1. CGLib ( Dynamic agent based on subclass ) Using the method interceptor MethodInterceptor , Import required cglib.jar and asm.jar package
  2. Dynamic agent based on subclass , Returns a subclass object
  3. Method interceptor to protected Modified methods can be called

The code is as follows :

public class Me {

    public void eat() {
        System.out.println("eat meat ....");
    }

    public void run() {
        System.out.println("I run with two legs");
    }

    public void lookConcert() {
        System.out.println("Listen to Jacky Cheung's Concert");
    }

    protected void sleep() {
        System.out.println("Go to bed at one o'clock in the morning");
    }

}

Dog class

public class Dog {

    public void eat() {
        System.out.println("eat Dog food ....");
    }

    public void run() {
        System.out.println("Dog running with four legs");
    }

    public void seeADoctor() {
        System.out.println("The dog go to the hospital");
    }

}

Cattle agents , Be careful invoke() Here's an extra parameter methodProxy , Its purpose is to carry out the goal ( Delegation class ) Methods , As for why methodProxy , The official explanation is that it's fast and intercep t There is no need to save the delegate object reference when calling delegate class methods inside .

public class HuangNiuHandle implements MethodInterceptor {

    private Object proxyTarget;

    public Object getProxyInstance(Object target) {
        this.proxyTarget = target;
        return Enhancer.create(target.getClass(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

        Object methodObject = null;

        if ("lookConcert".equals(method.getName()) ||
                "seeADoctor".equals(method.getName())) {
            System.out.println("line up");
            //  Calling the target method 
            methodObject = methodProxy.invokeSuper(proxy, args);
        } else {
            methodObject = method.invoke(proxyTarget, args);
        }

        return methodObject;
    }
}

client class

public class Client {

    public static void main(String[] args) {
        HuangNiuHandle huangNiuHandle = new HuangNiuHandle();
        Me me = (Me) huangNiuHandle.getProxyInstance(new Me());

        me.eat();
        me.run();
        me.sleep();
        me.lookConcert();

        System.out.println("------------------");

        Dog dog = (Dog) huangNiuHandle.getProxyInstance(new Dog());
        dog.eat();
        dog.run();
        dog.seeADoctor();
    }
}

result :

 Dynamic agent based on subclass

be aware Me Class protected The method of decoration sleep It can still be called by the client . This is not allowed in interface based dynamic proxies .

The difference between static agent and dynamic agent

Static agents need to write their own proxy classes and implement the target methods one by one , And the proxy class must implement the same interface as the target object .

Dynamic proxies don't need to implement proxy classes themselves , It is used. JDKAPI, Dynamically build proxy objects in memory ( We need to pass in the proxy class ), And all target methods are implemented by default .

Source download :https://github.com/turoDog/review_java.git

Last

If you see this , Like this article , Please forward 、 give the thumbs-up . WeChat search 「 A good loser 」, Welcome to your attention .

reply 「1024」 Send you a complete set java、python、c++、go、 front end 、linux、 Algorithm 、 big data 、 Artificial intelligence 、 Small program and English Course .

reply 「 e-book 」 Send you 50+ Ben java e-book .

 programing language