There are feelings, there are dry goods, wechat search [three prince Ao bing] pay attention to the programmer who has a little bit of things.

This article has been included in GitHub github.com/JavaFamily, there are a line of large factory interview complete test sites, information and my series of articles.

Design Patterns have been shared with you many common patterns, interested partners can review, consolidate understanding.

This time, I’m going to share with you the agent model, one of the three main types of design patterns. The agent model is not often used in business scenarios, but interviewers often ask a question.

Can you tell me about the AOP proxy pattern in Spring? What is the difference between the JDK proxy mode and cglib proxy mode?

Clear and unclear students can continue to look down, there will be a harvest.

Without further ado, let’s begin a step by step analysis of the proxy pattern.

Definition and Purpose

First, there are several types of proxy patterns

  • Remote proxy: Delegate work to a remote object (a different server, or a different process). It is commonly used in Web services. Also, our RPC calls can be understood as a kind of remote proxy.
  • Protected proxy: this mode mainly performs security and permission checks. (Little contact)
  • Caching proxy: This is well understood as a way to speed up calls with storage, such as the @cacheable method in Sping, which caches the results of a particular parameter and returns data directly from the cache the next time the method is called with the same parameter.
  • Virtual proxy: This proxy is used to add functionality to a method, such as logging performance metrics or lazy initialization

The above are just the concepts we are looking at, but let’s take a look at the components of the proxy pattern.

  • Subject (common interface) : The existing interface used by the client
  • RealSubject (Real Object) : Class of real objects
  • ProxySubject (proxy object) : Proxy class

As you can see from the figure, the whole interface is still very simple, just a real object and a proxy object.

Purpose: To provide a real proxy object for better control of the real object. This is from Beauty of Design Patterns.

Code example implementation

In order to facilitate understanding, or as an example, do not know whether everyone is reading junior high school or high school has experienced the process of the small strip, if now A classmate C have some words want to talk to classmates (such as playing games together A school) but because it’s class time, and cannot be said with A loud voice, between the classmate A and classmate C took A classmate B, So now student A can only find student B first and give it the note, and let him tell student C, but whether to play or not to play, that is still only A real student C can decide.

Therefore, the agent mode can be understood as student B is the agent of student C. Student A can only find student B when seeking student C. Student B can convey student C through student B and feedback the execution result of student C to student A at the same time.

Finished with the example or concrete look at the implementation of the code

public interface Subject {
   // Common interface
    void doSomething(a);
}
Copy the code

Define a common interface (what everyone should do: Play games together after school)

public class RealSubject implements Subject {
  	// Real objects
    @Override
    public void doSomething(a) {
        System.out.println("Play video games after school."); }}Copy the code

Build a real object, student C in this example

public class ProxySubject implements Subject {

    private RealSubject realSubject;

