Analysis of Tomcat Architecture Principle for reference to architecture design


Tomcat has developed for so many years, and it has been relatively mature and stable. In today’s “chasing new and fast” era, Tomcat, as a necessary tool for Java Web development, seems to have become a “familiar stranger”. Is it unnecessary to study it in depth now? What can we gain from learning it?

Calm down and savor the classic open source works。 To improve our “internal skills” is to learn how to design and build a middleware system, and let these experiences be used by us.

Beautiful things are often neat and elegant. But this is not equal to simple, but to decompose the complex system into small modules, and the division of responsibilities of each module should be clear and reasonable.

On the contrary, it’s messy. For example, you may feel uncomfortable when you see a pile of entangled wires in a village in the city. The maintenance code is thousands of lines for a class and hundreds of lines for a method. You might say what the f * k!

Analysis of Tomcat Architecture Principle for reference to architecture design

Learning purpose

Mastering the design and principle of Tomcat architecture and improving internal skill

On the macro level

Tomcat as a “HttpServer+ServletContainer “blocks the application layer protocol and network communication details, and gives us the standardRequestandResponseFor the specific business logic, as a change point, it is given to us to implement. We used itSpringMVCSuch a framework, but it never needs to be consideredTCPConnectionHttpData processing and response of the protocol. Because Tomcat has done this for us, we only need to focus on the specific business logic of each request.

From the microscopic point of view

TomcatIt also separates the change point and the invariable point internally, and uses component-based design to achieve a highly customized “Russian dolly style” (composition mode). However, there are some common things in the life cycle management of each component, which are extracted into interfaces and abstract classes, so that the specific subclasses can realize the change points, that is, the template method design pattern.

The popular microservice is also the same idea. According to the function, the monomer application is divided into “microservices”. In the splitting process, the commonness is extracted, and these commonalities will become the core basic services or general library. The same is true of the “middle Taiwan” ideology.

Design patterns are often a sharp tool to encapsulate changes. The rational use of design patterns can make our code and system design elegant and tidy.

This is the “internal skill” that can be obtained by learning excellent open source software. It will never be out of date. The fundamental way is the design idea and philosophy. We can learn from the design experience and use the design pattern to encapsulate the change and the invariance reasonably, so as to learn from their source code and improve their system design ability.

Macro understanding of how a request relates to spring

In the course of our work, we are already familiar with Java syntax. We have even “recited” some design patterns and used many web frameworks. However, we seldom have the opportunity to use them in actual projects. It seems that we can design a system independently according to the requirements. I don’t seem to have a java web development panorama in mind. For example, I don’t know how browser requests relate to the code in spring.

In order to break through this bottleneck, why not stand on the shoulders of giants to learn excellent open source systems and see how the Bulls think about these issues.

Learning the principle of tomcat, I found thatServletTechnology is the origin of web development, and almost all Java Web frameworks (such as spring) are based onServletThe spring application itself is aServletDispatchSevlet)And web containers like Tomcat and jetty are responsible for loading and runningServlet。 As shown in the figure:

Analysis of Tomcat Architecture Principle for reference to architecture design

Improve the ability of system design

Learning tomcat, I also found that many advanced Java technologies were used, such as Java multithreading concurrent programming, socket network programming and reflection. I only knew these skills before, and I recited some questions for the interview. But I always feel that there is a gap between the participants and users. Through learning Tomcat source code, I have learned what scenarios to use these technologies.

There are also system design capabilities, such as interface oriented programming, componentized composition patterns, skeleton abstract classes, one click start stop, object pool technology and various design patterns, such as template method, observer pattern, chain of responsibility pattern, etc. after that, I began to imitate them and apply these design ideas to practical work.

Pay attention to the official account “code brother”, more hard core, and so on.

Overall architecture design

Today, let’s analyze the design ideas of Tomcat step by step. On the one hand, we can learn the overall architecture of tomcat, learn how to design a complex system from a macro perspective, how to design top-level modules, and the relationship between modules; on the other hand, we can lay a foundation for us to learn the working principle of Tomcat.

Tomcat startup -> start ->java -jar org.apache.catalina.startup.Bootstrap.main()

Tomcat implements two core functions:

  • handleSocketConnection, responsible for network byte stream andRequestandResponseObject transformation.
  • Load and manageServletAnd dealing with specificRequestRequest.

So Tomcat has designed two core component connectors and containers. The connector is responsible for external communication, and the container is responsible for internal processing

TomcatIn order to support a variety ofI/OModel and application layer protocol, a container may dock multiple connectors, just as a room has multiple doors.

Analysis of Tomcat Architecture Principle for reference to architecture design

  • Server corresponds to a Tomcat instance.
  • There is only one service by default, that is, a Tomcat instance defaults to one service.
  • Connector: a service may have multiple connectors and accept different connection protocols.
  • Container: multiple connectors correspond to a container, and the top container is actually engine.

Each component has a corresponding life cycle, which needs to be started. At the same time, it also needs to start its own internal sub components. For example, a Tomcat instance contains a service, and a service contains multiple connectors and a container. As a container contains multiple hosts, there may be multiple context t containers inside the host, and a context may also contain multiple servlets. Therefore, Tomcat manages each component by using the combination mode, and treats each component as well as a single group。 As a whole, each component is designed like a “Russian Doll”.


I’ll lay the groundwork before I start with the connectorTomcatVarious types of supportI/OModel and application layer protocol.

TomcatSupportedI/OThe models are as follows:

  • NIO: non blockingI/O, usingJava NIOClass library implementation.
  • NIO2: asynchronousI/O, usingJDK 7abreast of the timesNIO2Class library implementation.
  • APR: UsingApachePortable runtime implementation, isC/C++Write a local library.

Tomcat supports the following application layer protocols:

  • HTTP/1.1: This is the access protocol used by most web applications.
  • AJP: for integration with web servers (such as APACHE).
  • HTTP/2HTTP 2.0 greatly improves web performance.

So a container may dock multiple connectors. Connector pairServletThe container blocks network protocol andI/OThe difference between models, whether it’sHttpstillAJP, and all that you get in the container are standardServletRequestObject.

