SpringBoot源码解读与原理分析(二十七)嵌入式Tomcat

前言

当Web应用需要部署运行时,传统的做法是将项目打包成war包,然后部署到外置的Web容器中(如最常用的Tomcat容器)。SpringBoot的一大重要特性是支持嵌入式Web容器,基于SpringBoot的Web应用仅凭一个单独的jar包即可独立运行。

8.1 嵌入式Tomcat简介

8.1.1 嵌入式Tomcat与普通Tomcat

嵌入式Tomcat是一种可以嵌入到Web应用中,无需单独部署的Tomcat容器。

普通的外置Tomcat与嵌入式Tomcat从核心、本质上看没有任何区别,都可以承载Web应用的运行。

但SpringBoot整合嵌入式Tomcat容器时在底层设定了一些额外的限制。

  • 部署应用的限制:由于嵌入式Tomcat不是独立的Web容器,而是嵌入到特定的Web应用中的,因此该嵌入式Tomcat容器只能部署这一个特定的Web应用。
  • web.xml的限制:SpringBoot整合嵌入式Tomcat后不再对web.xml文件予以支持。
  • Servlet原生三大组件的限制:原生的基于Servlet 3.0及以上规范的Web项目,其类路径下的Servlet、Filter、Listener可以被自动扫描并注册,而SpringBoot整合嵌入式Tomcat后该特性失效。如果需要开启该特性,需要配合@ServletConponentScan注解使用。
  • JSP的限制:SpringBoot整合嵌入式Tomcat后,如果以独立jar包的方式启动项目(java -jar),则JSP页面会失效;如果以war包的方式部署到外置的Tomcat容器,则JSP页面可以正常运行。

8.1.2 Tomcat整体架构

Tomcat整体架构
图片来源于CSDN文章:Tomcat.02结构图&启动&server.xml&连接器

从架构图可以得出Tomcat的架构设计如下:

  • 一个Tomcat服务器是一个Server;
  • 一个Server包含多个服务Service,其中提供默认HTTP请求响应服务的是Catalina;
  • 一个Service包含多个Connector,用于与客户端交互,实现接收客户端请求并转发到Engine和接收Engine响应结果并响应给客户端;
  • 一个Service还包含一个Container Engine,用于真正处理客户端的请求,并响应结果;
  • 一个Container Engine包含多个Host,每个Host可以装在多个Web应用;
  • 一个Web应用对应一个Context,一个Context包含多个Servlet。

8.1.3 Tomcat的核心工作流程

Tomcat作为一个Web服务器,其核心工作是接收客户端发起的HTTP请求,转发给服务器端的Web应用处理,处理完成后将结果响应给客户端。

其工作流程大致如下:

1、请求进入Tomcat容器,Tomcat容器内部根据请求URL,判断该请求应该由哪个应用来处理,并将请求封装为ServletRequest对象,转发至对应的Web应用中的Context。
2、Context接收到ServletRequest对象后,根据请求URI定位可以接收当前请求的Servlet,并将请求转发给具体的Servlet进行处理。
3、在转发到Servlet之前,容器会检查对应的Servlet是否已加载,如果没有加载,则会利用反射机制创建Servlet对象,并调用其init方法完成初始化,之后再进行逻辑处理。
4、Servlet处理完成后,将响应结果以ServletResponse对应响应给Service中的Connector,由Connector响应给客户端,至此完成一次请求处理。

8.2 SpringBoot中嵌入式容器的模型

SpringBoot支持的嵌入式容器包括Tomcat、Jetty、Undertow、Netty等。

8.2.1 WebServer

代码清单1WebServer.java|TomcatWebServer.java

public interface WebServer {
   
    void start() throws WebServerException;
    void stop() throws WebServerException;
    // ...
}

public class TomcatWebServer implements WebServer {
   
    private final Tomcat tomcat;
    // ...
}

由 代码清单1 可知,WebServer是SpringBoot针对所有嵌入式Web容器制定的顶级接口,定义了嵌入式Web容器的启动和停止动作。

实现WebServer接口的实现类通常会在内部组合一个真正的嵌入式容器,如TomcatWebServer中包含一个Tomcat对象,并重写start方法实现嵌入式Web容器的启动逻辑,重写stop方法实现嵌入式Web容器的停止和销毁逻辑。

