Design pattern correlation

Posted by dror_israel on Tue, 15 Feb 2022 05:30:45 +0100

1, Design mode

1. Six principles

1. [single principle]: a class or method is only responsible for one responsibility

2. [Richter substitution principle]: subclasses can extend the functions of the parent class, but cannot change the functions of the original parent class

3. [dependency inversion principle]: interface oriented programming (realizing application scenarios through interfaces as parameters)

  • Abstractions are interfaces or abstract classes, and details are implementation classes
  • The upper module should not rely on the lower module, but both should rely on its abstraction
  • Abstract should not rely on details, details should rely on abstraction

4. [interface isolation principle]: establish a single interface; (an extension class is also an interface, and everything is an interface)

Definition: a. the client should not rely on the interface it does not need; b. Dependencies between classes should be based on the smallest interface

Note: the smaller the design granularity of the interface, the more flexible the system is. However, with the flexibility, the complexity of the structure increases, the difficulty of development increases and the maintainability decreases.

5. [Demeter principle]: the principle of least knowledge to minimize the coupling between classes. An object should have the least understanding of other objects

6. [opening and closing principle]: open for extension and close for modification.

2. Classification of design patterns

[the external chain image transfer fails, and the source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-uwvdhvyz-1644898667723) (C: \ users \ 31289 \ appdata \ roaming \ typora \ typora user images \ image-20214190409865. PNG)]

1. Singleton mode: a class can only have one instance, providing a global access point

2. Determine which kind of factory to create based on the incoming factory parameter.

3. Factory method: define an interface for creating objects, and let subclasses decide which class to instantiate

4. Abstract Factory: create families of related or dependent objects without specifying specific classes.

5. Builder mode: encapsulates the construction process of a complex object and can be constructed step by step.

6. Prototype mode: create a new instance by copying an existing instance.

java.lang.Object#clone()

7. Adapter mode: convert the method interface of a class into another interface desired by the customer.

FileInputStream fileInput = new FileInputStream(file);
InputStreamReader inputStreamReader = new InputStreamReader(fileInput);

8. Combination mode: combine objects into a tree structure to represent the hierarchical structure of "part * whole"

9. Decoration mode: dynamically add new functions to objects

BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

10. Proxy mode: provide a proxy for other objects to control the access of this object.

11. Sharing element (fly quantity) mode: effectively support a large number of fine-grained objects through sharing technology.

12. Appearance mode: provide a unified method to access a group of interfaces in the subsystem.

13. Bridging mode: separate the abstract part from its implementation part, so that they can change independently. Decouple abstraction from implementation.

jdbc Bridge mode is used in

14. Template pattern: define an algorithm structure and delay some steps to subclass implementation.

java.util.Collections#sort()  java.util.AbstractList#indexOf()

15. Interpreter mode: given a language, define a representation of its grammar and define an interpreter.

java.util.Pattern.java.text.Format

16. Strategy pattern: define a series of algorithms, encapsulate them, and make them replace each other.

java.util.Comparator

17. State mode: allows an object to change its behavior when its internal state changes.

18. Observer model: one to many dependencies between objects.

javax.servlet.http.HttpSessionAttributeListener

19. Memo mode: keep the internal state of the object without destroying the encapsulation.

20. Mediator mode: encapsulate a series of object interactions with a mediation object.

21. Command mode: encapsulate the command request as an object, so that different requests can be parameterized.

22. Visitor mode: add new functions for a group of object elements without changing the data structure

23. Responsibility chain mode: decouple the sender or receiver of the request, so that multiple objects have the opportunity to process the request.

javax.servlet.Filter#doFilter()

24. Iterator pattern: a method of traversing and accessing each element in an aggregate object without exposing the internal structure of the object.

java.util.Iterator

3.UML

UML (Unified Modeling Language) is a unified modeling language, which is a standard language for describing, visualizing and documenting the products of object-oriented development system. The following will introduce the basic concepts of nine diagrams + package diagrams of UML and the use scenarios of each diagram.

In the past, we were particularly superstitious about UML. Of course, design must come first in the development process, but now the development emphasizes flexibility, and it is better to use a simpler representation method for design. The purpose of design is to help us better understand the business and develop better.

Common tools are: powerDesigner Rational Rose proccesson

UML is divided into static diagram and dynamic diagram, including 5 commonly used static diagrams and 4 Dynamic diagrams.

Common static diagrams: use case diagram, class diagram, package diagram, object diagram and deployment diagram

Commonly used dynamic diagrams: sequence diagram, communication diagram (called collaboration diagram in UML 1. X), state machine diagram and activity diagram