The functional requirements of the connector are as follows:

  • Listen to the network port.
  • Accept network connection requests.
  • Read request network byte stream.
  • According to the specific application layer protocol(HTTP/AJP)Parse byte stream to generate unifiedTomcat RequestObject.
  • takeTomcat RequestObject to standardServletRequest
  • callServletContainer, getServletResponse
  • takeServletResponseIntoTomcat ResponseObject.
  • takeTomcat ResponseConvert to network byte stream.
  • Write the response byte stream back to the browser.

After the requirements are listed clearly, the next question we need to consider is, what sub modules should the connector have? Excellent modular design should be consideredHigh cohesion and low coupling

  • High cohesionIt means that the functions with high correlation should be concentrated as much as possible and not dispersed.
  • Low couplingIt means that two related modules should reduce the dependent parts and degree as much as possible, and do not let the two modules produce strong dependence.

We found that three connectors need to be completedHigh cohesionFunctions of:

  • Network Communications.
  • Application layer protocol analysis.
  • Tomcat Request/ResponseAndServletRequest/ServletResponseThe transformation of.

Therefore, the designer of Tomcat has designed three components to realize these three functionsEndpoint, processor and adapter

The I / O model of network communication is changing, and the application layer protocol is also changing, but the overall processing logic is unchanged,EndPointResponsible for providing byte stream toProcessorProcessorResponsible for providingTomcat RequestObject toAdapterAdapterResponsible for providingServletRequestObject to the container.

Package change and invariance

So Tomcat designed a series of abstract base classesEncapsulate these stable parts, abstract base classAbstractProtocolYesProtocolHandlerInterface. Each application layer protocol has its own abstract base class, such asAbstractAjpProtocolandAbstractHttp11ProtocolThe implementation class of specific protocol extends the abstract base class of protocol layer.

This is the application of template design pattern.

Analysis of Tomcat Architecture Principle for reference to architecture design

In summary, there are three core components of the connectorEndpointProcessorandAdapterTo do three things, one of which isEndpointandProcessorPut it together and abstract itProtocolHandlerComponents, and their relationship is shown in the figure below.

Analysis of Tomcat Architecture Principle for reference to architecture design

Protocolhandler component

Main treatmentnetwork connectionsandApplication layer protocol, including two important components endpoint and processor. The two components combine to form protocohandler. I will introduce their working principles in detail.


EndPointIt is the communication endpoint, that is, the interface of communication monitoring, the specific socket receiving and sending processor, and the abstraction of the transport layerEndPointIs used to implementTCP/IPProtocol data read-write, the essence of the call operating system socket interface.

EndPointIs an interface, and the corresponding abstract implementation class isAbstractEndpoint, andAbstractEndpointFor example, in theNioEndpointandNio2EndpointIn, there are two important subcomponents:AcceptorandSocketProcessor

The acceptor is used to listen for socket connection requests.SocketProcessorFor processingAcceptorReceivedSocketRequest, it implementsRunnableInterface, inRunMethod to call the application layer protocol processing componentProcessorProcessing. In order to improve the processing capacity,SocketProcessorIs committed to the thread pool for execution.

As we know, the use of Java multiplexer is nothing more than two steps:

  1. Create a Seletor, register all kinds of interesting events on it, then call the select method, waiting for something interesting to happen.
  2. When something of interest occurs, such as when it can be read, a new thread is created to read data from the channel.

In TomcatNioEndpointthen isAbstractEndpointAlthough there are many components in it, the processing logic is still the first two steps. It containsLimitLatchAcceptorPollerSocketProcessorandExecutorThere are 5 components in total, which can realize the whole TCP / IP protocol processing separately.

  • Limitlatch is the connection controller, which is responsible for controlling the maximum number of connections. In NiO mode, the default value is 10000. After reaching this threshold, the connection request is rejected.
  • AcceptorIt runs in a separate thread, which is called in a dead loopacceptMethod to receive a new connection,acceptMethod returns aChannelObject, and then putChannelThe object is left to poller to handle.
  • PollerThe essence of is aSelector, also running in a separate thread.PollerMaintain one internallyChannelArray, which is constantly detected in an endless loopChannelData ready status, once availableChannelRead, generate oneSocketProcessorThe task object throwsExecutorTo deal with it.
  • The socket processor implements the runnable interface, where thegetHandler().process(socketWrapper, SocketEvent.CONNECT_FAIL);The code is to get the handler and execute the socket wrapper, and finally get the appropriate application layer protocol processor through the socket, that is to call the http11processor component to process the request. Http11processor reads channel data to generate ServletRequest object. Http11processor does not read channel directly. This is because Tomcat supports synchronous non blocking I / O model and asynchronous I / O model. In Java API, the corresponding channel classes are different, such as asynchronous socketchannel and socketchannel. In order to shield these differences from http11processor, Tomcat has designed a wrapper class called socketwrapper. Http11processor only calls socketwrapper To read and write data.
  • ExecutorIs the thread pool, responsible for runningSocketProcessorTask class,SocketProcessorOfrunMethod is calledHttp11ProcessorTo read and parse the request data. We know that,Http11ProcessorIt is the encapsulation of application layer protocol. It will call the container to get the response, and then pass the response throughChannelWrite it out.

The workflow is as follows:

Analysis of Tomcat Architecture Principle for reference to architecture design


Processor is used to implement HTTP protocol. Processor receives socket from endpoint, reads byte stream, parses it into Tomcat request and response object, and submits it to container for processing through adapter. Processor is the abstraction of application layer protocol.

Analysis of Tomcat Architecture Principle for reference to architecture design

From the figure, we can see that after receiving the socket connection, endpoint generates a socketprocessor task and submits it to the thread pool for processing. The runmethod of the socketprocessor will call the httpprocessor component to resolve the application layer protocol. After the processor generates the request object by parsing, it will call the service method of the adapter, and the method will pass through the The following code passes the request to the container.

// Calling the container
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

Adapter components

Due to different protocols, Tomcat defines its ownRequestClass to store the request information, which actually reflects the object-oriented thinking. But this request is not standardServletRequestTherefore, you cannot directly use Tomcat to define request as a direct container for parameters.