    public ProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    public ProxySubject(a) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        this.realSubject = (RealSubject) this.getClass().getClassLoader().loadClass("com.ao.bing.demo.proxyPattern.RealSubject").newInstance();
    }

    @Override
    public void doSomething(a) {
        realSubject.doSomething();
    }


    public static void main(String[] args) {

        try {
            // The first way
            new ProxySubject().doSomething();
            // Print the result: Go to play games after school
        } catch (Exception e) {
            // The proxy fails.
            // The teacher caught him passing the note. Or classmate C is not on the seat and other abnormal circumstances
        }

        // The second way
        new ProxySubject(new RealSubject()).doSomething();
        // Print the result: Go to play games after school}}Copy the code

Construct the proxy object, namely student B, so it can be seen that student A has no real contact with student C. Through student B’s proxy for student C, we can know whether student C can play games with him after school

Inside the Main method, there are two ways to call real objects

  • The first is to load the real column object in the form of a classloader, so we don’t have to worry about when we need a real real column object

  • The second way is to pass the materialized object as a value. (Understood as decorator mode)

    The difference here is that the proxy mode provides the exact same interface, while the decorator mode enhances the interface.

Static proxy, dynamic proxy and CGLIb proxy analysis

Static agent

The child implementation above is actually a static proxy, as you can see the overall simplicity. But its disadvantages are also obvious

Static proxies need to create a proxy class for each object, which increases maintenance costs and development costs. To solve this problem, dynamic proxies need to be created instead of creating a proxy class for each class that needs a proxy

A dynamic proxy

Dynamic proxies reasonably avoid the static approach of having to build proxy classes for the classes to be propped up in advance. Instead, it is created at run time through reflection.

You need to understand two things when writing dynamic proxies: Proxy can be understood as a scheduler, and InvocationHandler enhanced service interface can be understood as a Proxy. So I personally understand dynamic proxy as a kind of behavior monitoring.

Specific code to achieve an example: mantis cicada, by listening to the action of the cicada mantis. The multilevel proxy pattern will be discussed later.

public interface BaseService {
    void mainService(a);
}

public class Cicada implements BaseService {
    @Override
    public void mainService(a) {
        System.out.println("Main business, take the cicada for example, when the cicada makes a business call, mantis listens."); }}Copy the code

Create common interfaces as well as real object cicadas

public class PrayingMantis implements InvocationHandler {

    private BaseService baseService;

		// The first example is the style code
    public PrayingMantis(BaseService baseService) {
        this.baseService = baseService;
    }

    // Mantis's main business is listening to objects
    @Override
    public Object invoke(Object listener, Method method, Object[] args) throws Throwable {
        method.invoke(baseService,args);
        secondaryMain();
        return null;
    }
    // Here we understand enhanced business, that is, we can implement InvocationHandler to add other business, such as logging, etc.
    private void secondaryMain(a){
        System.out.println("Mantis chasing cicadas - Secondary business."); }}Copy the code

Create a mantis class that listens for the action of the cicada class

public class BeanFactory {

    public static BaseService newInstanc(Class classFile) {
        // 1. Create cicada, real class object
        BaseService trueCicada = new Cicada();
        // 2. Create a proxy mantis
        InvocationHandler prayingMantis = new PrayingMantis(trueCicada);
        // 3. Request a proxy object from the Jvm.
        Class classArray[] = {BaseService.class};
        BaseService baseService = (BaseService) Proxy.newProxyInstance(classFile.getClassLoader(), classArray, prayingMantis);
        return baseService;
    }
  
  	/ / the Demo test
    public static void main(String[] args) {
        BaseService baseService  = newInstanc(Cicada.class);
        baseService.mainService();
        // Test result: main business
        // Mantis catchers - secondary business}}Copy the code

It can be seen from the results that when cicada main business is called, Mantis can listen to cicada’s business and process other business logic, which is why AOP in Spring can deal with log facets.

Nature of agency:

I think it’s actually a behavior of listening on a proxy object ($proxy InvocationHandler).

Composition of proxy mode:

  • Interface: Declares behavior that needs to be listened on
  • Proxy implementation class (InvocationHandler) : Secondary business, secondary business and primary business binding execution
  • Proxy object (listener object)

Cglib dynamic proxy

Cglib dynamic proxies are similar to JDK dynamic proxies in that they implement the proxy interface.

The specific code is as follows:

public class PrayingMantis implements MethodInterceptor {

    private Cicada cicada;// Proxy object

    public Cicada getInstance(Cicada cicada) {
        this.cicada = cicada;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.cicada.getClass());
        enhancer.setCallback(this);
        return (Cicada) enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object object = methodProxy.invokeSuper(o, objects);
        secondaryMain();
        return object;
    }

    private void secondaryMain(a) {
        System.out.println("Mantis chasing cicadas - Secondary business.");
    }

    public static void main(String[] args) {
        PrayingMantis prayingMantis = new PrayingMantis();
        Cicada instance = prayingMantis.getInstance(new Cicada());
        instance.mainService();
        // Result: primary business
        // Mantis catchers - secondary business
    }

Copy the code

Because cicadas are the same, I won’t post them here.

Careful students have noticed that Cglib does not need to be implemented through an interface; it is invoked by implementing subclasses.

The Enhancer object implements dynamic proxying by setting the proxy object as a subclass of the promenade class. Because it is inherited, the proxy class cannot be modified with final, otherwise an error will be reported.

Final classes: Classes cannot be inherited; internal methods and variables become final types

JDK and Cglib:

JDK dynamic proxies use reflection to generate an anonymous class that implements the proxy interface and calls InvokeHandler to handle it before invoking the specific method

The cglib dynamic proxy uses the ASM open source package to load the class file of the propped object class and subclass it by modifying its bytecode

ASM: A Java bytecode manipulation framework. It can be used to dynamically generate classes or enhance the functionality of existing classes. ASM can either generate binary class files directly or dynamically change the behavior of classes before they are loaded into the Java virtual machine. Java classes are stored in rigorously formatted.class files that have enough metadata to parse all the elements of the class: class names, methods, attributes, and Java bytecodes (instructions). After reading information from class files, ASM can change class behavior, analyze class information, and even generate new classes based on user requirements. — The above ASM explanation comes from Jane’s book

Multistage dynamic agent

After looking at the dynamic proxy above, I do not know if you have an idea to achieve a multi-level dynamic proxy.

Or to mantis cicada as an example, plus a yellow sparrow after the realization of multi-level dynamic proxy mode.

public class Cardinal implements InvocationHandler {
		// Listen to proxy proxy objects
    private Object proxyOne;

    public Cardinal(Object proxyOne) {
        this.proxyOne = proxyOne;
    }

    // Mantis's main business is listening to objects
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws            Throwable {
        method.invoke(proxyOne, args);
        secondaryMain();
        return null;
    }
    private void secondaryMain(a) {
        System.out.println("Sparrow eats Mantis." - Secondary business.); }}Copy the code

Create a sparrow proxy object, which becomes a mantis as its real object, and when the mantis object is called, the sparrow is strong enough to make the corresponding business logic

public class BeanFactory {

    public static BaseService newInstanc(Class classFile) {
        // 1. Create cicada, real class object
        BaseService trueCicada = new Cicada();

        // 2. Create a proxy mantis
        InvocationHandler prayingMantis = new PrayingMantis(trueCicada);

        // 3. Requesting a proxy object from the Jvm is actually a solid object
        Class classArray[] = {BaseService.class};
        BaseService baseService = (BaseService) Proxy.newProxyInstance(classFile.getClassLoader(), classArray, prayingMantis);

        // 4. Create the agent to implement the yellow sparrow secondary agent
        InvocationHandler cardinal = new Cardinal(baseService);
        BaseService secondBaseService = (BaseService) Proxy.newProxyInstance(classFile.getClassLoader(), classArray, cardinal);
        
      	// Add a layer of proxy on top of the finches.
      	// omit other more hierarchical proxy objects
        return secondBaseService;
    }
  
 			 / / the demo test
      public static void main(String[] args) {
        BaseService baseService  = BeanFactory.newInstanc(Cicada.class);
        baseService.mainService();
        // Result: primary business
        // Mantis catchers - secondary business
        // Finches eat mantis - secondary business}}Copy the code

Based on this code, the multilevel proxy process is basically implemented. The mantis listens to the cicadas, and the finch listens to the mantis.

Similarly, if you want to achieve three-level proxy, four-level proxy is not difficult, in each layer of the top of the proxy object can be added.

The essence of dynamic proxy can be understood as decoupling the “secondary business” from the “primary business”, allowing developers to focus more on the primary business, improving development efficiency and maintenance costs.

conclusion

The proxy pattern is relatively rare in business code in my opinion, and dynamic proxies in particular are almost unheard of. But the proxy mode is also a mode we must understand, because learning a good proxy mode helps us to read some source code, check some deeper problems, or in the face of some business scenarios, can also have a great improvement, design mode itself is to solve the problem and create.

With an understanding of dynamic proxies, the implementation of AOP is now self-evident to us.

This is the end of the detailed design patterns, and I will give you a summary of some unusual design patterns.

I’m Aobing, the more you know, the more you don’t know, thank you for your talent: likes, favorites and comments, we’ll see you next time!


This article is constantly updated. You can search “Santaizi Aobing” on wechat and read it for the first time. Reply [Information] There are the interview materials and resume templates for first-line big factories prepared by me.