(1) Class diagram:

Class diagram shows the static structure of the model, especially the classes existing in the model, the internal structure of classes and their relationship with other classes. Class diagrams do not display temporary information. Class diagram is the main component of object-oriented modeling. It is used not only for general conceptual modeling of system classification of applications, but also for detailed modeling to convert the model into programming code. Class diagrams can also be used for data modeling.

In UML class diagrams, the following relationships are common: Generalization, Realization, association, Aggregation, composition, and dependency

1. Generalization

[generalization relation]: it is an inheritance relation, which represents the relationship between general and special. It specifies how subclasses specialize all the characteristics and behaviors of the parent class. For example, tiger is a kind of animal, which has both the characteristics of tiger and the commonalities of animals.

[arrow pointing]: a solid line with a triangular arrow pointing to the parent class

[the external chain picture transfer fails, and the source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-oFz4UO4z-1644898667725)(C:\Users289\Desktop\MD folder \ MD picture screenshot \ 1134451-20170812123014413-2064534630.ac661a9a.png)]

2. Realization

[implementation relationship]: it refers to the relationship between a type and an interface. It indicates that a class is the implementation of all features and behaviors of an interface

[arrow pointing]: dotted line with triangular arrow pointing to the interface

[the transfer of external chain pictures fails, and the source station may have anti-theft chain mechanism. It is recommended to save the pictures and upload them directly (img-2IqKNxZR-1644898667725)(C:\Users289\Desktop\MD folder \ MD picture screenshot \ 20120201092741_47.12ca736a.gif)]

3. Association

[association relation]: a kind of ownership relation, which enables one class to know the attributes and methods of another class; For example, the relationship between teachers and students, husband and wife can be two-way or one-way. A two-way association can have two arrows or no arrows, and a one-way association can have one arrow.

[code embodiment]: member variable

[arrow and direction]: a solid line with an ordinary arrow pointing to the owner

In the above figure, teachers and students are two-way related. Teachers have more than one student, and students may also have more than one teacher. However, the relationship between students and a course is one-way correlation. A student may have to take multiple courses. The course is an abstract thing, which does not have students.

The following figure shows self association:

[the external chain picture transfer fails, and the source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-0Zt3VzCT-1644898667726)(C:\Users289\Desktop\MD folder \ MD picture screenshot \ 20120201092741_335.50e5daa8.gif)]

4. Aggregation

[aggregation relationship]: it is the relationship between the whole and the part, and the part can exist separately from the whole. If the relationship between the car and the tire is integral and partial, the tire can still exist when leaving the car.

Aggregation relationship is a kind of association relationship, which is a strong association relationship; Association and aggregation cannot be distinguished grammatically, so we must investigate the specific logical relationship.

[code embodiment]: member variable

[arrow and direction]: a solid line with a hollow diamond, and the diamond points to the whole

[the external chain picture transfer fails, and the source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-0iptlysm-164489867727) (C: \ users \ 31289 \ desktop \ MD folder \ MD picture screenshot \ 20120201092741_681.9be86ead.gif)]

5. Composition

[combination relationship]: it is the relationship between the whole and the part, but the part cannot exist alone without the whole. If the relationship between the company and the Department is a whole and part, there will be no department without the company.

Composite relationship is a kind of association relationship, which is stronger than aggregation relationship. It requires that the objects representing the whole in ordinary aggregation relationship are responsible for the life cycle of some objects.

[code embodiment]: member variable

[arrow and direction]: solid line with solid diamond, and the diamond points to the whole

[the external chain picture transfer fails, and the source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-EXyPD4ZD-1644898667727)(C:\Users289\Desktop\MD folder \ MD picture screenshot \ 20120201092741_278.33bbf67a.gif)]

6. Dependency

Dependency: it is a kind of usage relationship, that is, the implementation of one class needs the assistance of another class, so try not to use two-way interdependence

[code representation]: local variables, method parameters or calls to static methods

[arrow and direction]: dotted line with arrow, pointing to the user

(2) Sequence diagram:

Sequence Diagram, also known as Sequence Diagram and Sequence Diagram, is a kind of UML Interaction diagram open in new window . It shows the dynamic cooperation among multiple objects by describing the time sequence of sending messages between objects. It can mean Use case open in new window When a use case behavior is executed, each message corresponds to a class operation or State machine open in new window The trigger event that caused the transition in.

[the transfer of external chain pictures fails, and the source station may have anti-theft chain mechanism. It is recommended to save the pictures and upload them directly (img-64UHsJ1u-1644898667728)(C:\Users289\Desktop\MD folder \ MD picture screenshot \ image-20211102151330465.7152bd4e.png)]