The solution for Tomcat designers is to introduceCoyoteAdapterThis is a classic application of the adapter pattern, connector callCoyoteAdapterOfSeviceMethod, the input isTomcat RequestObject,CoyoteAdapterResponsible forTomcat RequestIntoServletRequestAnd then call the container’sServicemethod.


The connector is responsible for external communication, and the container is responsible for internal processing. Specifically, the connector handles socket communication and application layer protocol analysisServletRequests; containers handle themServletRequest.

Container: as the name implies, it is used to hold things, so Tomcat container is used to loadServlet

Tomcat has designed four kinds of containers, namelyEngineHostContextandWrapperServerRepresents the Tomcat instance.

It should be noted that these four containers are not parallel, they belong to parent-child relationship, as shown in the following figure:

Analysis of Tomcat Architecture Principle for reference to architecture design

You may ask, why design so many layers of containers, it does not increase the complexity? In fact, the consideration behind this is,Tomcat makes servlet container have good flexibility through a layered architecture. Because this is in line with a host with multiple contexts, a context also contains multiple servlets, and each component needs unified lifecycle management, so the composite pattern designs these containers

WrapperRepresents aServletContextRepresents a web application, and a web application may have more than oneServletHostIt refers to a virtual host, or a site. A Tomcat can configure multiple sites (hosts); a site (host) can deploy multiple web applications;EngineRepresents the engine, which is used to manage multiple sites (hosts). A service can only have oneEngine

We can deepen the understanding of its hierarchical relationship through Tomcat configuration file.

The top-level component can contain multiple services, representing a Tomcat instance

  < service name = "Catalina" > // top level component, including one engine and multiple connectors
    <Connector port="8080" protocol="HTTP/1.1"
               redirectPort="8443" />

    <!-- Define an AJP 1.3 Connector on port 8009 -->
    < connector port = 8009 "protocol =" AJP / 1.3 "redirectport =" 8443 "/ > // connector

    //Container component: one engine handles all requests of service, including multiple hosts
    <Engine name="Catalina" defaultHost="localhost">
      //Container component: handles client requests under the specified host, which can contain multiple contexts
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
            //Container component: handles all client requests for a specific context web application

How do you manage these containers? We found that there is a parent-child relationship between containers, forming a tree structure. Did we think of the design patternCombination mode

Tomcat manages these containers in a composite pattern. The specific implementation method is as follows,All container components are implementedContainerTherefore, the composition pattern can make the user’s use of single container object and composite container object consistent。 Here, the single container object refers to the lowest levelWrapperThe composite container object refers to theContextHostperhapsEngineContainerThe interface is defined as follows:

public interface Container extends Lifecycle {
    public void setName(String name);
    public Container getParent();
    public void setParent(Container container);
    public void addChild(Container child);
    public void removeChild(Container child);
    public Container findChild(String name);

We saw itgetParentSetParentaddChildandremoveChildAnd so on. Here we just verify the combination pattern. We also see thatContainerInterface expandedLifecycleThrough TomcatLifecycleManage the life cycle of components of all containers in a unified way. Manage all containers through combination mode, expandLifecycleRealize the life cycle management of each component,LifecycleMain methods includedInit(), start(), stop() and destroy ()

The process of requesting to locate the servlet

How is a request located to whichWrapperOfServletProcessed? The answer is that Tomcat does this with mapper components.

MapperThe function of the component is to send theURLLocate to aServletIts working principle is as follows:MapperThe configuration information of the web application is saved in the componentMapping relationship between container component and access pathFor exampleHostThe domain name configured in the containerContextIn the containerWebApplication path, andWrapperIn the containerServletYou can imagine that the configuration information is multi-levelMap

When a request arrives,MapperBy parsing the domain name and path in the request URL, the component can locate aServlet。 Please note that a request URL will end up with only oneWrapperContainer, that is, a containerServlet

Analysis of Tomcat Architecture Principle for reference to architecture design

Suppose a user visits a URL, such as the one in the figure does Tomcat locate this URL to a servlet?

  1. First, determine the service and engine according to the protocol and port number。 Tomcat’s default HTTP connector listens on port 8080, and the default AJP connector listens on port 8009. The URL in the above example accesses port 8080, so the request will be received by the HTTP connector. A connector belongs to a service component, so the service component determines. We also know that in addition to multiple connectors, a service component also has a container component, specifically an engine container. Therefore, if the service is determined, it means that the engine is also determined.
  2. Select the host according to the domain name.After the service and engine are determined, mapper component searches the corresponding host container through the domain name in the URL. For example, the domain name accessed by the URL in the example mapper finds the host2 container.
  3. Find the context component according to the URL path.After the host is determined, mapper matches the path of the corresponding web application according to the path of the URL. For example, / order is accessed in the example, so the context container context4 is found.
  4. Find the wrapper (servlet) according to the URL path.After the context is determined, mapper will use the web.xml To find the specific wrapper and servlet.

The adapter in the connector will call the service method of the container to execute the servlet. The engine container gets the request first. After the engine container has processed the request, it will pass the request to its own sub container host to continue processing, and so on. Finally, the request will be passed to the wrapper container, and the wrapper will call the final servlet to process it. How to implement the calling procedure? The answer is to use pipeline valve pipes.

Pipeline-ValveIt refers to the chain of responsibility mode. In the process of processing a request, many processors process the request in turn. Each processor is responsible for its own processing. After processing, it will call the next processor to continue processing. Valve represents a processing point (i.e., a processing valve). Therefore, it can be used to process the requestinvokeMethod is to handle the request.

public interface Valve {
  public Valve getNext();
  public void setNext(Valve valve);
  public void invoke(Request request, Response response)

Continue with the pipeline interface

public interface Pipeline {
  public void addValve(Valve valve);
  public Valve getBasic();
  public void setBasic(Valve valve);
  public Valve getFirst();

PipelineThere areaddValvemethod. Maintained in pipelineValveLinked list,ValveCan be inserted intoPipelineDo some processing for the request. We also found that there is no invoke method in pipeline, because the trigger of the whole call chain is completed by valve,ValveAfter completing its own processing, callgetNext.invoke()To trigger the next valve call.

In fact, each container has a pipeline object. As long as the first valve of the pipeline is triggered, the containerPipelineAll the valves in the will be called to. However, how do the pipelines of different containers be triggered in chain? For example, the pipeline in the engine needs to call the pipeline in the lower container host.

that is becausePipelineThere’s another one in thegetBasicmethod. thisBasicValvebe inValveAt the end of the list, it’sPipelineAn essential part ofValveIs responsible for calling the first valve in the pipeline of the lower container.

Analysis of Tomcat Architecture Principle for reference to architecture design

The whole process is divided into two parts: theCoyoteAdapterTrigger, it will call the first valve of engine:

public void service(org.apache.coyote.Request req, org.apache.coyote.Response res) {
    //Omit other codes
    // Calling the container
        request, response);

The last valve of the wrapper container creates a filter chain and calls thedoFilter()Method, which will be adjusted toServletOfservicemethod.

We didn’t talk about itFilterIt seems to have similar functionsValveandFilterWhat’s the difference? The differences between them are as follows:

  • ValveyesTomcatAnd the infrastructure of TomcatAPIIt’s tightly coupled.Servlet APIIt is a public standard. All web containers, including jetty, support the filter mechanism.
  • Another important difference is thatValveWorks at the web container level, intercepts all application requests; andServlet FilterWorking at the application level, only one can be interceptedWebAll requests for the app. If you want to do the whole thingWebThe interceptor of the container must pass throughValveTo achieve.


We can see from the frontContainerThe container inheritsLifecycleLife cycle. If we want a system to provide services to the outside world, we need to create, assemble and start these components; when the service stops, we also need to release resources and destroy these components, so this is a dynamic process. In other words, Tomcat needs to dynamically manage the lifecycle of these components.

How to manage the creation, initialization, start, stop and destroy of components? How to make the code logic clear? How to easily add or remove components? How to achieve the start and stop of components without omission and repetition?

One touch start stop: lifecycle interface

Design is to find the change point and invariable point of the system. The invariable point here is that each component has to go through the process of creation, initialization and startup, and these States and state transformation are invariant. The change point is that the initialization method of each specific component is different, that is, the startup method is different.

Therefore, Tomcat abstracts invariant points into an interface, which is related to the life cycle and is called lifecycle. The lifecycle interface defines the following methods:Init(), start(), stop() and destroy ()Each specific component (that is, the container) implements these methods.

In the parent component’sinit()Method to create and call the subcomponentinit()method. Similarly, in the parent component’sstart()Method also needs to call thestart()Method, so callers can call theinit()Methods andstart()This is the methodCombination modeAnd as long as you call the top-level component, that is, the server componentinit()andstart()Method, the entire Tomcat is started. Therefore, Tomcat adopts the combination mode to manage the container, and the container inherits the lifecycle interface. In this way, the lifecycle of each container can be managed with one key as for a single object, and the whole Tomcat will be started.

Scalability: lifecycle events

Let’s consider another problem, which is the scalability of the system. Because of the componentsinit()andstart()The specific implementation of the method is complex and changeable. For example, in the startup method of the host container, you need to scan the web application under the webapps directory, create the corresponding context container, and modify it directly if you need to add new logic in the futurestart()method? This will violate the opening and closing principle, so how to solve this problem? The open close principle says that in order to extend the functions of the system, you can not directly modify the existing classes in the system, but you can define new classes.

Component’sinit()andstart()The call is triggered by the state change of its parent component. The initialization of the upper component will trigger the initialization of the child component, and the startup of the upper component will trigger the start of the child component. Therefore, we define the life cycle of a component as a state and treat the state transition as an event. Events have listeners, in which some logic can be implemented, and listeners can be easily added and deletedThis is typicalObserver model

Here’s what it looks likeLyfecycleDefinition of interface:

Analysis of Tomcat Architecture Principle for reference to architecture design

Reusability: lifecyclebase abstract base class

Again, see the abstract template design pattern.

With interfaces, we need to implement interfaces with classes. Generally speaking, there is more than one implementation class. Different classes often have the same logic when implementing interfaces. If each subclass is implemented once, there will be repeated code. How can subclasses reuse this logic? In fact, it is to define a base class to implement the common logic, and then let each subclass inherit it to achieve the purpose of reuse.

Tomcat defines a base class lifecyclebase to implement the lifecycle interface. It puts some common logic into the base class, such as the transformation and maintenance of life state, the triggering of life events, and the addition and deletion of listeners. The subclass is responsible for implementing its own initialization, start and stop methods.

public abstract class LifecycleBase implements Lifecycle{
    //Hold all observers
    private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();
     *Publishing events
     * @param type  Event type
     * @param data  Data associated with event.
    protected void fireLifecycleEvent(String type, Object data) {
        LifecycleEvent event = new LifecycleEvent(this, type, data);
        for (LifecycleListener listener : lifecycleListeners) {
    //The template method defines the entire startup process and starts all containers
    public final synchronized void init() throws LifecycleException {
        //1. Condition check
        if (!state.equals(LifecycleState.NEW)) {

        try {
            //2. The listener that triggers the initiating event
            setStateInternal(LifecycleState.INITIALIZING, null, false);
            //3. Call the initialization method of concrete subclass
            //4. The listener that triggers the initialized event
            setStateInternal(LifecycleState.INITIALIZED, null, false);
        } catch (Throwable t) {
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(
                    sm.getString("lifecycleBase.initFail",toString()), t);

In order to realize one click start stop and elegant life cycle management, Tomcat takes the object-oriented idea and design pattern to the extreme, considering the scalability and reusability,ContainaerThe interface maintains the parent-child relationship of the container,LifecycleComposition pattern is used to maintain the life cycle of components. In the life cycle, each component has its own change and invariable points. The template method pattern is used. RespectivelyComposition pattern, observer pattern, skeleton abstract class and template method

If you need to maintain a bunch of entities with parent-child relationships, consider using composition patterns.

The observer mode sounds “high and high”, but it means that when an event occurs, it needs to perform a series of update operations. A low coupling, non intrusive notification and update mechanism is implemented.

Analysis of Tomcat Architecture Principle for reference to architecture design

ContainerIt inherits lifecycle. Standardengine, standardhost, standardcontext and standardwrapper are the concrete implementation classes of corresponding container components. Because they are all containers, they inherit the abstract base class of containerbase. Containerbase implements the container interface and inherits lifecyclebase Class, their lifecycle management interfaces and functional interfaces are separate, which is also consistent with the designPrinciple of interface separation

Why does Tomcat break the parental delegation mechanism

Parental delegation

We knowJVMWhen loading a class, the class loader is based on the parent delegation mechanism, that is, the load will be handed over to its own parent loader. If the parent loader is empty, it will be searchedBootstrapWhether it has been loaded or not. When it cannot be loaded, you can load it yourself. JDK provides an abstract classClassLoaderThree key methods are defined in this abstract class. External useLoadclass (string name) used for subclass override to break parent delegation: loadclass (string name, Boolean resolve)

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
    synchronized (getClassLoadingLock(name)) {
        //Find out if the class has been loaded
        Class<?> c = findLoadedClass(name);
        //If not loaded
        if (c == null) {
            //Delegate to the parent loader to load, recursively call
            if (parent != null) {
                c = parent.loadClass(name, false);
            } else {
                //If the parent loader is empty, find if the bootstrap has been loaded
                c = findBootstrapClassOrNull(name);
            //If it still fails to load, call your own findclass to load it
            if (c == null) {
                c = findClass(name);
        if (resolve) {
        return c;
protected Class<?> findClass(String name){
    //1. According to the class name passed in, find the class file in the specific directory and read the. Class file into memory

        //2. Call defineclass to convert byte array into class object
        return defineClass(buf, off, len);

//The bytecode array is parsed into a class object and implemented by native method
protected final Class<?> defineClass(byte[] b, int off, int len){

There are three class loaders in JDK. In addition, you can customize class loaders. Their relationship is shown in the following figure.

Analysis of Tomcat Architecture Principle for reference to architecture design

  • BootstrapClassLoaderIs the boot class loader, implemented by C language, used to loadJVMThe core classes required for startup, such asrt.jarresources.jarEtc.
  • ExtClassLoaderIs an extension class loader used to load\jre\lib\extDirectory under the jar package.
  • AppClassLoaderIs the system class loader used to loadclasspathThis is used by the application to load classes by default.
  • The custom class loader is used to load the classes under the custom path.

The working principle of these class loaders is the same, the difference is that their loading paths are different, that is to sayfindClassThis method looks for different paths. The parent delegation mechanism is to ensure that a Java class is unique in the JVM. If you accidentally write a class with the same name as the JRE core class, such asObjectClass, the parent delegation mechanism can ensure that theJREThe one in thereObjectClass, not what you wroteObjectClass. that is becauseAppClassLoaderWhen loading your object class, theExtClassLoaderTo load, andExtClassLoaderWill be entrusted toBootstrapClassLoaderBootstrapClassLoaderI found myself loadedObjectClass, will directly return, will not load you writeObjectClass. We can only get it at mostExtClassLoaderPay attention here.

Tomcat hot load

The essence of Tomcat is to do periodic tasks through a background thread, regularly detect the changes of class files, and reload the classes if there are changes. Let’s seeContainerBackgroundProcessorHow to realize it.

protected class ContainerBackgroundProcessor implements Runnable {

    public void run() {
        //Note that the parameter passed in here is an instance of the host class

    protected void processChildren(Container container) {
        try {
            //1. Call the backgroundprocess method of the current container.

            //2. Traverse all the child containers and call processchildren recursively,
            //In this way, the descendants of the current container will be processed
            Container[] children = container.findChildren();
            for (int i = 0; i < children.length; i++) {
            //Here, please note that there is a variable called backgroundprocessordelay in the container base class. If it is greater than 0, it indicates that the child container has its own background thread, and there is no need for the parent container to call its processchildren method.
                if (children[i].getBackgroundProcessorDelay() <= 0) {
        } catch (Throwable t) { ... }

The hot loading of Tomcat is implemented in the context container, which mainly calls the reload method of the context container. Apart from the details, from the macro point of view, the main tasks are as follows:

  1. Stop and destroy the context container and all its child containers. The child container is actually the wrapper, that is to say, the servlet instance in the wrapper is also destroyed.
  2. Stop and destroy the listener and filter associated with the context container.
  3. Stop and destroy pipeline and various valves under context.
  4. Stop and destroy the class loader of context and the class file resources loaded by the class loader.
  5. Start the context container, during which the resources destroyed in the previous four steps will be recreated.

In this process, the class loader plays a key role. A context container corresponds to a class loader. During the destruction process, the class loader will destroy all the classes it loads. During the startup process of the context container, a new class loader is created to load the new class file.

Class loader of Tomcat

Custom class loader of TomcatWebAppClassLoaderIt breaks the parental delegation mechanism, itFirst of all, try to load a class by yourself. If you can’t find it, then proxy it to the parent class loaderThe purpose is to give priority to loading classes defined by web applications. The concrete implementation is rewritingClassLoaderThere are two ways to do itfindClassandloadClass

Findclass method

org.apache.catalina.loader.WebappClassLoaderBase#findClassTo facilitate understanding and reading, I have removed some details:

public Class<?> findClass(String name) throws ClassNotFoundException {

    Class<?> clazz = null;
    try {
            //1. Find the class in the web application directory first
            clazz = findClassInternal(name);
    }  catch (RuntimeException e) {
           throw e;

    if (clazz == null) {
    try {
            //2. If it is not found in the local directory, send it to the parent loader to find it
            clazz = super.findClass(name);
    }  catch (RuntimeException e) {
           throw e;

    //3. If the parent class is not found, throw classnotfoundexception
    if (clazz == null) {
        throw new ClassNotFoundException(name);

    return clazz;
  1. First, find the class to be loaded in the local directory of the web application.
  2. If it is not found, the parent loader is the system class loader mentioned aboveAppClassLoader
  3. How can the parent loader not find this classClassNotFoundAbnormal.
Loadclass method

Let’s look at the Tomcat class loaderloadClassMethod implementation, I also removed some details:

public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {

    synchronized (getClassLoadingLock(name)) {

        Class<?> clazz = null;

        //1. Check whether the class has been loaded in the local cache
        clazz = findLoadedClass0(name);
        if (clazz != null) {
            if (resolve)
            return clazz;

        //2. Find out whether it has been loaded from the cache of the system class loader
        clazz = findLoadedClass(name);
        if (clazz != null) {
            if (resolve)
            return clazz;

        //3. Try to load with extclassloader class. Why?
        ClassLoader javaseLoader = getJavaseClassLoader();
        try {
            clazz = javaseLoader.loadClass(name);
            if (clazz != null) {
                if (resolve)
                return clazz;
        } catch (ClassNotFoundException e) {
            // Ignore

        //4. Try to search the local directory for class and load it
        try {
            clazz = findClass(name);
            if (clazz != null) {
                if (resolve)
                return clazz;
        } catch (ClassNotFoundException e) {
            // Ignore

        //5. Try to use the system class loader (i.e. appclassloader) to load
            try {
                clazz = Class.forName(name, false, parent);
                if (clazz != null) {
                    if (resolve)
                    return clazz;
            } catch (ClassNotFoundException e) {
                // Ignore

    //6. All the above processes fail to load and throw an exception
    throw new ClassNotFoundException(name);

There are six main steps

  1. First, find out whether the class has been loaded in the local cache, that is, whether the Tomcat class loader has loaded the class.
  2. If the Tomcat class loader has not loaded this class, then check whether the system class loader has.
  3. If not, let itExtClassLoaderTo load, this step is more critical, purposeTo prevent web application from covering core classes of JRE。 Because Tomcat needs to break the parent delegation mechanism. If a class called object is defined in the web application, if the object class is loaded first, the object class in the JRE will be overridden. This is why the Tomcat class loader will give priority to using itExtClassLoaderTo load, becauseExtClassLoaderWill be entrusted toBootstrapClassLoaderTo load,BootstrapClassLoaderIf you find that you have loaded the object class, you can directly return it to the Tomcat class loader. In this way, the Tomcat class loader will not load the object class under the web application, and the problem of covering the JRE core class will be avoided.
  4. IfExtClassLoaderLoader failed to load, that is to sayJREIf there is no such class in the core class, find and load it in the local web application directory.
  5. If there is no such class in the local directory, it means that it is not a class defined by the web application itself, and then it is loaded by the system class loader. Notice here that web applications areClass.forNameCall to the system class loader becauseClass.forNameThe default loader for is the system class loader.
  6. If all of the above loading processes fail, theClassNotFoundAbnormal.

Tomcat class loader hierarchy

Tomcat asServletContainer, which is responsible for loading ourServletClass, which is also responsible for loadingServletThe dependent jar package. alsoTomcatIt is also a java program, so it needs to load its own classes and dependent jar packages. First of all, let’s think about these questions

  1. If we run two web applications in tomcat, there are two web applications with the same nameServletBut the functions are different. Tomcat needs to load and manage these two namesakeServletClasses to ensure that they do not conflict, so classes between web applications need to be isolated.
  2. Suppose both web applications rely on the same third-party jar package, such asSpringThatSpringAfter the jar package is loaded into memory,TomcatTo ensure that these two web applications can be shared, that is to saySpringThe jar package is only loaded once, otherwise, as the number of dependent third-party jar packages increases,JVMThe memory will inflate.
  3. Like the JVM, we need to isolate Tomcat’s own classes from those of the web application.

Analysis of Tomcat Architecture Principle for reference to architecture design

1. WebAppClassLoader

Tomcat’s solution is to customize a class loaderWebAppClassLoaderAnd create a class loader instance for each web application. We know that the context container component corresponds to a web application, so eachContextThe container is responsible for creating and maintaining aWebAppClassLoaderLoader instance. The principle behind this is,Classes loaded by different loader instances are considered to be different classesEven if they have the same class name. This is equivalent to creating isolated Java class spaces in the Java virtual machine. Each web application has its own class space. Web applications are isolated from each other through their own class loaders.


The essential requirement is how to share library classes between two web applications, and the same classes cannot be loaded repeatedly. In the parent delegation mechanism, all child loaders can load classes through the parent loader, so it’s OK to put the shared classes in the loading path of the parent loader.

So the designer of Tomcat added a class loaderSharedClassLoader, asWebAppClassLoaderTo load classes shared between web applications. IfWebAppClassLoaderIf you don’t load it into a class, you delegate to the parent loaderSharedClassLoaderTo load this class,SharedClassLoaderThe shared class will be loaded in the specified directory, and then returned toWebAppClassLoaderIn this way, the problem of sharing is solved.

3. CatalinaClassloader

How to isolate Tomcat’s own classes from those of web applications?

Sharing can be done through parent-child relationship, and isolation requires brotherhood. Sibling relationship means that two class loaders are parallel. They may have the same parent loader. Based on this, Tomcat designs another class loaderCatalinaClassloaderTo load Tomcat’s own classes.

There is a problem with this design. What should we do when we need to share some classes between Tomcat and various web applications?

The old way is to add another oneCommonClassLoader, asCatalinaClassloaderandSharedClassLoaderThe parent loader of.CommonClassLoaderAny class that can be loaded can beCatalinaClassLoaderandSharedClassLoaderuse

Overall architecture design analysis and harvest summary

Through the study of the overall architecture of tomcat, we know what core components Tomcat has and the relationship between components. And how Tomcat handles an HTTP request. Let’s review it through a simplified class diagram. From the diagram, you can see the hierarchical relationship of various components. The dotted lines in the diagram indicate the flow process of a request in Tomcat.

Analysis of Tomcat Architecture Principle for reference to architecture design


The overall architecture of Tomcat includes two core components, connector and container. The connector is responsible for external communication, and the container is responsible for internal processing. For connectorProtocolHandlerInterface to encapsulate communication protocols andI/OThe difference between models,ProtocolHandlerThe interior is divided intoEndPointandProcessormodular,EndPointResponsible for the bottom floorSocketsignal communication,ProccesorResponsible for application layer protocol analysis. Connector through adapterAdapterCall the container.

By studying the overall architecture of tomcat, we can get some basic ideas for designing complex systems.First of all, we need to analyze the requirements, determine the sub modules according to the principle of high cohesion and low coupling, and then find out the change points and invariant points in the sub modules, encapsulate the invariant points with interfaces and abstract base classes, define template methods in the abstract base classes, and let the subclass implement the abstract methods, that is, the concrete subclasses to realize the change points.


UsedCombined pattern management container, release start event through observer mode to achieve decoupling, open and close principle. Skeleton abstract class and template method Abstract change and unchanged, the change is handed over to subclass implementation, so as to realize code reuse and flexible expansion。 Use the chain of responsibility to process requests, such as logging.

Class loader

Custom class loader of TomcatWebAppClassLoaderIn order to isolate web applications, it breaks the two parent delegation mechanismFirst of all, try to load a class by yourself. If you can’t find it, then proxy it to the parent class loaderThe purpose is to give priority to loading classes defined by web applications.To prevent web application from covering core classes of JRE, usingExtClassLoaderTo load, which not only breaks the parent delegation, but also can load safely.

How to read the source code for continuous learning

Learning is an anti human process, which is quite painful。 In particular, learning the excellent technical framework that we often use is relatively large, and the design is relatively complex. In the early stage of learning, it is easy to encounter “frustration”. Debug jumps around and falls into the horror details, and often gives up.

It is very important to find a suitable learning method. The same key is to keep learning interest and motivation, and get learning feedback effect

Learning excellent source code, what we gain is the ability of architecture design. When we meet complex requirements, we learn that we can use reasonable patterns and component abstraction to design scalable code capabilities.

How to read

For example, when I first studied the spring framework, I got into a module at the beginning. However, because spring is too large and there are connections between modules, I don’t know why I should write it like this. I just think why it is designed so “around”.

Wrong way

  • Not looking at the detailsBefore I knew what the forest looked like, I looked at the leavesWe can’t see the whole picture and the overall design idea. Therefore, when reading the source code, do not enter the details at the beginning, but look at the overall architecture design ideas and the relationship between modules.
  • Research on how to design before learning how to use it: first of all, the framework basically uses design patterns. At the very least, we should understand the commonly used design patterns. Even if it is “back”, we must know them clearly. In learning a technology, I recommend reading the official documents first to see what modules and overall design ideas are. Then download the sample run again, and finally see the source code.
  • Look at the source code and study the details: when it comes to the source code of a specific module, we should subconsciously not go deep into the details. The important thing is to learn the design ideas, not to implement the logic of a specific method. Unless you want to do secondary development based on the source code.

The right way

  • Focus principle: grasp the main line (grasp a core process to analyze, do not read aimlessly everywhere).
  • Macro Thinking: from the overall perspective, God’s perspective to sort out the main core architecture design, forest first, then leaves. Don’t try to understand every line of code.
  • Breakpoint: reasonable use of call stack (observe call process context).

Learn with purpose

For example, some knowledge points are hot spots in the interview, and the learning goal is to thoroughly understand and master it. When asked the relevant questions, your answer can make the interviewer look at you with a new look. Sometimes, with a certain bright spot, the final employment result can be affected.

Or you get a slightly more complex requirement,Learn from excellent source design ideas and optimization skills.

Finally, practiceApply what you have learned to work projects. Only hands-on practice will let us have the most intuitive feeling of technology. Sometimes when we listen to other people’s experiences and theories, we seem to understand them, but after a while we forget them.

Actual scene application

This paper briefly analyzes the overall architecture design of tomcat, from connector to container, and details the design ideas and design patterns of some components. The next step is how to apply what you have learned and applied it to practical work.Learning starts with imitation.

Responsibility chain model

In the work, there is such a requirement that users can input some information and select to check the business information, judicial information, zhongdeng information and other modules of the enterprise as shown below. Moreover, there are some public things among the modules that need to be reused by each module.

This is like a request, which is processed by multiple modules. So each query module can be abstracted asHandling valvesTo save these valves with a list, we only need to add one new modulevalveThat’s itOpen close principleAt the same time, it decouples a lot of codes to different specific valves, using abstract class extraction“Unchanging”Function.

Analysis of Tomcat Architecture Principle for reference to architecture design

The specific example code is as follows:

First, we abstract our processing valves,NetCheckDTOIt’s a request for information

 *Chain of responsibility model: handle each module valve
public interface Valve {
     * @param netCheckDTO
    void invoke(NetCheckDTO netCheckDTO);

Define abstract base class and reuse code.

public abstract class AbstractCheckValve implements Valve {
    public final AnalysisReportLogDO getLatestHistoryData(NetCheckDTO netCheckDTO, NetCheckDataTypeEnum checkDataTypeEnum){
        //Get history, omit code logic

    //Get audit data source configuration
    public final String getModuleSource(String querySource, ModuleEnum moduleEnum){
       //Omit code logic

Define the business logic of each module, such as the corresponding processing of Baidu negative news

public class BaiduNegativeValve extends AbstractCheckValve {
    public void invoke(NetCheckDTO netCheckDTO) {


Finally, the management user selects the module to check, and we save it through list. Used to trigger the required verification module

public class NetCheckService {
    //Fill all valves
    private Map<String, Valve> valveMap;

     *Send inspection request
     * @param netCheckDTO
    public void sendCheckRequest(NetCheckDTO netCheckDTO) {
        //Used to save the module valve selected by the customer
        List<Valve> valves = new ArrayList<>();

        CheckModuleConfigDTO checkModuleConfig = netCheckDTO.getCheckModuleConfig();
        //Add the user selected module to the valve chain
        if (checkModuleConfig.getBaiduNegative()) {
        //Omit part of the code
        if (CollectionUtils.isEmpty(valves)) {
   ("the web search and inspection module is empty, there is no task to be checked");
        //Trigger processing
        valves.forEach(valve -> valve.invoke(netCheckDTO));

Template method pattern

The requirement is that financial report analysis can be performed according to the Excel data or enterprise name entered by customers.

For unlisted companies, analyze excel, verify whether the data is legal, and perform calculation.

Listed enterprises: judge whether the name exists or not. If it does not, send e-mail and abort the calculation → pull financial report data from the database, initialize the inspection log, generate a report record, trigger the calculation, and modify the task status according to the failure and success.

Analysis of Tomcat Architecture Principle for reference to architecture design

Important “change” and “invariance”,

  • unchangedThe whole process isInitialize the audit log and a reportPre calibration data(if the listed company fails to pass the verification, it is also necessary to build and send the email data), pull the financial report data from different sources and adapt the general data, and then trigger the calculation. The status of task exception and success needs to be modified.
  • changeWhat’s more, the verification rules of listing and unlisted are different, and the methods of obtaining financial report data are different, so the financial report data of the two methods need to be adapted

The whole algorithm process is a fixed template, but it needs toThe internal variation of the algorithmIt is the best way to implement different sub patterns in different scenarios.

public abstract class AbstractAnalysisTemplate {
     *Submit financial report analysis template method and define skeleton process
     * @param reportAnalysisRequest
     * @return
    public final FinancialAnalysisResultDTO doProcess(FinancialReportAnalysisRequest reportAnalysisRequest) {
        FinancialAnalysisResultDTO analysisDTO = new FinancialAnalysisResultDTO();
        //Abstract method: validation of submitted verification
        boolean prepareValidate = prepareValidate(reportAnalysisRequest, analysisDTO); ("preparevalidate validation result = {}", preparevalidate);
        if (!prepareValidate) {
            //Abstract method: data needed to build notification mail
   ("build mail information, data = {}", JSON.toJSONString (analysisDTO));
            return analysisDTO;
        String reportNo = FINANCIAL_REPORT_NO_PREFIX + reportAnalysisRequest.getUserId() + SerialNumGenerator.getFixLenthSerialNumber();
        //Generate analysis log
        initFinancialAnalysisLog(reportAnalysisRequest, reportNo);
        //Generate analysis record
        initAnalysisReport(reportAnalysisRequest, reportNo);

        try {
            //Abstract method: pull financial report data, different subclass implementation
            FinancialDataDTO financialData = pullFinancialData(reportAnalysisRequest);
   ("pull financial report data complete, ready to perform calculation");
            //Measurement index
            financialCalcContext.calc(reportAnalysisRequest, financialData, reportNo);
            //Set analysis log to success
        } catch (Exception e) {
            log.error (the sub task of financial report calculation is abnormal, e);
            //Failed to set analysis log
            throw e;
        return analysisDTO;

Finally, two new subclasses are created to inherit the template and implement the abstract method. In this way, the listing and unlisted processing logic is decoupled, and the code is reused.

Strategy model

It is assumed that the fields of transaction account No. and payee’s general account No. and payee’s daily account No. and payee’s name are included in this way. Now we parse out the subscript of the excel header where each necessary field is located. But there are many kinds of flowing water

  1. One is to include all standard fields.
  2. The index of income and expenditure is the same column, which distinguishes income and expenditure by positive and negative.
  3. Revenue and expense are the same column, and there is a field of transaction type to distinguish.
  4. Special treatment for special banks.

That’s what we wantFind the corresponding processing logic algorithm according to the corresponding subscriptWe may write too many in one methodif elseIf there is a new type of pipeline in the future, we should continue to change the old code. Finally, code complexity that is “stinky, long, and hard to maintain” may occur.

We can use it at this timeStrategy modelThe pipeline of different templates is processed by different processors, and the corresponding policy algorithm is found according to the template。 Even if we add another type of processor in the future, we only need to add a new processor, which has high cohesion and low coupling, and can be expanded.

Analysis of Tomcat Architecture Principle for reference to architecture design

Define the processor interface, different processors to implement the processing logic. Inject all processors intoBankFlowDataHandlerOfdata_processor_mapAccording to different scenarios, the existing processor processing pipeline is extracted.

public interface DataProcessor {
     *Processing flow data
     *@ param bankflowtemplatedo pipelining data
     * @param row
     * @return
    BankTransactionFlowDO doProcess(BankFlowTemplateDO bankFlowTemplateDO, List<String> row);

     *Whether to support processing the template or not, different types of pipeline policies determine whether to support parsing according to the template data
     * @return
    boolean isSupport(BankFlowTemplateDO bankFlowTemplateDO);

//The context of the processor
public class BankFlowDataContext {
    //Inject all processors into the map
    private List<DataProcessor> processors;

    //Find the corresponding processor processing pipeline
    public void process() {
         DataProcessor processor = getProcessor(bankFlowTemplateDO);
           for(DataProcessor processor : processors) {
           if (processor.isSupport(bankFlowTemplateDO)) {
             //Row is a row of data
                 processor.doProcess(bankFlowTemplateDO, row);



Define the default processor to process the normal template, and add the template as long as the new processor is implementedDataProcessorThat’s fine.

 *Default processor: facing specification pipelining template
public class DefaultDataProcessor implements DataProcessor {

    public BankTransactionFlowDO doProcess(BankFlowTemplateDO bankFlowTemplateDO) {
        //Omit processing logic details
        return bankTransactionFlowDO;

    public String strategy(BankFlowTemplateDO bankFlowTemplateDO) {
      //Omit to determine whether the pipeline supports parsing
      boolean isDefault = true;

      return isDefault;

Through the strategy pattern, we assign different processing logic to different processing classes, which is completely decoupled and easy to expand.

Debugging source code by using embedded Tomcat: GitHub:…

Due to the space limitation, the comprehensive examples of how to use Tomcat’s design ideas to practical development will be explained in the next chapter. This article is full of dry goods. It is suggested that we should review it after collection. We also hope that readers can “like”, “share” and “watch” three times are the greatest encouragement.

Backstage reply “add group” enter exclusive technology group to grow together

Analysis of Tomcat Architecture Principle for reference to architecture design

Recommended Today

Java security framework

The article is mainly divided into three parts1. The architecture and core components of spring security are as follows: (1) authentication; (2) authority interception; (3) database management; (4) authority caching; (5) custom decision making; and;2. To build and use the environment, the current popular spring boot is used to build the environment, and the actual […]