8.2.2 WebServerFactory

代码清单2WebServerFactory.java

public interface WebServerFactory {
   
}

由 代码清单2 可知,WebServerFactory接口没有定义任何方法,仅为标记性接口

WebServerFactory是所有具备创建WebServer能力的工厂对象的根接口。

代码清单3ConfigurableWebServerFactory.java

public interface ConfigurableWebServerFactory extends WebServerFactory, ErrorPageRegistry {
   
    void setPort(int port);
    void setSsl(Ssl ssl);
    // ...
    // 优雅停机
    default void setShutdown(Shutdown shutdown) {
   
	}
}

由 代码清单3 可知,ConfigurableWebServerFactory是WebServerFactory的扩展接口,具备对WebServerFactory的配置能力(包括配置端口、SSL等)。

值得注意的是,嵌入式Web容器具有优雅停机的特性,即容器在关闭时不直接终止进程,而是预留一些时间使容器内部的业务线程全部处理完毕后才关停容器服务。

8.2.3 ServletWebServerFactory和ReactiveWebServerFactory

代码清单4ServletWebServerFactory.java|ReactiveWebServerFactory.java

public interface ServletWebServerFactory {
   
    WebServer getWebServer(ServletContextInitializer... initializers);
}

public interface ReactiveWebServerFactory {
   
    WebServer getWebServer(HttpHandler httpHandler);
}

由 代码清单4 可知,这是两个平级接口,分别是Servlet和Reactive场景下的嵌入式容器创建工厂。getWebServer方法的定义仅是入参不同(不同类型的Web容器在创建时传入的初始化组件不同),但都返回WebServer对象。

8.2.4 ConfigurableServletWebServerFactory

代码清单5ConfigurableServletWebServerFactory.java

public interface ConfigurableServletWebServerFactory extends ConfigurableWebServerFactory, ServletWebServerFactory {
   
    void setContextPath(String contextPath);
    void setInitializers(List<? extends ServletContextInitializer> initializers);
    void addInitializers(ServletContextInitializer... initializers);
    void setInitParameters(Map<String, String> initParameters);
    // ...
}

由 代码清单5 可知,ConfigurableServletWebServerFactory是ConfigurableWebServerFactory和ServletWebServerFactory组合后产生的子接口,具备更多能力,如可以设置访问Web应用所需的context-path、设置ServletContextInitializer、设置初始化参数等。

8.3 嵌入式Tomcat的初始化

SpringBoot源码解读与原理分析(二十三)IOC容器的刷新(四) 中提到,IOC容器刷新的第9步的onRefresh方法是一个模板方法,需要子类实现。

ServletWebServerApplicationContext类就是其中一个子类,重写了onRefresh方法,用于嵌入式Web容器的初始化。

代码清单6ServletWebServerApplicationContext.java

@Override
protected void onRefresh() {
   
    super.onRefresh();
    try {
   
        createWebServer();
    } // catch ...
}

private void createWebServer() {
   
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    // 如果WebServer和ServletContext均为null,则需要创建嵌入式Web容器
    if (webServer == null && servletContext == null) {
   
        // 获取WebServerFactory
        ServletWebServerFactory factory = getWebServerFactory();
        // 创建WebServer
        this.webServer = factory.getWebServer(getSelfInitializer());
        // 回调优雅停机的钩子
        getBeanFactory().registerSingleton("webServerGracefulShutdown",
                new WebServerGracefulShutdownLifecycle(this.webServer));
        // 回调容器启停的生命周期钩子
        getBeanFactory().registerSingleton("webServerStartStop",
                new WebServerStartStopLifecycle(this, this.webServer));
    } else if (servletContext != null) {
   
        try {
   
            getSelfInitializer().onStartup(servletContext);
        } catch (ServletException ex) {
   
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    initPropertySources();
}

protected ServletWebServerFactory getWebServerFactory() {
   
    String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
    // ...
    return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}

由 代码清单6 可知,createWebServer方法中包含WebServer的创建,还有两个与生命周期回调相关的钩子。

8.3.1 创建TomcatWebServer

由 代码清单6 可知,创建嵌入式Web容器的入口是ServletWebServerFactory,其中嵌入式Tomcat容器的创建在其实现类TomcatServletWebServerFactory的getWebServer方法中实现。

代码清单7TomcatServletWebServerFactory.java

public static final String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol";
private String protocol = DEFAULT_PROTOCOL;
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
   
    // ...
    Tomcat tomcat = new Tomcat();
    // 给嵌入式Tomcat创建一个临时文件夹,用于存放Tomcat运行中需要的文件
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    // Connector中默认放入的protocol为NIO模式
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    // 向Service中添加Connector,并执行定制规则
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    // 关闭热部署(因为嵌入式Tomcat不存在修改web.xml、war包等情况)
    tomcat.getHost().setAutoDeploy(false);
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
   
        tomcat.getService().addConnector(additionalConnector);
    }
    // 生成TomcatEmbeddedContext上下文
    prepareContext(tomcat.getHost(), initializers);
    // 创建TomcatWebServer
    return getTomcatWebServer(tomcat);
}

