Home >Java >javaTutorial >[Tomcat] Analysis of Tomcat related design patterns

[Tomcat] Analysis of Tomcat related design patterns

PHP中文网
PHP中文网Original
2017-07-10 18:12:181090browse

Facade Mode

The facade pattern is used in many places in Tomcat. This design pattern is used in Request and Response object encapsulation, Standard Wrapper to ServletConfig encapsulation, ApplicationContext to ServletContext encapsulation, etc.

Principle of facade design pattern

This design pattern has been used on so many occasions, so what role does this design pattern have? As the name suggests, it is to encapsulate something into a facade to make it easier to communicate with others, just like the Ministry of Foreign Affairs of a country.

This design pattern is mainly used when a large system consists of multiple subsystems. These subsystems must involve mutual communication, but each subsystem cannot expose its own internal data to others too much. system, otherwise there would be no need to divide into subsystems. Each subsystem will design a facade to encapsulate the data of interest to other systems and access it through this facade. This is the purpose of the facade design pattern.

The schematic diagram of the facade design pattern is as follows:

Figure 1. Facade diagram

Client can only access the data provided in the Façade, which is the key to the façade design pattern. As for how the Client accesses the Façade and how the Subsystem provides the Façade, the façade design pattern does not stipulate.

Tomcat’s facade mode example

The facade design pattern is used a lot in Tomcat, because there are many different components in Tomcat, and each component needs to interact with each other. Using the facade pattern to isolate data is a good way.

The following is the facade design pattern used on Request:

Figure 2. Request’s facade design pattern class diagram

It can be seen from the figure that the HttpRequestFacade class encapsulates the HttpRequest interface to provide data. The data accessed through the HttpRequestFacade is proxied to the HttpRequest. Usually the encapsulated objects are set to Private or Protected access modifications to prevent Façade is accessed directly.

Observer Pattern

This design pattern is also a commonly used design method. It is also usually called the publish-subscribe pattern, which is the event listening mechanism. It usually triggers some operations before and after an event occurs.

Principle of Observer Pattern

The principle of the observer mode is also very simple, that is, there is always someone watching you next to you when you are doing something. When you do something that interests it, it will do other things accordingly. But the person staring at you must register with you, otherwise you cannot notify it. The observer pattern usually includes the following roles:

  • Subject is the abstract subject: it is responsible for managing the references of all observers and defining the main event operations.
  • ConcreteSubject Concrete subject: It implements all defined interfaces of the abstract subject. When it changes, it will notify all observers.
  • Observer Observer: Monitor the corresponding operation interface for changes in the topic.

Tomcat’s Observer Pattern Example

The observer mode is also used in many places in Tomcat. The Lifecycle that controls the component life cycle mentioned earlier is the embodiment of this mode. The same principle applies to the creation of Servlet instances, Session management, Container, etc. The following mainly looks at the specific implementation of Lifecycle.

Lifecycle’s observer pattern structure diagram:

Figure 3. Observer pattern structure diagram of Lifecycle

In the above structure diagram, LifecycleListener represents an abstract observer, which defines a lifecycleEvent method. This method is the method to be executed when the theme changes. ServerLifecycleListener represents a specific observer. It implements the methods of the LifecycleListener interface, which is the specific implementation method of this specific observer. The Lifecycle interface represents an abstract subject that defines methods for managing observers and other methods it has to do. StandardServer represents a specific topic, which implements all methods of abstract topics. Here Tomcat has extended the observer and added two other classes: LifecycleSupport and LifecycleEvent, which serve as auxiliary classes to extend the functions of the observer. LifecycleEvent allows you to define event categories, and different events can be processed differently, making it more flexible. The LifecycleSupport class represents the topic's management of multiple observers. This management is extracted and implemented uniformly. If you modify it in the future, you only need to modify the LifecycleSupport class. There is no need to modify all specific topics, because all specific topics have operations on observers. Delegated to the LifecycleSupport class. This can be thought of as an improved version of the Observer pattern.

LifecycleSupport's method code for calling the observer is as follows:

Listing 1. fireLifecycleEvent method in LifecycleSupport
<span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> fireLifecycleEvent(String type, Object data) {
    LifecycleEvent event </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> LifecycleEvent(lifecycle, type, data);
    LifecycleListener interested[] </span>= <span style="color: #0000ff">null</span><span style="color: #000000">;
    </span><span style="color: #0000ff">synchronized</span><span style="color: #000000"> (listeners) {
        interested </span>=<span style="color: #000000"> (LifecycleListener[]) listeners.clone();
    }
    </span><span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> i = 0; i < interested.length; i++<span style="color: #000000">)
        interested[i].lifecycleEvent(event);
}</span>

How does the topic notify observers? Look at the code below:

Listing 2. start method in container
<span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> start() <span style="color: #0000ff">throws</span><span style="color: #000000"> LifecycleException {
    lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, </span><span style="color: #0000ff">null</span><span style="color: #000000">);
    lifecycle.fireLifecycleEvent(START_EVENT, </span><span style="color: #0000ff">null</span><span style="color: #000000">);
    started </span>= <span style="color: #0000ff">true</span><span style="color: #000000">;
    </span><span style="color: #0000ff">synchronized</span><span style="color: #000000"> (services) {
        </span><span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> i = 0; i < services.length; i++<span style="color: #000000">) {
            </span><span style="color: #0000ff">if</span> (services[i] <span style="color: #0000ff">instanceof</span><span style="color: #000000"> Lifecycle)
                ((Lifecycle) services[i]).start();
            }
        }
    lifecycle.fireLifecycleEvent(AFTER_START_EVENT, </span><span style="color: #0000ff">null</span><span style="color: #000000">);
}</span>

Let’s take a look at this part of the code in Tomcat7. The previous article said that the life cycle of the component is managed by the parent container containing the component. The startup process of the Service is managed by the Server. The standard implementation class of the Server interface is StandardServer class, StandardServer implements the startInernal() method, which is the process of cyclically starting the Service managed by the StandServer. Tomcat's Services all implement the Lifecycle interface, so the managed Services will be notified, thereby executing the start() method, startIntenal () method is like this:

<span style="color: #008000">/**</span><span style="color: #008000">
 * Start nested components ({</span><span style="color: #808080">@link</span><span style="color: #008000"> Service}s) and implement the requirements
 * of {</span><span style="color: #808080">@link</span><span style="color: #008000"> org.apache.catalina.util.LifecycleBase#startInternal()}.
 *
 * </span><span style="color: #808080">@exception</span><span style="color: #008000"> LifecycleException if this component detects a fatal error
 *  that prevents this component from being used
 </span><span style="color: #008000">*/</span><span style="color: #000000">
@Override
</span><span style="color: #0000ff">protected</span> <span style="color: #0000ff">void</span> startInternal() <span style="color: #0000ff">throws</span><span style="color: #000000"> LifecycleException {

    fireLifecycleEvent(CONFIGURE_START_EVENT, </span><span style="color: #0000ff">null</span><span style="color: #000000">);
    setState(LifecycleState.STARTING);

    globalNamingResources.start();

    </span><span style="color: #008000">//</span><span style="color: #008000"> Start our defined Services</span>
    <span style="color: #0000ff">synchronized</span><span style="color: #000000"> (services) {
        </span><span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> i = 0; i < services.length; i++<span style="color: #000000">) {
            services[i].start();
        }
    }
}</span>

Now all Services will receive the notification and then execute the start method. If a Service is not allowed to be used, a LifecycleException will be thrown.

stopIntenal() will notify all Services to execute the stop method. The specific processing process is similar to the startIntenal() method.

The key to the above code list lies in the fireLifecycleEvent() method, and its execution process is as follows:

  1. Call the fireLifecycleEvent(LifecycleListener listener) method of LifecycleBase. LifecycleBase is an abstract class that implements the Lifecycle interface
  2. Continue to call the fireLifecycleEvent(String type, Object data) method of LifecycleSupport (which is an auxiliary event notification class for registered listeners, cannot be inherited, use final)
  3. Complete event notification

The method of fireLifecycleEvent(String type, Object data) is as follows:

<span style="color: #008000">/**</span><span style="color: #008000">
 * Notify all lifecycle event listeners that a particular event has
 * occurred for this Container.  The default implementation performs
 * this notification synchronously using the calling thread.
 *
 * </span><span style="color: #808080">@param</span><span style="color: #008000"> type Event type
 * </span><span style="color: #808080">@param</span><span style="color: #008000"> data Event data
 </span><span style="color: #008000">*/</span>