(3) Use case diagram:

It is the simplest form of interaction between users and the system User open in new window And related to him Use case open in new window Relationship between. Through the use case diagram, people can know different types of users and use cases of the system. Use case diagrams are often used in conjunction with other diagrams.

2, Factory design mode

Factory pattern is divided into simple factory pattern, factory method pattern and abstract factory pattern. They all belong to the creation pattern in design pattern. Its main function is to help us extract the instantiation part of the object, in order to reduce the code coupling in the system and enhance the scalability of the system.

Another important reason is that the creation process of objects is complex. Using factory mode for encapsulation can shield the complex creation process of objects.

1. Simple factory design mode

The biggest advantage of simple factory mode is to separate the creation of objects from the use of objects, and hand over the creation of objects to a special factory class. However, its biggest disadvantage is that the factory class is not flexible enough. Adding new specific products requires modifying the judgment logic code of the factory class, and when there are many products, the factory method code will be very complex.

The simple factory pattern includes the following roles:

  • Factory: Factory role

    The factory role is responsible for implementing the internal logic for creating all instances

  • Product: abstract product role

    Abstract product role is the parent class of all created objects, which is responsible for describing the common interface shared by all instances

  • ConcreteProduct: specific product role

    The specific product role is the creation target, and all created objects act as instances of a specific class of this role.

    Class diagram:

    [the transfer of external chain pictures fails. The source station may have anti-theft chain mechanism. It is recommended to save the pictures and upload them directly (img-sDbDSq4T-1644898667728)(C:\Users289\Desktop\MD folder \ MD picture screenshot \ image-20211102153046488.1969a898.png)]

1. Define an interface

public interface HuaweiPhone {
    /**
     * How to make a phone call
     */
    void call();
}

Create an implementation class

public class Mate50 implements HuaweiPhone {
    public void call() {
        System.out.println("use a mate50 to make a call.");
    }
}
public class P50 implements HuaweiPhone {
    public void call() {
        System.out.println("use a p50 to make a call.");
    }
}

Create a factory

public class PhoneFactory {
    public static HuaweiPhone getCar(String type){
        if("p50".equalsIgnoreCase(type)){
            //There may be very complex operations
            return new P50();
        }else if("mate50".equalsIgnoreCase(type)){
            //There may be very complex operations
            return new Mate50();
        }else {
            throw new RuntimeException("There are no cell phones of this brand!");
        }
    }
}

test

public class Client {
    public static void main(String[] args) {
        HuaweiPhone p50 = PhoneFactory.getCar("p50");
        p50.call();
        HuaweiPhone mate50 = PhoneFactory.getCar("mate50");
        mate50.call();
    }
}

2. Factory method model

We said that the opening and closing principle should be followed in java development. If I want to add a new car one day, I must modify CarFactory, which is not very flexible. The solution is to use the factory method pattern.

The factory method pattern includes the following roles:

  • Product: abstract product
  • Concrete product: specific product
  • Factory: Abstract Factory
  • ConcreteFactory: specific factory

[the external chain picture transfer fails, and the source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-SkBOIZQ0-1644898667729)(C:\Users289\Desktop\MD folder \ MD picture screenshot \ image-20211102153137978.7a1b4b82.png)]

We build a factory for every car:

First abstract a factory interface

public interface Factory {
    /**
     * Unified creation method
     * @return
     */
    HuaweiPhone create();
}

Then build a factory method for each product

public class Mate50Factory implements Factory {
    public HuaweiPhone create() {
        //Omit 10000 lines of code in the middle
        return new Mate50();
    }
}
public class P50Factory implements Factory {
    public HuaweiPhone create() {
        //Omit 10000 lines of code in the middle
        return new P50();
    }
}

Application scenario

public class Client {
    public static void main(String[] args) {
        Factory benzFactory = new BenzFactory();
        Car benz = benzFactory.create();
        benz.run();
        Factory bmwFactory = new BmwFactory();
        Car bmw = bmwFactory.create();
        bmw.run();
    }
}

benefit

In this mode, by defining an abstract core factory class and defining the interface to create product objects, the creation of specific product instances is delayed to its factory subclass. The advantage of this is that the core class only focuses on the interface definition of the factory class, and the specific product instances are created by the specific factory subclasses. When the system needs to add a new product, there is no need to modify the existing system code, but only need to add a specific product class and its corresponding factory subclass, so that the scalability of the system becomes very good and conforms to the opening and closing principle of object-oriented programming.

shortcoming

Although the factory method pattern has good scalability, it increases the difficulty of coding and the number of classes. Therefore, how to choose depends on the actual requirements.

[the external chain picture transfer fails, and the source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-2nCW2LcG-1644898667729)(C:\Users289\Desktop\MD folder \ MD picture screenshot \ image-20211102110530789.f01ac93b.png)]

3. Abstract factory design pattern

The abstract factory pattern contains the following roles:

  • AbstractFactory: Abstract Factory
  • ConcreteFactory: specific factory
  • AbstractProduct: abstract product
  • Specific Product: Product

Abstract factory pattern is an upgraded version of factory method pattern. When there are multiple business varieties, businesses and classifications, generating required objects through abstract factory pattern is a very good solution.

[the transfer of external chain pictures fails, and the source station may have anti-theft chain mechanism. It is recommended to save the pictures and upload them directly (img-25SWoEyG-1644898667730)(C:\Users289\Desktop\MD folder \ MD picture screenshot \ image-20211102153213009.e85e0a00.png)]

1. Define product abstract classes

Computer product line

/**
 * @author itnanls((wechat)
 * Our service: accompany all the way and get employed smoothly
 */
public abstract class AbstractComputerProduct {

    // Sharing method for producing computer products
    public void sharedMethod(){
        System.out.println("This is the production line of computers.");
    }

    abstract void networking();
}

Mobile phone product line

/**
 * @author itnanls((wechat)
 * Our service: accompany all the way and get employed smoothly
 */
public abstract class AbstractPhoneProduct {

    // Add logo tag
    public void labelling(){
        System.out.println("This is Xiaomi's product.");
    }

    abstract void networking();
}

2. Specific products

We define two Huawei products

public class MateBook16 extends AbstractComputerProduct {
    @Override
    public void networking() {
        System.out.println("Use a mateBook16 to connect to the Internet");
    }
}
public class Mate60 extends AbstractPhoneProduct {
    @Override
    public void networking() {
        System.out.println("Use a Mate60 to connect to the Internet");
    }
}

Define two millet products

public class Mi12 extends AbstractPhoneProduct {
    @Override
    public void networking() {
        System.out.println("Use a Mi12 to connect to the Internet");
    }
}
public class MiBookPro15 extends AbstractComputerProduct {
    @Override
    public void networking() {
        System.out.println("Use a MiBookPro15 to connect to the Internet");
    }
}

3. Define abstract factory

public abstract class AbstractFactory {
    /**
     * Method for producing mobile phone
     * @return
     */
    abstract AbstractPhoneProduct createPhone();

    /**
     * Method of producing computer
     * @return
     */
    abstract AbstractComputerProduct createComputer();
}

4. Define the product factory

Xiaomi product line factory

public class MiFactory extends AbstractFactory {

    @Override
    AbstractPhoneProduct createPhone() {
        return new Mi12();
    }

    @Override
    AbstractComputerProduct createComputer() {
        return new MiBookPro15();
    }
}

Factory of Huawei product line

public class HuaweiFactory extends AbstractFactory {

    @Override
    AbstractPhoneProduct createPhone() {
        return new Mate60();
    }

    @Override
    AbstractComputerProduct createComputer() {
        return new MateBook16();
    }
}

test

public class Client {
    public static void main(String[] args) {
        AbstractFactory factory = new MiFactory();
        AbstractPhoneProduct phone = factory.createPhone();
        phone.networking();
    }
}

This time, our design is relatively complex, adding the concept of product family. If we want to add a product under a certain product line, like the previous factory method design mode, we only need to add corresponding products and factory classes, which will not affect any other products. However, because of our design, the product cannot exist independently of the product family. If we want to add a new product line, such as Lenovo, we don't need to modify other codes.

3, Creator design pattern

Separate the separate construction (by Buider) and assembly (by Director) of the sub components of the object. Thus, complex objects can be constructed. This pattern is applicable to the case where the construction process of an object is complex.

Due to the decoupling of construction and assembly. Different constructors and the same assembly can also make different objects; With the same constructor, different assembly sequences can also make different objects. That is to realize the decoupling of construction algorithm and assembly algorithm, and realize better reuse.

The most typical ones are our StringBuilder and StringBuffer.

The builder mode includes the following roles:

  • Builder: Abstract builder
  • ConcreteBuilder: specific builder
  • Director: Commander
  • Product: product role

[the transfer of external chain pictures fails, and the source station may have anti-theft chain mechanism. It is recommended to save the pictures and upload them directly (img-ZOblhmaa-1644898667730)(C:\Users289\Desktop\MD folder \ MD picture screenshot \ image-20211102162320154.2396ac3b.png)]

1. Simulate a complex object

public class Computer {
    // mouse
    public String mouse;
    // keyboard
    public String keyboard;
    // cpu
    public String cpu;
    // Memory
    public String memoryBank;
    // a main board
    public String mainBoard;
	
	...ellipsis getter and setter
}

2. We can add a static internal class in the Computer class, which is responsible for the construction work

public static class Builder{

    private final Computer computer = new Computer();

    public Builder buildMouse(String type){
        computer.setMouse("One is installed"+ type +"mouse");
        return this;
    }

    public Builder buildKeyboard(String type){
        computer.setKeyboard("One is installed"+ type +"keyboard");
        return this;
    }

    public Builder buildCpu(String type){
        computer.setCpu("One is installed"+ type +"Cpu");
        return this;
    }


    public Builder buildMemoryBank(String type){
        computer.setMemoryBank("One is installed"+ type +"MemoryBank");
        return this;
    }

    public Builder buildMainBoard(String type){
        computer.setMainBoard("One is installed"+ type +"mainBoard");
        return this;
    }

    public Computer build(){
        return computer;
    }
}

We can use this scheme when building computers.

public static void main(String[] args) {
    Computer computer = new Builder()
        .buildMainBoard("ASUS")
        .buildCpu("Intel")
        .buildMemoryBank("Samsung")
        .buildKeyboard("Rapoo")
        .buildMouse("Logitech")
        .build();
    System.out.println(computer);
}

What is the difference between this construction method and ordinary method?

1. When the object we want to construct is relatively complex and there are many member variables, it is not easy to configure flexibly using the constructor.

2. The construction process can be flexibly arranged, and the process can be abandoned or added at any time. Sometimes, there may be requirements for sequencing. The construction order is different, and the results are different.

3. In fact, a single construction process is also complex. For example, the construction of the motherboard itself is also a complex process, and the selection of cpu is also a complex process. It is not easy to express the complexity using setter method.

The creator mode is a "refined" construction process for the construction process of objects. The construction of each part may be changed, but the organization process of objects is fixed. This unified creation method undoubtedly increases our design flexibility. When we build complex objects, if we find that each part may be changed, When there are many different construction steps, we can consider using the creator pattern.

Compared with the factory and abstract factory mode, there is still a big difference. The creator is suitable for the creation of such complex objects. For the abstract factory, it may not be able to complete such assembly work. Moreover, the creator mode calls the internal creation methods of complex objects and organizes and coordinates the control of the sequence of each part of the object. The creator simply describes the situation.

4, Agent design pattern

Agent mode is divided into static agent and dynamic agent. The core function of agent is method enhancement.

1. Static proxy

Static agent role analysis

  • Abstract role: it is generally implemented using interfaces or abstract classes
  • Real role: the role represented
  • Acting role: acting for the real role; After representing a real role, you usually do some ancillary operations
  • Customer: use the agent role to perform some operations

code implementation

Write an interface

public interface Singer {
    /**
     * Singers can sing
     */
    void sing();
}

Define male singers

public class MaleSinger implements Singer{

    private String name;

    public MaleSinger(String name) {
        this.name = name;
    }

    @Override
    public void sing() {
        System.out.println(this.name + "Start singing!");
    }
}

Define broker

/**
 * @author IT Miss Nan
 * @date 2020/5/28
 */
public class Agent implements Singer {

    private Singer singer;

    public Agent(Singer singer) {
        this.singer = singer;
    }

    @Override
    public void sing() {
        System.out.println("Show team, come here! Need to perform, talk about the cost of the performance.....");
        singer.sing();
        System.out.println("Settlement fee, next cooperation appointment......");
    }
}

Client . java is the customer

/**
 * @author IT Miss Nan
 * @date 2020/5/28
 */
public class Client {

    public static void main(String[] args) {
        Singer singer = new MaleSinger("Lu Han");
        Singer agent = new Agent(singer);
        agent.sing();
    }
}

Analysis: in this process, you are in direct contact with Lu Han's agent. The agent played a great role before and after Lu Han's performance.

In addition to implementing a common interface, we can also use the way of inheriting classes

public class Agent extends MaleSinger {

    private MaleSinger maleSinger;

    public void setMaleSinger(MaleSinger maleSinger) {
        this.maleSinger = maleSinger;
    }

    @Override
    public void sing() {
        System.out.println("Start singing-----------");
        maleSinger.sing();
        System.out.println("It's over-----------");
    }
}
public static void main(String[] args) {
    MaleSinger maleSinger = new MaleSinger("Lu Han");
    Agent agent = new Agent();
    agent.setMaleSinger(maleSinger);
    agent.sing();
}

advantage

  • Luhan is still Luhan. There is no need to change Luhan for the pre post work
  • The unified problems of the public shall be handled by the agent
  • When the public business is expanded or changed, it can be more convenient
  • Isn't this more in line with the opening and closing principle, the single principle?

Disadvantages:

  • Write a proxy for each class. It's so troublesome.

2. Dynamic agent

  • The role of dynamic agent is the same as that of static agent
  • The agent class of dynamic agent is dynamically generated, and the agent class of static agent is written by us
  • Dynamic agents are divided into two categories: one is interface based dynamic agents, and the other is class based dynamic agents
    • Dynamic agent based on interface -- JDK dynamic agent
    • Class based dynamic proxy – cglib

Dynamic proxy is that when a large number of classes need to execute some common code, it is too troublesome for us to write it ourselves. Can we directly use java code and automatically generate a class to help us enhance some methods in batch.

(1) JDK native dynamic proxy

Core: InvocationHandler and Proxy. Open the JDK help document and have a look

[InvocationHandler: call handler]

Object invoke(Object proxy, method method, Object[] args);
//parameter
//Proxy - the proxy instance that calls the method
//Method - the method corresponds to the instance that invokes the interface method on the proxy instance. The declared class of the method object will be the interface declared by the method, which can be the super interface of the proxy interface of the proxy class inheriting the method.
//args - array of objects containing method calls that pass the parameter values of the proxy instance, or null if the interface method has no parameters. Included in the appropriate wrapper instance of the original Java class, for example Lang. integer or Java lang.Boolean . 

[Proxy: Proxy]

Proxy.newProxyInstance(ClassLoader loader,
                       Class<?>[] interfaces,
                       InvocationHandler h)

code implementation

Abstract characters and real characters are the same as before!

Or singers and male singers

Agent. java is the broker

/**
 * @author IT Miss Nan
 * @date 2020/5/21
 * agent
 */
public class Agent implements InvocationHandler {

    private Singer singer;

    /**
     * Agent agent
     * @param singer
     */
    public void setSinger(Singer singer) {
        this.singer = singer;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("---------------The broker checks--------------");
        Object returnObj = method.invoke(singer, args);
        System.out.println("---------------Collect the money after singing------------------------");
        return returnObj;
    }

    /**
     * Get a proxy object
     * @return
     */
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                new Class[]{Singer.class},this);
    }
}

Client . java

/**
 * @author IT Miss Nan
 * @date 2020/5/21
 */
public class Client {
    public static void main(String[] args) {

        MaleSinger luhan = new MaleSinger();

        Agent agent = new Agent();
        agent.setSinger(luhan);
        Singer singer = (Singer)agent.getProxy();

        singer.sing();
    }
}

Core: a dynamic agent generally represents a certain type of business. A dynamic agent can represent multiple classes, and the agent is the interface!

//This setting is used to output the classes generated by jdk dynamic proxy
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 

(2) Based on cglib

<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <encoding>utf-8</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>
public static void main(String[] args) {
    Enhancer enhancer=new Enhancer();
    enhancer.setSuperclass(MaleSinger.class);
    enhancer.setCallback(new MethodInterceptor() {
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("---------");
            Object invoke = methodProxy.invokeSuper(o,objects);
            System.out.println("++++++++++");
            return invoke;
        }
    });
    MaleSinger maleSinger = (MaleSinger)enhancer.create();
    maleSinger.sing();
}

public static void main(String[] args) {
    Enhancer enhancer=new Enhancer();
    enhancer.setSuperclass(MaleSinger.class);
    enhancer.setCallback(new MethodInterceptor() {
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("---------");
            Object invoke = methodProxy.invokeSuper(o,objects);
            System.out.println("++++++++++");
            return invoke;
        }
    });
    MaleSinger maleSinger = (MaleSinger)enhancer.create(new Class[]{String.class},new Object[]{"petty thief"});
    maleSinger.sing();
}
 /**
     * All generated proxied methods call this method instead of the original method.
     * The original method may either be invoked by normal reflection using the Method object,
     * or by using the MethodProxy (faster).
     * @param obj "this", the enhanced object
     * @param method intercepted Method
     * @param args argument array; primitive types are wrapped
     * @param proxy used to invoke super (non-intercepted method); may be called
     * as many times as needed
     * @throws Throwable any exception may be thrown; if so, super method will not be invoked
     * @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
     * @see MethodProxy
     */    
    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                               MethodProxy proxy) throws Throwable;
//This setting is used to output the classes generated by cglib dynamic proxy
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class");
  1. Java dynamic Proxy can only Proxy interfaces, not ordinary classes (because the parent class of all generated Proxy classes is Proxy, and Java class inheritance mechanism does not allow multiple inheritance);
  2. CGLIB can represent general classes;
  3. Java dynamic proxy uses Java's native reflection API to operate, which is more efficient in generating classes; CGLIB uses ASM framework to directly operate bytecode, which is more efficient in the process of class execution.

3. ASM chat about the essence of dynamic agent

1. Introduction

ASM is a java bytecode manipulation and analysis framework, which can directly modify classes in binary form or dynamically generate classes. open in new window

2. Use

Next, we use the asm framework to implement a and JDK dynamic agent open in new window The same function.

3. Introducing maven dependency

<dependency>
   <groupId>org.ow2.asm</groupId>
   <artifactId>asm-util</artifactId>
   <version>8.0.1</version>
</dependency>
<dependency>
   <groupId>org.ow2.asm</groupId>
   <artifactId>asm</artifactId>
   <version>8.0.1</version>
</dependency>

4. Define target object interface

/**
 * Can sing
 */
public interface Singable {
  /**
   * sing
   */
  void sing();
}

5. Define target object

/**
 * singer
 */
public class Singer implements Singable {
  @Override
  public void sing() {
    System.out.println("I am singing...");
  }
}

6. Define your own class loader

/**
 * Custom class loader
 */
public class MyClassLoader extends ClassLoader {
  public MyClassLoader() {
    super(Thread.currentThread().getContextClassLoader());
  }

  /**
   * Convert byte array to Class object
   *
   * @param name Class full name
   * @param data class array
   * @return
   */
  public Class<?> defineClassForName(String name, byte[] data) {
    return this.defineClass(name, data, 0, data.length);
  }

}

7. Create proxy

/**
 * @author itnanls((wechat)
 * Our service: accompany all the way and get employed smoothly
 */
public class SingerAgentDump implements Opcodes {

    // Generate a byte array of class
    public static byte[] dump() throws Exception {

        ClassWriter classWriter = new ClassWriter(0);
        FieldVisitor fieldVisitor;
        RecordComponentVisitor recordComponentVisitor;
        MethodVisitor methodVisitor;
        AnnotationVisitor annotationVisitor0;

        // Define class version 1.8, access rights, class name, inherited class, implementation interface and other information
        classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER, "com/ydlclass/SingerAgent", null, "java/lang/Object", new String[]{"com/ydlclass/Singable"});

        classWriter.visitSource("SingerAgent.java", null);

        {
        // Define private properties
            fieldVisitor = classWriter.visitField(ACC_PRIVATE, "delegate", "Lcom/ydlclass/Singable;", null, null);
            fieldVisitor.visitEnd();
        }
        {
        // Define constructor
            methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "(Lcom/ydlclass/Singable;)V", null, null);
            methodVisitor.visitParameter("delegate", 0);
            methodVisitor.visitCode();
            Label label0 = new Label();
            methodVisitor.visitLabel(label0);
            methodVisitor.visitLineNumber(10, label0);
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
            Label label1 = new Label();
            methodVisitor.visitLabel(label1);
            methodVisitor.visitLineNumber(11, label1);
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitVarInsn(ALOAD, 1);
            methodVisitor.visitFieldInsn(PUTFIELD, "com/ydlclass/SingerAgent", "delegate", "Lcom/ydlclass/Singable;");
            Label label2 = new Label();
            methodVisitor.visitLabel(label2);
            methodVisitor.visitLineNumber(12, label2);
            methodVisitor.visitInsn(RETURN);
            Label label3 = new Label();
            methodVisitor.visitLabel(label3);
            methodVisitor.visitLocalVariable("this", "Lcom/ydlclass/SingerAgent;", null, label0, label3, 0);
            methodVisitor.visitLocalVariable("delegate", "Lcom/ydlclass/Singable;", null, label0, label3, 1);
            methodVisitor.visitMaxs(2, 2);
            methodVisitor.visitEnd();
        }
        {
// Define method sing
            methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "sing", "()V", null, null);
            methodVisitor.visitCode();
            Label label0 = new Label();
            methodVisitor.visitLabel(label0);
            methodVisitor.visitLineNumber(16, label0);
            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            methodVisitor.visitLdcInsn("before sing...");
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
            Label label1 = new Label();
            methodVisitor.visitLabel(label1);
            methodVisitor.visitLineNumber(17, label1);
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitFieldInsn(GETFIELD, "com/ydlclass/SingerAgent", "delegate", "Lcom/ydlclass/Singable;");
            methodVisitor.visitMethodInsn(INVOKEINTERFACE, "com/ydlclass/Singable", "sing", "()V", true);
            Label label2 = new Label();
            methodVisitor.visitLabel(label2);
            methodVisitor.visitLineNumber(18, label2);
            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            methodVisitor.visitLdcInsn("after sing...");
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
            Label label3 = new Label();
            methodVisitor.visitLabel(label3);
            methodVisitor.visitLineNumber(19, label3);
            methodVisitor.visitInsn(RETURN);
            Label label4 = new Label();
            methodVisitor.visitLabel(label4);
            methodVisitor.visitLocalVariable("this", "Lcom/ydlclass/SingerAgent;", null, label0, label4, 0);
            methodVisitor.visitMaxs(2, 1);
            methodVisitor.visitEnd();
        }
        classWriter.visitEnd();

        return classWriter.toByteArray();
    }

    // Load byte array as class
    public static Singable newProxyInstance(Singable delegate) throws Exception {
        String className = "com.ydlclass.SingerAgent";
        byte[] classData = dump();
        Class<?> aClass = new MyClassLoader().defineClassForName(className, classData);
        return (Singable) aClass.getDeclaredConstructor(Singable.class).newInstance(delegate);
    }
}

ASM provides a function that can convert ordinary java code into ASM code. The above code is obtained through conversion.

8. Define the java code to convert

/**
 * Singer agent
 */
public class SingerAgent implements Singable {

  private Singable delegate;

  public SingerAgent(Singable delegate) {
    this.delegate = delegate;
  }

  @Override
  public void sing() {
    System.out.println("before sing...");
    delegate.sing();
    System.out.println("after sing...");
  }
}

9. Conversion

public class Client {
  public static void main(String[] args) throws IOException {
    ASMifier.main(new String[]{SingerAgent.class.getName()});
  }
}

10. Client call

public class Client {
  public static void main(String[] args) throws Exception {
    Singable singerAgent = SingerAgentDump.newProxyInstance(new Singer());
    singerAgent.sing();
  }

}

The output result is

before sing...
I am singing...
after sing...

As expected, it shows that the class es we create through ASM can work normally. However, compared with the dynamic proxy created by JDK, ASM involves a lot of operations on the underlying bytecode of java. The more familiar we are with bytecode, the easier it will be to use ASM.

11. Problems encountered

// Load byte array as class
public static SingerAgent newProxyInstance(Singable delegate) throws Exception {
    String className = "com.imooc.sourcecode.java.dynamicproxy.asm.test3.SingerAgent";
    byte[] classData = dump();
    Class<?> aClass = new MyClassLoader().defineClassForName(className, classData);
    return (SingerAgent) aClass.getDeclaredConstructor(Singable.class).newInstance(delegate);
  }

At first, I forcibly converted the loaded class to the SingerAgent class, but an exception was reported

Exception in thread "main" java.lang.ClassCastException: class com.imooc.sourcecode.java.dynamicproxy.asm.test3.SingerAgent cannot be cast to class com.imooc.sourcecode.java.dynamicproxy.asm.test3.SingerAgent (com.imooc.sourcecode.java.dynamicproxy.asm.test3.SingerAgent is in unnamed module of loader com.imooc.sourcecode.java.dynamicproxy.asm.test3.MyClassLoader @4590c9c3; com.imooc.sourcecode.java.dynamicproxy.asm.test3.SingerAgent is in unnamed module of loader 'app')
	at com.imooc.sourcecode.java.dynamicproxy.asm.test3.SingerAgentDump.newProxyInstance(SingerAgentDump.java:98)
	at com.imooc.sourcecode.java.dynamicproxy.asm.test3.Client.main(Client.java:5)

The reason is that the generated SingerAgent class and the original SingerAgent class are not loaded by the same class loader, so they cannot be converted.

12. ASM usage scenarios

ASM is a very powerful bytecode tool. The following uses ASM

  • The openjdk, to generate the lambda call sites, and also in the nashorn compiler
  • The groovy compiler and the Kotlin compiler, cobertura and Jacob, to instrument classes in order to measure code coverage, compilers for groovy and Kotlin
  • CGLIB, to dynamically generate proxy classes (which are used in other projects such as Mockito and EasyMock), Cglib framework
  • Gradle, to generate some classes at runtime. Gradle framework

Topics: Design Pattern