由 代码清单7 可知,获取到WebServerFactory后,下一步会执行getWebServer方法创建嵌入式Tomcat。

该方法核心步骤大致分为三步:创建Tomcat对象,并初始化基础的Connector和Engine;prepareContext方法初始化Context,构建应用上下文;getTomcatWebServer方法创建最终的TomcatWebServer对象。

8.3.1.1 prepareContext
代码清单8TomcatServletWebServerFactory.java

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
   
    File documentRoot = getValidDocumentRoot();
    // 创建Context
    TomcatEmbeddedContext context = new TomcatEmbeddedContext();
    // ...
    // 配置生命周期监听器等
    // ...
    // 应用ServletContextInitializer
    ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
    // Context添加到Host中
    host.addChild(context);
    configureContext(context, initializersToUse);
    postProcessContext(context);
}

由 代码清单8 可知,prepareContext方法的环节包括创建Tomcat内置上下文对象、配置生命周期监听器、应用ServletContextInitializer等。

  • ServletContextInitializer的设计

ServletContextInitializer本身并不是Servlet相关规范中定义的API,它是SpringBoot 1.4.0以后定义的API接口,这也说明ServletContextInitializer与某个具体的Web容器没有任何关系

借助IDEA,可以发现ServletContextInitializer的其中一个实现类RegistrationBean,而它又是Servlet三大核心组件的注册实现类ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean的父类。

如此来看,Servlet三大核心组件应会通过ServletContextInitializer与嵌入式Tomcat对接。

代码清单9TomcatServletWebServerFactory.java

protected void configureContext(Context context, ServletContextInitializer[] initializers) {
   
    TomcatStarter starter = new TomcatStarter(initializers);
    if (context instanceof TomcatEmbeddedContext) {
   
        TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
        embeddedContext.setStarter(starter);
        embeddedContext.setFailCtxIfServletStartFails(true);
    }
    context.addServletContainerInitializer(starter, NO_CLASSES);
    // ...
}

由 代码清单8、9 可知,prepareContext方法倒数第2行有对ServletContextInitializer的配置应用,即调用configureContext方法,该方法会在内部实例化一个TomcatStarter对象,并加载到Servlet容器中。

代码清单10TomcatStarter.java

class TomcatStarter implements ServletContainerInitializer {
   
    private final ServletContextInitializer[] initializers;
    @Override
    public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
   
        try {
   
            for (ServletContextInitializer initializer : this.initializers) {
   
                initializer.onStartup(servletContext);
            }
        } // catch ...
    }
}

由 代码清单10 可知,TomcatStarter对象本身也是一个ServletContainerInitializer,被Servlet容器加载后,调用其onStartup方法。在该方法内部,会循环调用所有ServletContextInitializer的onStartup方法,由此完成Servlet原生三大核心组件的注册。

8.3.1.2 getTomcatWebServer

prepareContext方法执行完后,会执行getTomcatWebServer方法,以创建TomcatWebServer对象。

代码清单11TomcatServletWebServerFactory.java

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
   
    return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}
代码清单12TomcatWebServer.java

public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
   
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    this.tomcat = tomcat;
    this.autoStart = autoStart;
    this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
    initialize();
}

由 代码清单11、12 可知,TomcatWebServer的初始化逻辑在其initialize方法中。

(1)获取Context
代码清单13TomcatWebServer.java

private void initialize() throws WebServerException {
   
    // ...
    //(1)获取第一个可用的Context
    Context context = findContext();
    //...
}

private Context findContext() {
   
    for (Container child : this.tomcat.getHost().findChildren()) {
   
        if (child instanceof Context) {
   
            return (Context) child;
        }
    }
    throw new IllegalStateException("The host does not contain a Context");
}

由 代码清单13 可知,initialize方法的第一个核心动作获取Tomcat中第一个可用的Context,即调用findContext方法。findContext方法会从Host中获取第一个类型为Context的子元素并返回。

实际上,这里获取到的就是在prepareContext方法中创建的TomcatEmbeddedContext。

(2)阻止Connector初始化
代码清单14TomcatWebServer.java

private void initialize() throws WebServerException {
   
    // ...
    //(2)添加LifecycleListener,移除Connector
    context.addLifecycleListener((event) -> {
   
        if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
   
            // javadoc:移除Service中的Connector,那么当服务启动后协议绑定就不会发生。
            removeServiceConnectors();
        }
    });
    // ...
}

由 代码清单14 可知,initialize方法的第二个核心动作是添加一个LifecycleListener,该监听器的功能是移除Service中的Connector,即阻止Connector初始化。

在 代码清单7 中,getWebServer方法创建了Connector并放入了Service中,为什么这里要移除掉呢?

那是因为,创建嵌入式Web容器的时机是在IOC容器刷新的第9步,早于第11步的 初始化所有剩下的单实例finishBeanFactoryInitialization方法,此时IOC容器中绝大多数单实例bean对象尚未初始化,还不具备提供服务的能力。

Connector的功能是与客户端交互,一旦Connector初始化完成,意味着Tomcat可以对外提供服务,即客户端可以成功访问到Tomcat服务。

为了访问客户端成功访问到暂时无法提供服务的Tomcat服务,需要先将Connector移除。

(3)启动Tomcat
代码清单15TomcatWebServer.java

private void initialize() throws WebServerException {
   
    // ...
    //(3)启动Tomcat
    this.tomcat.start();
    // ...
}

由 代码清单15 可知,initialize方法的第三个核心动作是启动Tomcat。

代码清单16Tomcat.java

public void start() throws LifecycleException {
   
    getServer();
    server.start();
}

public Server getServer() {
   
    // ...
    server = new StandardServer();
    // ...
    // 端口设置为-1,代表这是嵌入式Tomcat
    server.setPort( -1 );
    Service service = new StandardService();
    service.setName("Tomcat");
    server.addService(service);
    return server;
}
代码清单17LifecycleBase.java

@Override
public final synchronized void start() throws LifecycleException {
   
    // 前置判断 ...
    
    // 初始化
    if (state.equals(LifecycleState.NEW)) {
   
        init();
    } // else if ...

    try {
   
        setStateInternal(LifecycleState.STARTING_PREP, null, false);
        // 启动自身
        startInternal();
        // ...
    } // catch ...
}

由 代码清单16、17 可知,Tomcat引导Server初始化和启动时,会在获取到Server后调用其start方法,而start方法定义在所有Tomcat核心组件的共同父类LifecycleBase上。

值得注意的是,获取Server时端口号被设置为-1,代表这是嵌入式Tomcat,方便后续使用。

代码清单18LifecycleBase.java

@Override
public final synchronized void init() throws LifecycleException {
   
    if (!state.equals(LifecycleState.NEW)) {
   
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }

    try {
   
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        initInternal();
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    } // catch ...
}

protected abstract void initInternal() throws LifecycleException;

由 代码清单18 可知,init方法依然由父类LifecycleBase定义,其中间的initInternal方法是一个模板方法,由子类实现。

代码清单19StandardServer.java

@Override
protected void initInternal() throws LifecycleException {
   
    super.initInternal();
    // ...
    for (Service service : services) {
   
        service.init();
    }
}

由 代码清单19 可知,StandardServer类实现了initInternal方法,在该方法中会触发Service的初始化。

通过查阅源码发现,service.init()内部会触发Connector的初始化,以及后续所有核心组件的初始化逻辑都大致相同。完整的逻辑可以通过下面这张时序图体现出来:

在这里插入图片描述

(4)阻止Tomcat结束
代码清单20TomcatWebServer.java

private void initialize() throws WebServerException {
   
    // ...
    //(4)阻止Tomcat结束
    startDaemonAwaitThread();
    // ...
}

private void startDaemonAwaitThread() {
   
    // 创建一个新线程
    Thread awaitThread = new Thread("container-" + (containerCounter.get())) {
   
        @Override
        public void run() {
   
            TomcatWebServer.this.tomcat.getServer().await();
        }
    };
    awaitThread.setContextClassLoader(getClass().getClassLoader());
    // 将该线程设置为非守护进程
    awaitThread.setDaemon(false);
    awaitThread.start();
}

由 代码清单20 可知,initialize方法的第四个核心动作是启动一个新的awaitThread线程,以阻止Tomcat进程结束,其内部实现的run方法是回调Server的await方法。

  • Daemon(守护)线程

在一个Java应用中,只要有一个非Daemon线程在运行,Daemon线程就不会停止,整个应用也不会终止。

如果Tomcat需要一直运行以接收客户端请求,就必须让Tomcat内部的Daemon进程都存活,至少需要一个能阻止Tomcat进程停止的非Daemon进程,而这里创建的awaitThread进程,将其Daemon设置为false,就是要负责阻止Tomcat进程停止。

  • await方法
代码清单21StandardServer.java

public void await() {
   
    // -2 时的处理 ...
    
    // 如果关闭Tomcat的端口是-1,代表是嵌入式Tomcat
    if (getPortWithOffset() == -1) {
   
        try {
   
            awaitThread = Thread.currentThread();
            while(!stopAwait) {
   
                try {
   
                    Thread.sleep( 10000 );
                } catch( InterruptedException ex ) {
   
                    // continue and check the flag
                }
            }
        } finally {
   
            awaitThread = null;
        }
        return;
    }
    
    // 退出端口的其他处理 ...
}

由 代码清单21 可知,如果关闭Tomcat的端口是-1,代表是嵌入式Tomcat(详见(3)启动Tomcat)。阻塞Tomcat进程结束的方式是每隔10s检查一次stopAwait的值,只要该值一直为false,Tomcat就不会退出。

······

经过上述一系列核心组件的初始化和启动,嵌入式Tomcat容器初始化完成。但此时Tomcat还不能提供服务,因为Connector在该阶段被移除,无法与客户端建立有效连接。

8.3.2 Web容器关闭相关的回调

由 代码清单6 可知,createWebServer方法中还有两个与生命周期回调相关的钩子。

8.3.2.1 WebServerGracefulShutdownLifecycle
代码清单22WebServerGracefulShutdownLifecycle.java

class WebServerGracefulShutdownLifecycle implements SmartLifecycle {
   
    private final WebServer webServer;
    WebServerGracefulShutdownLifecycle(WebServer webServer) {
   
        this.webServer = webServer;
	}
    @Override
    public void stop(Runnable callback) {
   
        this.running = false;
        this.webServer.shutDownGracefully((result) -> callback.run());
    }
}

由 代码清单22 可知,WebServerGracefulShutdownLifecycle用于触发嵌入式Web容器优雅停机的核心生命周期回调,它实现了SmartLifecycle接口,可以在IOC容器销毁阶段回调其stop方法以触发销毁逻辑。

stop方法会回调WebServer的shutDownGracefully方法实现优雅停机。

8.3.2.2 WebServerStartStopLifecycle
代码清单23WebServerStartStopLifecycle.java

class WebServerStartStopLifecycle implements SmartLifecycle {
   
    private final WebServer webServer;
    WebServerStartStopLifecycle(WebServer webServer) {
   
        this.webServer = webServer;
	}
    @Override
    public void start() {
   
        this.webServer.start();
        this.running = true;
        this.applicationContext
            .publishEvent(new ServletWebServerInitializedEvent(this.webServer, this.applicationContext));
    }
    
    @Override
    public void stop() {
   
        this.webServer.stop();
    }
}

由 代码清单23 可知,WebServerStartStopLifecycle的作用启动和关闭嵌入式Web容器,它会在IOC容器刷新即将完成/销毁时被回调,从而回调WebServer的start/stop方法,真正启动/关闭嵌入式Web容器。

8.4 嵌入式Tomcat的启动

onRefresh方法执行完毕,后续的finishBeanFactoryInitialization方法执行完毕,IOC容器中所有非延迟加载的单实例bean对象均初始化完毕,此时会执行IOC容器刷新的第12步finishRefresh方法,该方法中会回调所有的SmartLifeCycle,其中就包括 8.3.2.2 中的WebServerStartStopLifecycle,它会在该阶段回调嵌入式Web容器的start方法,从而真正启动Web容器。

代码清单24WebServerManager.java

void start() {
   
    this.handler.initializeHandler();
    this.webServer.start();
    this.applicationContext
        .publishEvent(new ReactiveWebServerInitializedEvent(this.webServer, this.applicationContext));
}
代码清单25TomcatWebServer.java

public void start() throws WebServerException {
   
    synchronized (this.monitor) {
   
        if (this.started) {
   
            return;
        }
        try {
   
            // 还原、启动Connector
            addPreviouslyRemovedConnectors();
            Connector connector = this.tomcat.getConnector();
            if (connector != null && this.autoStart) {
   
                performDeferredLoadOnStartup();
            }
            checkThatConnectorsHaveStarted();
            this.started = true;
            logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '"
                    + getContextPath() + "'");
        } // catch ...
    }
}

private void addPreviouslyRemovedConnectors() {
   
    Service[] services = this.tomcat.getServer().findServices();
    for (Service service : services) {
   
        // 之前移除的Connector在serviceConnectors中
        Connector[] connectors = this.serviceConnectors.get(service);
        if (connectors != null) {
   
            for (Connector connector : connectors) {
   
                // 添加并启动Connector
                service.addConnector(connector);
                if (!this.autoStart) {
   
                    stopProtocolHandler(connector);
                }
            }
            this.serviceConnectors.remove(service);
        }
    }
}

由 代码清单24、25 可知,进入TomcatWebServer的start方法后,会调用addPreviouslyRemovedConnectors方法以还原并启动之前被移除掉的Connector(启动逻辑也在addConnector方法中)。

至此,嵌入式Tomcat完整启动。

8.5 小结

第8章到此就梳理完毕了,本章的主题是:嵌入式Web容器。回顾一下本章的梳理的内容:

(二十七)嵌入式Tomcat容器

更多内容请查阅分类专栏:SpringBoot源码解读与原理分析

第9章主要梳理:AOP模块的生命周期。主要内容包括:

  • AOP的核心后置处理器AnnotationAwareAspectJAutoProxyCreator;
  • AOP底层收集切面类的机制;
  • Bean被AOP代理的过程原理;
  • 代理对象执行的全流程分析。

最近更新

  1. TCP协议是安全的吗?

    2024-02-22 03:16:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-22 03:16:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-22 03:16:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-22 03:16:01       18 阅读

热门阅读

  1. 炫技亮点 优雅处理数据流程 过滤器模式

    2024-02-22 03:16:01       28 阅读
  2. 类和对象 下(再谈构造函数 static成员 友元)

    2024-02-22 03:16:01       32 阅读
  3. 【Linux 内核源码分析】内存管理——Slab 分配器

    2024-02-22 03:16:01       29 阅读
  4. C++面试高频问题汇总( 一)

    2024-02-22 03:16:01       36 阅读
  5. gtowizard合租cash和锦标赛mtt

    2024-02-22 03:16:01       26 阅读
  6. 前端常见面试题

    2024-02-22 03:16:01       26 阅读
  7. Qt 基本知识

    2024-02-22 03:16:01       28 阅读
  8. webrtc 中 FIR PLI 有何区别? 分别适用于什么场景

    2024-02-22 03:16:01       32 阅读
  9. vue 学习definproperty方法

    2024-02-22 03:16:01       32 阅读
  10. Gradio学习(二)—————学习block布局

    2024-02-22 03:16:01       27 阅读
  11. SpringBoot-将Bean放入容器的五种方式

    2024-02-22 03:16:01       30 阅读
  12. Sora 使用教程,新手小白可用

    2024-02-22 03:16:01       40 阅读
  13. win32 汇编调用C标准库函数

    2024-02-22 03:16:01       30 阅读
  14. SpringBoot+WebSocket实现即时通讯(一)

    2024-02-22 03:16:01       29 阅读