<span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> fireLifecycleEvent(String type, Object data) {

    LifecycleEvent event </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> LifecycleEvent(lifecycle, type, data);
    LifecycleListener interested[] </span>=<span style="color: #000000"> listeners;
    </span><span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> i = 0; i < interested.length; i++<span style="color: #000000">)
        interested[i].lifecycleEvent(event);

}</span>

So, the notification of specific events is completed by the lifecycleEvent method of the LifecycleListener interface. Each implementation class can implement different event listening logic according to different situations.

Command Mode

Earlier, the two core components of Tomcat, Connector and Container, were compared to a couple. The man will give the accepted request to the hostess in the form of a command. Corresponding to Connector and Container, Connector also calls Container through command mode.

Principle of command mode

The main function of the command mode is to encapsulate the command and separate the responsibility of issuing the command from the responsibility of executing the command. It is also a functional division of labor. Different modules can interpret the same command differently.

The following is the command mode that usually includes the following roles:

  • Client: Create a command and determine the recipient
  • Command command: The command interface defines an abstract method
  • ConcreteCommand: Specific command, responsible for calling the corresponding operation of the recipient
  • Invoker Requester: Responsible for calling the command object to execute the request
  • Receiver: Responsible for implementing and executing a request

Example of command mode in Tomcat

The command mode in Tomcat is reflected between the Connector and Container components. As an application server, Tomcat will undoubtedly receive many requests. How to distribute and execute these requests is a necessary function.

Let’s take a look at how Tomcat implements the command mode. The following is the structure diagram of the Tomcat command mode:

Figure 4. Structure diagram of Tomcat command mode

Connector acts as an abstract requester, and HttpConnector acts as a concrete requester. HttpProcessor as command. Container serves as the abstract receiver of the command, and ContainerBase serves as the concrete receiver. The client is the application server Server component. Server first creates the command requester HttpConnector object, and then creates the command HttpProcessor command object. The command object is then handed over to the command recipient ContainerBase container to process the command. The command is ultimately executed by Tomcat's Container. Commands can come in as queues, and Containers can also handle requests in different ways. For example, the HTTP1.0 protocol and HTTP1.1 will be handled differently.

Chain of responsibility model

One of the easiest design patterns to discover in Tomcat is the chain of responsibility pattern. This design pattern is also the basis of Container design in Tomcat. The entire container is connected together through a chain, and this chain always correctly passes the request to the final processing. The requested Servlet.

Principle of chain of responsibility model

The chain of responsibility model is that many objects have references to their next objects and are connected to form a chain. The request is passed on this chain until an object on the chain handles the request, or each object The request can be processed and passed to the next one until eventually every object in the chain is processed. In this way, any processing node can be added to the chain without affecting the client.

Usually the chain of responsibility model includes the following roles:

  • Handler (abstract handler): defines an interface for processing requests
  • ConcreteHandler (specific handler): The specific class that handles the request, or passes it to the next party

Example of chain of responsibility pattern in Tomcat

This design pattern is almost completely used in tomcat. The container setting of tomcat is the chain of responsibility mode. From Engine to Host to Context to Wrapper, requests are passed through a chain.

The class structure diagram of the chain of responsibility model in Tomcat is as follows:

Figure 5. Structure diagram of Tomcat chain of responsibility model

The above figure basically describes the class structure diagram of four sub-containers using the chain of responsibility model. The corresponding roles of the chain of responsibility model, Container plays the role of abstract processor, and the specific processor is played by sub-containers such as StandardEngine. Different from the standard chain of responsibility, the Pipeline and Valve interfaces are introduced here. What do they do?

In fact, Pipeline and Valve have expanded the functionality of this chain, allowing them to accept outside intervention during the chain's downward transmission. Pipeline is the tube that connects each sub-container. The Request and Response objects passed inside are like the water flowing in the tube, and Valve is the small openings in this tube, giving you the opportunity to touch the water inside and do some extra things. things.

In order to prevent water from being led out and unable to flow to the next container, there is always a node at the end of each section of pipe to ensure that it can flow to the next sub-container, so each container has a StandardXXXValve. As long as it involves this kind of chained processing flow, this is a model worth learning from.

Go to:

The above is the detailed content of [Tomcat] Analysis of Tomcat related design patterns. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn