SpringBoot内嵌的Tomcat启动过程以及请求

1.springboot内嵌的tomcat的pom坐标

 启动后可以看到tomcat版本为9.0.46

 2.springboot 内嵌tomcat启动流程

点击进入SpringApplication.run()方法里面

看这次tomcat启动相关的核心代码refreshContext(context);刷新上下文方法

public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;configureHeadlessProperty();SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);configureIgnoreBeanInfo(environment);Banner printedBanner = printBanner(environment);context = createApplicationContext();prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 此次的核心代码是刷新上下文方法refreshContext(context);afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}listeners.started(context);callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}return context;
}

进入 私有方法refreshContext

private void refreshContext(ConfigurableApplicationContext context) {if (this.registerShutdownHook) {try {context.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.}}// 核心方法,继续进入refresh((ApplicationContext) context);
}
@Deprecated
protected void refresh(ApplicationContext applicationContext) {Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);// 核心代码,继续进入方法refresh((ConfigurableApplicationContext) applicationContext);
}

进入refresh方法

protected void refresh(ConfigurableApplicationContext applicationContext) {// 核心方法,查看调用的方法refresh()applicationContext.refresh();
}

查看refresh方法,有三个实现类,进入默认的实现类ServletWebServerApplicationContext,定位到实现方法

@Override
public final void refresh() throws BeansException, IllegalStateException {try {// 进入父类refreshsuper.refresh();}catch (RuntimeException ex) {WebServer webServer = this.webServer;if (webServer != null) {webServer.stop();}throw ex;}
}

 看onRefresh()方法

@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// 我们进入这个方法// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}
}

 找到子类的实现方法:

@Override
protected void onRefresh() {super.onRefresh();try {// 核心代码,创建web服务器createWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}
}

进入createWebServer方法,ServletContext、WebServer在tomcat启动的时候一定为空,还没有初始化完成,会进入if代码块执行

private void createWebServer() {WebServer webServer = this.webServer;// tomcat启动servletContext才进行创建ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {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();
}

 getWebServer方法需要参数,该参数是一个方法getSelfInitializer(),找到

private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {// 方法引用,ServletContextInitializer是一个函数是接口,通过方法引用返回一个实现        // ServletContextInitializer的一个实现子类,作用同匿名内部类。return this::selfInitialize;
}

函数式接口:

@FunctionalInterface
public interface ServletContextInitializer {/*** Configure the given {@link ServletContext} with any servlets, filters, listeners* context-params and attributes necessary for initialization.* @param servletContext the {@code ServletContext} to initialize* @throws ServletException if any call against the given {@code ServletContext}* throws a {@code ServletException}*/void onStartup(ServletContext servletContext) throws ServletException;}

this::selfInitialize就是实例::方法名调用方式,this代表当前对象,selfInitialize是this(ServletWebServerApplicationContext)对象的一个私有方法

private void selfInitialize(ServletContext servletContext) throws ServletException {prepareWebApplicationContext(servletContext);registerApplicationScope(servletContext);WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);for (ServletContextInitializer beans : getServletContextInitializerBeans()) {beans.onStartup(servletContext);}
}

继续进入getWebServer,发现是一个接口,有三个实现类Jetty、Tomcat、UnderTow,我们是Tomcat服务器,进入TomcatServletWebServerFactory中的实现方法

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();}Tomcat tomcat = new Tomcat();File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");// 用于设置临时文件的目录,这些目录用于存放JSP生成的源代码及Class文件xtomcat.setBaseDir(baseDir.getAbsolutePath());Connector connector = new Connector(this.protocol);connector.setThrowOnFailure(true);tomcat.getService().addConnector(connector);customizeConnector(connector);// 用于设置链接器,包括协议、I/0、端口、压缩、加密等等tomcat.setConnector(connector);// 是否自动部署tomcat.getHost().setAutoDeploy(false);configureEngine(tomcat.getEngine());for (Connector additionalConnector : this.additionalTomcatConnectors) {tomcat.getService().addConnector(additionalConnector);}prepareContext(tomcat.getHost(), initializers);// 核心代码return getTomcatWebServer(tomcat);
}
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}

进入TomcatWebServer构造方法

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();
}

 进入initialize()方法

private void initialize() throws WebServerException {logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));synchronized (this.monitor) {try {addInstanceIdToEngineName();Context context = findContext();context.addLifecycleListener((event) -> {if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {// Remove service connectors so that protocol binding doesn't// happen when the service is started.removeServiceConnectors();}});// 核心代码// Start the server to trigger initialization listenersthis.tomcat.start();// We can re-throw failure exception directly in the main threadrethrowDeferredStartupExceptions();try {ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());}catch (NamingException ex) {// Naming is not enabled. Continue}// Unlike Jetty, all Tomcat threads are daemon threads. We create a// blocking non-daemon to stop immediate shutdownstartDaemonAwaitThread();}catch (Exception ex) {stopSilently();destroySilently();throw new WebServerException("Unable to start embedded Tomcat", ex);}}
}
public void start() throws LifecycleException {getServer();// 核心代码server.start();
}

进入start()方法,找到LifecycleBase实现类方法

 LifecycleBase类中init();startInternal();两个核心方法

 @Override
public final synchronized void start() throws LifecycleException {if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||LifecycleState.STARTED.equals(state)) {if (log.isDebugEnabled()) {Exception e = new LifecycleException();log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);} else if (log.isInfoEnabled()) {log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));}return;}if (state.equals(LifecycleState.NEW)) {// 核心代码1init();} else if (state.equals(LifecycleState.FAILED)) {stop();} else if (!state.equals(LifecycleState.INITIALIZED) &&!state.equals(LifecycleState.STOPPED)) {invalidTransition(Lifecycle.BEFORE_START_EVENT);}try {setStateInternal(LifecycleState.STARTING_PREP, null, false);// 核心代码2startInternal();if (state.equals(LifecycleState.FAILED)) {// This is a 'controlled' failure. The component put itself into the// FAILED state so call stop() to complete the clean-up.stop();} else if (!state.equals(LifecycleState.STARTING)) {// Shouldn't be necessary but acts as a check that sub-classes are// doing what they are supposed to.invalidTransition(Lifecycle.AFTER_START_EVENT);} else {setStateInternal(LifecycleState.STARTED, null, false);}} catch (Throwable t) {// This is an 'uncontrolled' failure so put the component into the// FAILED state and throw an exception.handleSubClassException(t, "lifecycleBase.startFail", toString());}
}

 init()方法

 @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 (Throwable t) {handleSubClassException(t, "lifecycleBase.initFail", toString());}
}

initInternal有很多实现类,看组件Connector中的实现方法

 组件Connector类型方法

@Override
protected void initInternal() throws LifecycleException {super.initInternal();if (protocolHandler == null) {throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"));}// Initialize adapteradapter = new CoyoteAdapter(this);protocolHandler.setAdapter(adapter);if (service != null) {protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());}// Make sure parseBodyMethodsSet has a defaultif (null == parseBodyMethodsSet) {setParseBodyMethods(getParseBodyMethods());}if (protocolHandler.isAprRequired() && !AprStatus.isInstanceCreated()) {throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprListener",getProtocolHandlerClassName()));}if (protocolHandler.isAprRequired() && !AprStatus.isAprAvailable()) {throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprLibrary",getProtocolHandlerClassName()));}if (AprStatus.isAprAvailable() && AprStatus.getUseOpenSSL() &&protocolHandler instanceof AbstractHttp11JsseProtocol) {AbstractHttp11JsseProtocol<?> jsseProtocolHandler =(AbstractHttp11JsseProtocol<?>) protocolHandler;if (jsseProtocolHandler.isSSLEnabled() &&jsseProtocolHandler.getSslImplementationName() == null) {// OpenSSL is compatible with the JSSE configuration, so use it if APR is availablejsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());}}try {// 核心方法protocolHandler.init();} catch (Exception e) {throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);}
}

进入实现类AbstractProtocol中init()方法

@Override
public void init() throws Exception {if (getLog().isInfoEnabled()) {getLog().info(sm.getString("abstractProtocolHandler.init", getName()));logPortOffset();}if (oname == null) {// Component not pre-registered so register itoname = createObjectName();if (oname != null) {Registry.getRegistry(null, null).registerComponent(this, oname, null);}}if (this.domain != null) {ObjectName rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());this.rgOname = rgOname;Registry.getRegistry(null, null).registerComponent(getHandler().getGlobal(), rgOname, null);}String endpointName = getName();endpoint.setName(endpointName.substring(1, endpointName.length()-1));endpoint.setDomain(domain);// 核心代码endpoint.init();
}

进入endpoint.init()方法

public final void init() throws Exception {if (bindOnInit) {// 核心代码bindWithCleanup();bindState = BindState.BOUND_ON_INIT;}if (this.domain != null) {// Register endpoint (as ThreadPool - historical name)oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");Registry.getRegistry(null, null).registerComponent(this, oname, null);ObjectName socketPropertiesOname = new ObjectName(domain +":type=SocketProperties,name=\"" + getName() + "\"");socketProperties.setObjectName(socketPropertiesOname);Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {registerJmx(sslHostConfig);}}
}

进入bindWithCleanup方法里面 

private void bindWithCleanup() throws Exception {try {// 核心代码bind();} catch (Throwable t) {// Ensure open sockets etc. are cleaned up if something goes// wrong during bindExceptionUtils.handleThrowable(t);unbind();throw t;}
}
@Override
public void bind() throws Exception {// 核心代码initServerSocket();setStopLatch(new CountDownLatch(1));// Initialize SSL if neededinitialiseSsl();selectorPool.open(getName());
}

初始化Socket 

protected void initServerSocket() throws Exception {if (getUseInheritedChannel()) {// Retrieve the channel provided by the OSChannel ic = System.inheritedChannel();if (ic instanceof ServerSocketChannel) {serverSock = (ServerSocketChannel) ic;}if (serverSock == null) {throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));}} else if (getUnixDomainSocketPath() != null) {SocketAddress sa = JreCompat.getInstance().getUnixDomainSocketAddress(getUnixDomainSocketPath());serverSock = JreCompat.getInstance().openUnixDomainServerSocketChannel();serverSock.bind(sa, getAcceptCount());if (getUnixDomainSocketPathPermissions() != null) {Path path = Paths.get(getUnixDomainSocketPath());Set<PosixFilePermission> permissions =PosixFilePermissions.fromString(getUnixDomainSocketPathPermissions());if (path.getFileSystem().supportedFileAttributeViews().contains("posix")) {FileAttribute<Set<PosixFilePermission>> attrs = PosixFilePermissions.asFileAttribute(permissions);Files.setAttribute(path, attrs.name(), attrs.value());} else {java.io.File file = path.toFile();if (permissions.contains(PosixFilePermission.OTHERS_READ) && !file.setReadable(true, false)) {log.warn(sm.getString("endpoint.nio.perms.readFail", file.getPath()));}if (permissions.contains(PosixFilePermission.OTHERS_WRITE) && !file.setWritable(true, false)) {log.warn(sm.getString("endpoint.nio.perms.writeFail", file.getPath()));}}}} else {serverSock = ServerSocketChannel.open();socketProperties.setProperties(serverSock.socket());InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());serverSock.bind(addr, getAcceptCount());}serverSock.configureBlocking(true); //mimic APR behavior
}

startInternal();

看下这个方法,找到Connector里面的startInternal()方法实现

@Override
protected void startInternal() throws LifecycleException {// Validate settings before startingString id = (protocolHandler != null) ? protocolHandler.getId() : null;if (id == null && getPortWithOffset() < 0) {throw new LifecycleException(sm.getString("coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));}setState(LifecycleState.STARTING);try {// 核心代码protocolHandler.start();} catch (Exception e) {throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);}
}

进入protocolHandler.start();方法里面。进入AbstractProtocol里面的实现方法

@Override
public void start() throws Exception {if (getLog().isInfoEnabled()) {getLog().info(sm.getString("abstractProtocolHandler.start", getName()));logPortOffset();}// 核心代码endpoint.start();monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(() -> {if (!isPaused()) {startAsyncTimeout();}}, 0, 60, TimeUnit.SECONDS);
}

找到endpoint.start()方法

public final void start() throws Exception {if (bindState == BindState.UNBOUND) {bindWithCleanup();bindState = BindState.BOUND_ON_START;}// 核心代码startInternal();
}

进入这个实现类里,找到实现方法

 

@Overridepublic void startInternal() throws Exception {if (!running) {running = true;paused = false;if (socketProperties.getProcessorCache() != 0) {processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,socketProperties.getProcessorCache());}if (socketProperties.getEventCache() != 0) {eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,socketProperties.getEventCache());}if (socketProperties.getBufferPool() != 0) {nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,socketProperties.getBufferPool());}// Create worker collectionif (getExecutor() == null) {createExecutor();}initializeConnectionLatch();// 核心代码 *******// Start poller threadpoller = new Poller();Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");// 设置进程的优先级pollerThread.setPriority(threadPriority);// 守护线程:当非守护线程销毁的时候,守护线程跟着销毁。当运行的唯一线程是守护线程时,        // Java虚拟机将退出pollerThread.setDaemon(true);// 开启线程pollerThread.start();startAcceptorThread();}}

Poller类是什么?

Poller里面的run方法

@Override
public void run() {// Loop until destroy() is called// 核心代码1 轮询while (true) {boolean hasEvents = false;try {if (!close) {hasEvents = events();if (wakeupCounter.getAndSet(-1) > 0) {// If we are here, means we have other stuff to do// Do a non blocking selectkeyCount = selector.selectNow();} else {keyCount = selector.select(selectorTimeout);}wakeupCounter.set(0);}if (close) {events();timeout(0, false);try {selector.close();} catch (IOException ioe) {log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);}break;}// Either we timed out or we woke up, process events firstif (keyCount == 0) {hasEvents = (hasEvents | events());}} catch (Throwable x) {ExceptionUtils.handleThrowable(x);log.error(sm.getString("endpoint.nio.selectorLoopError"), x);continue;}Iterator<SelectionKey> iterator =keyCount > 0 ? selector.selectedKeys().iterator() : null;// Walk through the collection of ready keys and dispatch// any active event.while (iterator != null && iterator.hasNext()) {SelectionKey sk = iterator.next();iterator.remove();NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();// Attachment may be null if another thread has called// cancelledKey()if (socketWrapper != null) {// 核心代码2processKey(sk, socketWrapper);}}// Process timeoutstimeout(keyCount,hasEvents);}getStopLatch().countDown();
}

启动一个Poller线程轮询监听NIO接收的请求,直到执行核心代码2processKey(sk, socketWrapper);Poller类会轮询监听Socket连接,到这里tomcat算启动成功啦。

springboot嵌入tomcat处理请求的过程

主要看下Poller类中的processKey方法,我们发起一个请求怎么处理的呢?

processKey方法

protected void processKey(SelectionKey sk, NioSocketWrapper socketWrapper) {try {if (close) {cancelledKey(sk, socketWrapper);} else if (sk.isValid() && socketWrapper != null) {if (sk.isReadable() || sk.isWritable()) {if (socketWrapper.getSendfileData() != null) {processSendfile(sk, socketWrapper, false);} else {unreg(sk, socketWrapper, sk.readyOps());boolean closeSocket = false;// Read goes before writeif (sk.isReadable()) {if (socketWrapper.readOperation != null) {// 核心代码if (!socketWrapper.readOperation.process()) {closeSocket = true;}// 核心代码} else if (!processSocket(socketWrapper, SocketEvent.OPEN_READ, true)) {closeSocket = true;}}if (!closeSocket && sk.isWritable()) {if (socketWrapper.writeOperation != null) {if (!socketWrapper.writeOperation.process()) {closeSocket = true;}} else if (!processSocket(socketWrapper, SocketEvent.OPEN_WRITE, true)) {closeSocket = true;}}if (closeSocket) {cancelledKey(sk, socketWrapper);}}}} else {// Invalid keycancelledKey(sk, socketWrapper);}} catch (CancelledKeyException ckx) {cancelledKey(sk, socketWrapper);} catch (Throwable t) {ExceptionUtils.handleThrowable(t);log.error(sm.getString("endpoint.nio.keyProcessingError"), t);}
}

进入processSocket方法

public boolean processSocket(SocketWrapperBase<S> socketWrapper,SocketEvent event, boolean dispatch) {try {if (socketWrapper == null) {return false;}// 核心代码 线程SocketProcessorBase<S> sc = null;if (processorCache != null) {sc = processorCache.pop();}if (sc == null) {sc = createSocketProcessor(socketWrapper, event);} else {sc.reset(socketWrapper, event);}// 核心代码 获取线程池,通过线程池执行Executor executor = getExecutor();if (dispatch && executor != null) {executor.execute(sc);} else {sc.run();}} catch (RejectedExecutionException ree) {getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);return false;} catch (Throwable t) {ExceptionUtils.handleThrowable(t);// This means we got an OOM or similar creating a thread, or that// the pool and its queue are fullgetLog().error(sm.getString("endpoint.process.fail"), t);return false;}return true;
}

看看SocketProcessBase是什么?  实现了Runnable接口 

看下SocketProcessBase的run()方法

 @Overridepublic final void run() {synchronized (socketWrapper) {// It is possible that processing may be triggered for read and// write at the same time. The sync above makes sure that processing// does not occur in parallel. The test below ensures that if the// first event to be processed results in the socket being closed,// the subsequent events are not processed.if (socketWrapper.isClosed()) {return;}// 核心代码doRun();}}

看下doRun()方法

找到过滤器链ApplicationFilterChain,找到doFilter()方法

@Override
public void doFilter(ServletRequest request, ServletResponse response)throws IOException, ServletException {if( Globals.IS_SECURITY_ENABLED ) {final ServletRequest req = request;final ServletResponse res = response;try {java.security.AccessController.doPrivileged((java.security.PrivilegedExceptionAction<Void>) () -> {// 核心代码internalDoFilter(req,res);return null;});} catch( PrivilegedActionException pe) {Exception e = pe.getException();if (e instanceof ServletException)throw (ServletException) e;else if (e instanceof IOException)throw (IOException) e;else if (e instanceof RuntimeException)throw (RuntimeException) e;elsethrow new ServletException(e.getMessage(), e);}} else {internalDoFilter(request,response);}
}

定位到internalDoFilter()方法

private void internalDoFilter(ServletRequest request,ServletResponse response)throws IOException, ServletException {// Call the next filter if there is oneif (pos < n) {ApplicationFilterConfig filterConfig = filters[pos++];try {Filter filter = filterConfig.getFilter();if (request.isAsyncSupported() && "false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);}if( Globals.IS_SECURITY_ENABLED ) {final ServletRequest req = request;final ServletResponse res = response;Principal principal =((HttpServletRequest) req).getUserPrincipal();Object[] args = new Object[]{req, res, this};SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);} else {filter.doFilter(request, response, this);}} catch (IOException | ServletException | RuntimeException e) {throw e;} catch (Throwable e) {e = ExceptionUtils.unwrapInvocationTargetException(e);ExceptionUtils.handleThrowable(e);throw new ServletException(sm.getString("filterChain.filter"), e);}return;}// We fell off the end of the chain -- call the servlet instancetry {if (ApplicationDispatcher.WRAP_SAME_OBJECT) {lastServicedRequest.set(request);lastServicedResponse.set(response);}if (request.isAsyncSupported() && !servletSupportsAsync) {request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,Boolean.FALSE);}// Use potentially wrapped request from this pointif ((request instanceof HttpServletRequest) &&(response instanceof HttpServletResponse) &&Globals.IS_SECURITY_ENABLED ) {final ServletRequest req = request;final ServletResponse res = response;Principal principal =((HttpServletRequest) req).getUserPrincipal();Object[] args = new Object[]{req, res};SecurityUtil.doAsPrivilege("service",servlet,classTypeUsedInService,args,principal);} else {// 核心代码servlet.service(request, response);}} catch (IOException | ServletException | RuntimeException e) {throw e;} catch (Throwable e) {e = ExceptionUtils.unwrapInvocationTargetException(e);ExceptionUtils.handleThrowable(e);throw new ServletException(sm.getString("filterChain.servlet"), e);} finally {if (ApplicationDispatcher.WRAP_SAME_OBJECT) {lastServicedRequest.set(null);lastServicedResponse.set(null);}}
}

定位到service()方法的实现类HttpServlet中的service()方法中

@Overridepublic void service(ServletRequest req, ServletResponse res)throws ServletException, IOException {HttpServletRequest  request;HttpServletResponse response;try {request = (HttpServletRequest) req;response = (HttpServletResponse) res;} catch (ClassCastException e) {throw new ServletException(lStrings.getString("http.non_http"));}// 核心代码service(request, response);}

继续进入到service()方法中

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {long startTime = System.currentTimeMillis();Throwable failureCause = null;LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();LocaleContext localeContext = buildLocaleContext(request);RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());initContextHolders(request, localeContext, requestAttributes);try {// 核心代码doService(request, response);}catch (ServletException | IOException ex) {failureCause = ex;throw ex;}catch (Throwable ex) {failureCause = ex;throw new NestedServletException("Request processing failed", ex);}finally {resetContextHolders(request, previousLocaleContext, previousAttributes);if (requestAttributes != null) {requestAttributes.requestCompleted();}logResult(request, response, failureCause, asyncManager);publishRequestHandledEvent(request, response, startTime, failureCause);}
}

 进入doService()方法

 https://blog.csdn.net/z69183787/article/details/129240218
https://blog.csdn.net/sunshinezx8023/article/details/128630710

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/231826.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

kubesphere和k8s的使用分享

文章目录 什么是kubernetesKubernetes的部分核心概念互式可视化管理平台与kubernetes的关系市面是常见的kubernetes管理平台 什么是kubesphereKubesphere默认安装的组件Kubesphere涉及的服务组件kubesphere的安装Kubesphere相关的内容 什么是kubernetes 就在这场因“容器”而起…

2024 AIGC应用层十大趋势:AI Agent将成为AI应用主流形态

ITValue 钉钉这类平台型应用加生态&#xff0c;成为大模型产业居中而立的一个桥梁。 钛媒体作者&#xff5c;张帅 ITValue 2023年的科技产业喧嚣且骚动&#xff0c;AIGC蔓延到一切领域&#xff0c;产业各方仿佛要拿出所有的精力和资源&#xff0c;生怕错过这一班通向未来的快车…

MYSQL多种提权方式

&#x1f419;MYSQL-提权条件 - 数据库的最高权限用户的密码 - secure-file-priv没进行目录限制 - 拿下了网站的权限&#xff08;通过webshell或者其他方式&#xff09; - 获取到了数据库的账号密码 &#xff08;获取密码&#xff1a;D:/phpstudy/MySQL/data/mysql/user.MYD…

es简单入门

星光下的赶路人star的个人主页 努力努力再努力 文章目录 1、简介2、使用场景3、基本知识4、中文文档和官网链接5、增删改查&#xff08;php代码&#xff09;6、基本查询7、HTTP操作7.1 索引操作7.1.1 创建索引 7.2 文档操作7.2.1 创建文档7.2.2 查看文档7.2.3 修改文档7.2.4 修…

MySQL-数据库概述

数据库相关概念&#xff1a; 数据库(DateBase)简称DB,就是一个存储数据的仓库&#xff0c;数据有组织的进行存储。 数据库分为关系型数据库简称RDBMS和非关系型数据库 关系型数据库简称RDBMS:建立在关系模型的基础上&#xff0c;由多张相互连接的二维表组成的数据库.简单来说…

音频播放软件Foobar2000 mac特点介绍

Foobar2000 mac是一款高度可定制的音频播放器&#xff0c;适用于Windows平台。它支持各种音频格式&#xff0c;包括MP3、FLAC、AAC、WMA等&#xff0c;同时也支持各种音频插件和效果器&#xff0c;可以提供更好的音质和用户体验。 Foobar2000 mac软件特点 1. 高度可定制&#…

洛谷——P1983 [NOIP2013 普及组] 车站分级(拓扑排序、c++)

文章目录 一、题目[NOIP2013 普及组] 车站分级题目背景题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 样例 #2样例输入 #2样例输出 #2 提示 二、题解基本思路&#xff1a;代码 一、题目 [NOIP2013 普及组] 车站分级 题目背景 NOIP2013 普及组 T4 题目描述 一条单…

免费的GPT4来了,你还不知道吗?

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

Maven(mvn)的学习下载和配置

文章目录 Maven&#xff08;mvn&#xff09;1.Maven 是什么&#xff1f;2.Maven做什么&#xff1f;2.1传统方式对项目的管理2.2Maven对jar包的管理 3.Maven怎么学3.1Maven如何创建项目3.2Maven的下载与配置3.3Maven的项目结构3.4Maven依赖的引入3.5Maven依赖的剔除3.6Maven依赖…

2023 hnust 湖南科技大学 大四上 计算机图形图像技术 课程 期末考试 复习资料

计算机图形图像技术复习资料 前言 改编自&#xff1a;https://blog.csdn.net/Liu_Xin233/article/details/135232531★重点&#xff0c;※补充github 考试题型 简述题&#xff08;10分4题&#xff0c;共40分&#xff09; 第1章的基本内容三维观察流水线中的基本概念与理解三…

【面试高频算法解析】算法练习6 广度优先搜索

前言 本专栏旨在通过分类学习算法&#xff0c;使您能够牢固掌握不同算法的理论要点。通过策略性地练习精选的经典题目&#xff0c;帮助您深度理解每种算法&#xff0c;避免出现刷了很多算法题&#xff0c;还是一知半解的状态 专栏导航 二分查找回溯&#xff08;Backtracking&…

【Python机器学习】k近邻——模型复杂度与泛化能力的关系

以某数据进行研究&#xff0c;先将数据集分为训练集和测试集&#xff0c;然后用不同的邻居数对训练集合测试集的新能进行评估&#xff1a; from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split from sklearn.neighbors imp…

日程安排小程序实战教程

日常中我们经常有一些事情需要提醒自己&#xff0c;使用日历的形式比较符合实际的使用习惯。本篇我们就利用微搭低代码工具带着大家开发一款日程安排的小程序。 1 创建数据源 登录微搭低代码控制台&#xff0c;打开数据模型&#xff0c;点击创建 输入数据源的名称日程安排 …

记录第一次在GitHub上面提交Issue

第一次在GitHub上面提交Issue&#xff0c;记录一下。 对着源码调了好久才发现&#xff0c;问题并不在程序而在模型&#xff08;虽然只是一个很小的问题&#xff0c;但是能够解决问题&#xff0c;并且做出了自己的一点小小贡献&#xff0c;还是很开心。嘻嘻&#xff0c;发博客记…

BART论文解读:BERT和GPT结合起来会发生什么?

BART:Denoising Sequence-to-Sequence Pre-training for Natural Language Generation, Translation, and Comprehension 主要工作 提出了BART (Bidirectional and Auto-Regressive Transformers)&#xff0c; 是一种用于自然语言生成、翻译和理解的序列到序列的预训练方法。它…

stable diffusion 基础教程-提示词之光的用法

基图 prompt: masterpiece,best quality,1girl,solo,looking at viewer,brown hair,hair between eyes,bangs,very long hair,red eyes,blush,bare shoulders,(white sundress),full body,Negative prompt: EasyNegative,badhandv4,nsfw,lowres,bad anatomy,bad hands,text…

【DevOps-07-2】Sonarqube基本使用

一、简要说明 Sonar Qube的使用方式很多&#xff0c;Maven可以整合&#xff0c;也可以采用sonar-scanner的方式&#xff0c;再查看Sonar Qube的检测效果 Sonarqube集成在Maven实现代码检测使用sonar-scanner客户端的方式 二、Sonarqube管理后台安装中文插件 1、登录Sonarqube管…

数据结构和算法-插入排序(算法效率 折半优化 顺序表与链表插入排序 代码实现)

文章目录 插入排序算法实现算法效率分析优化-折半插入排序代码实现对链表进行插入排序小结 插入排序 首先49当作第一个已经排好序得元素&#xff0c;将第二个元素与前面得元素对比&#xff0c;发现小于49&#xff0c;于是49移动位置 此时将65与之前元素对比&#xff0c;发现其…

C语言编译器(C语言编程软件)完全攻略(第二部分:与编译器相关的几个知识点)

介绍常用C语言编译器的安装、配置和使用。 二、与编译器相关的几个知识点 上节我们介绍了编译器和 IDE 的概念&#xff0c;大家肯定希望赶紧实践一下&#xff0c;用 IDE 真正地运行一段C语言代码来看看效果&#xff0c;这样能够更快地获得成就感。 但是&#xff0c;使用 IDE …

Linux第20步_在虚拟机上安装“Visual Studio Code”

1、双击windows系统桌面上的“FileZilla Client.exe”&#xff0c;打开FTP客户端&#xff0c;点击03软件下的Visual Studio Code&#xff0c;发现code_1.50.1-1602600906_amd64。 2、点击“文件”&#xff0c;然后点击“站点管理器”&#xff0c;见下图操作&#xff1a; 3、点…