前言 说到 Java Web 就是 Tomcat,在后面学习 Java 无文件 WebShell 之前先来观摩一下 Tomcat 源码。
主要看看 filter、servlet 等。
catalina.sh 启动 Tomcat 的方法之一:./catalina.sh start,在脚本中执行的启动如下:
1 2 3 4 5 6 7 eval exec "\"$_RUNJAVA \"" "\"$CATALINA_LOGGING_CONFIG \"" $LOGGING_MANAGER "$JAVA_OPTS " "$CATALINA_OPTS " \ -D$ENDORSED_PROP ="\"$JAVA_ENDORSED_DIRS \"" \ -classpath "\"$CLASSPATH \"" \ -Dcatalina.base="\"$CATALINA_BASE \"" \ -Dcatalina.home="\"$CATALINA_HOME \"" \ -Djava.io.tmpdir="\"$CATALINA_TMPDIR \"" \ org.apache.catalina.startup.Bootstrap "$@ " start
通过 ps 查看到的完整命令如下:
1 2 3 4 5 6 7 8 9 10 11 12 /usr/lib/jvm/jdk1.8.0_191/jre/bin/java -Djava.util.logging.config.file=/opt/apache-tomcat-9.0.12/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath /opt/apache-tomcat-9.0.12/bin/bootstrap.jar:/opt/apache-tomcat-9.0.12/bin/tomcat-juli.jar -Dcatalina.base=/opt/apache-tomcat-9.0.12 -Dcatalina.home=/opt/apache-tomcat-9.0.12 -Djava.io.tmpdir=/opt/apache-tomcat-9.0.12/temp org.apache.catalina.startup.Bootstrap start
这里的 classpath 主要是两个 jar:
1 -classpath /opt/apache-tomcat-9.0.12/bin/bootstrap.jar:/opt/apache-tomcat-9.0.12/bin/tomcat-juli.jar
以 start 为参数调用了 org.apache.catalina.startup.Bootstrap 类。
load/init 这个类在 bootstrap.jar 中,静态代码块主要是设置各种环境变量,就不提了,看下 main 函数,前一半代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static void main (String[] args) { synchronized (daemonLock) { if (daemon == null ) { Bootstrap bootstrap = new Bootstrap (); try { bootstrap.init(); } catch (Throwable var5) { ... } daemon = bootstrap; } else { Thread.currentThread().setContextClassLoader(daemon.catalinaLoader); } } ... }
首先设置线程的环境,初始化环境的代码在 Bootstrap 的 init 函数中,前一半如下:
1 2 3 4 5 6 7 8 9 10 public void init () throws Exception { this .initClassLoaders(); Thread.currentThread().setContextClassLoader(this .catalinaLoader); SecurityClassLoad.securityClassLoad(this .catalinaLoader); if (log.isDebugEnabled()) { log.debug("Loading startup class" ); } ... }
首先初始化了类加载器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private void initClassLoaders () { try { this .commonLoader = this .createClassLoader("common" , (ClassLoader)null ); if (this .commonLoader == null ) { this .commonLoader = this .getClass().getClassLoader(); } this .catalinaLoader = this .createClassLoader("server" , this .commonLoader); this .sharedLoader = this .createClassLoader("shared" , this .commonLoader); } catch (Throwable var2) { handleThrowable(var2); log.error("Class loader creation threw exception" , var2); System.exit(1 ); } }
可以看到这里会创建三个类加载器,其中 catalinaLoader 和 sharedLoader 是 commonLoader 的子类加载器,createClassLoader 函数中的创建代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 private ClassLoader createClassLoader (String name, ClassLoader parent) throws Exception { String value = CatalinaProperties.getProperty(name + ".loader" ); if (value != null && !value.equals("" )) { value = this .replace(value); List<Repository> repositories = new ArrayList (); String[] repositoryPaths = getPaths(value); String[] var6 = repositoryPaths; int var7 = repositoryPaths.length; for (int var8 = 0 ; var8 < var7; ++var8) { String repository = var6[var8]; try { new URL (repository); repositories.add(new Repository (repository, RepositoryType.URL)); } catch (MalformedURLException var11) { if (repository.endsWith("*.jar" )) { repository = repository.substring(0 , repository.length() - "*.jar" .length()); repositories.add(new Repository (repository, RepositoryType.GLOB)); } else if (repository.endsWith(".jar" )) { repositories.add(new Repository (repository, RepositoryType.JAR)); } else { repositories.add(new Repository (repository, RepositoryType.DIR)); } } } return ClassLoaderFactory.createClassLoader(repositories, parent); } else { return parent; } }
简单来说,就是从配置中获取加载类的路径然后创建类加载器,观察 CatalinaProperties 类的 getProperty 和 loadProperties 函数,可以看到配置来自 /org/apache/catalina/startup/catalina.properties:
1 2 3 common.loader = "${catalina.base} /lib" ,"${catalina.base} /lib/*.jar" ,"${catalina.home} /lib" ,"${catalina.home} /lib/*.jar" server.loader = shared.loader =
可以看到,commonLoader 类加载器的加载路径是 Tomcat 的 lib 目录,而 catalinaLoader 和 sharedLoader 的加载路径为空,也就是说他们的值都是 commonLoader。
创建完类加载器后, 会加载一个 org.apache.catalina.startup.Catalina 类,实例化并将 sharedLoader,即 commonLoader 放入其中:
1 2 3 4 5 6 7 8 9 10 11 12 Class<?> startupClass = this .catalinaLoader.loadClass("org.apache.catalina.startup.Catalina" );Object startupInstance = startupClass.getConstructor().newInstance();if (log.isDebugEnabled()) { log.debug("Setting startup class properties" ); }String methodName = "setParentClassLoader" ; Class<?>[] paramTypes = new Class []{Class.forName("java.lang.ClassLoader" )}; Object[] paramValues = new Object []{this .sharedLoader};Method method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues);this .catalinaDaemon = startupInstance;
回到 main 函数,start 命令会执行的代码如下:
1 2 3 4 5 6 7 8 else if (command.equals("start" )) { daemon.setAwait(true ); daemon.load(args); daemon.start(); if (null == daemon.getServer()) { System.exit(1 ); } }
setAwait 只是给一个布尔属性赋值,跳过它看 load 函数。
调用的 load 函数来自 org.apache.catalina.startup.Catalina 类:
1 2 3 4 5 6 7 8 9 10 public void load () { if (!this .loaded) { this .loaded = true ; long t1 = System.nanoTime(); this .initDirs(); this .initNaming(); this .parseServerXml(true ); ... } }
此时还没有加载,所以 loaded 为 false,前面的环境变量设置(时间、目录等)跳过,后面的 parseServerXml 函数一看就是跟加载 web 配置文件相关的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 protected void parseServerXml (boolean start) { ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource (Bootstrap.getCatalinaBaseFile(), this .getConfigFile())); File file = this .configFile(); File serverXmlLocation = null ; if (this .generateCode) { ... } Catalina.ServerXml serverXml = null ; if (this .useGeneratedCode) { ... } if (serverXml != null ) { serverXml.load(this ); } else { try { Resource resource = ConfigFileLoader.getSource().getServerXml(); Throwable var6 = null ; try { Digester digester = start ? this .createStartDigester() : this .createStopDigester(); InputStream inputStream = resource.getInputStream(); InputSource inputSource = new InputSource (resource.getURI().toURL().toString()); inputSource.setByteStream(inputStream); digester.push(this ); if (this .generateCode) { ... } digester.parse(inputSource); if (this .generateCode) { ... } } ... } ... } }
实例化了一个 Digester XML 解析器,并将自身 Catalina 对象放入栈中,用于后面保存解析过程中生成的对象。
配置文件来自 CatalinaBaseConfigurationSource 类,使用 ConfigFileLoader.getSource().getServerXml() 获取配置文件资源:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public Resource getServerXml () throws IOException { IOException ioe = null ; Resource result = null ; try { if (this .serverXmlPath != null && !this .serverXmlPath.equals("conf/server.xml" )) { result = this .getResource(this .serverXmlPath); } else { result = super .getServerXml(); } } catch (IOException var6) { ioe = var6; } if (result == null ) { ... } if (result == null && ioe != null ) { throw ioe; } else { return result; } }
this.serverXmlPath 来自实例化时传入的 this.getConfigFile(),即 Catalina 类的 configFile 属性:
1 protected String configFile = "conf/server.xml" ;
获取配置文件资源后,然后开始使用 Digester 解析 xml(参考文章 ),这是个匹配 -> 回调感觉的解析器,详细的规则可以在 createStartDigester 函数中看到,比如这三句:
1 2 3 digester.addObjectCreate("Server" , "org.apache.catalina.core.StandardServer" , "className" ); digester.addSetProperties("Server" ); digester.addSetNext("Server" , "setServer" , "org.apache.catalina.Server" );
这里的规则匹配 Server 标签,addObjectCreate 规则在没有 className 属性的情况下默认实例化一个 org.apache.catalina.core.StandardServer 类并放入栈中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public void begin (String namespace, String name, Attributes attributes) throws Exception { String realClassName = this .getRealClassName(attributes); if (realClassName == null ) { throw new NullPointerException (sm.getString("rule.noClassName" , new Object []{namespace, name})); } else { Class<?> clazz = this .digester.getClassLoader().loadClass(realClassName); Object instance = clazz.getConstructor().newInstance(); this .digester.push(instance); ... } }protected String getRealClassName (Attributes attributes) { String realClassName = this .className; if (this .attributeName != null ) { String value = attributes.getValue(this .attributeName); if (value != null ) { realClassName = value; } } return realClassName; }
addSetProperties 规则用其他属性给 StandardServer 类赋值,跳过。
addSetNext 规则将 StandardServer 对象作为参数,调用 Catalina 对象的 setServer 函数将 StandardServer 保存起来。
解析完 server.xml 之后,Tomcat 服务端对象已经创建好并按层次保存在 Catalina 对象的 server 属性中了,回到 load 函数,接下来的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Server s = this .getServer();if (s != null ) { this .getServer().setCatalina(this ); this .getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); this .getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); this .initStreams(); try { this .getServer().init(); } catch (LifecycleException var5) { ... } if (log.isInfoEnabled()) { log.info(...); } }
调用 this.getServer().init() 开始初始化服务端,看了一下各个模块类的 initInternal 函数,好像没什么特别的,那就直接回到 main 函数里面 daemon.start() 这一句吧。
start 观察 server.xml 中的配置,可以发现一个 Service 就是一个 Web 服务,所以我们关注 Service 的启动,Service 的配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <Service name ="Catalina" > <Connector port ="8080" protocol ="HTTP/1.1" connectionTimeout ="20000" redirectPort ="8443" /> <Engine name ="Catalina" defaultHost ="localhost" > <Realm className ="org.apache.catalina.realm.LockOutRealm" > <Realm className ="org.apache.catalina.realm.UserDatabaseRealm" resourceName ="UserDatabase" /> </Realm > <Host name ="localhost" appBase ="webapps" unpackWARs ="true" autoDeploy ="true" > <Valve className ="org.apache.catalina.valves.AccessLogValve" directory ="logs" prefix ="localhost_access_log" suffix =".txt" pattern ="%h %l %u %t " %r" %s %b" /> </Host > </Engine > </Service >
观察里面的配置项,可以看出 Connector 应该用于处理 HTTP 协议,而 Host 部分应该用于处理 HTTP 请求。
跟 init 类似,start 主要调用的是 startInternal 函数,Service 的 startInternal 函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 protected void startInternal () throws LifecycleException { if (log.isInfoEnabled()) { log.info(sm.getString("standardService.start.name" , new Object []{this .name})); } this .setState(LifecycleState.STARTING); if (this .engine != null ) { synchronized (this .engine) { this .engine.start(); } } synchronized (this .executors) { ... } this .mapperListener.start(); synchronized (this .connectorsLock) { Connector[] var10 = this .connectors; int var11 = var10.length; for (int var4 = 0 ; var4 < var11; ++var4) { Connector connector = var10[var4]; if (connector.getState() != LifecycleState.FAILED) { connector.start(); } } } }
因为配置文件中没有 executors,所以就跳过了,简单来说就是启动 engine、mapperListener 和 connector,我们先看看 engine:
1 2 3 4 5 6 7 protected synchronized void startInternal () throws LifecycleException { if (log.isInfoEnabled()) { log.info(sm.getString("standardEngine.start" , new Object []{ServerInfo.getServerInfo()})); } super .startInternal(); }
engine 的启动没什么特别的,再看看 mapperListener:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public void startInternal () throws LifecycleException { this .setState(LifecycleState.STARTING); Engine engine = this .service.getContainer(); if (engine != null ) { this .findDefaultHost(); this .addListeners(engine); Container[] conHosts = engine.findChildren(); Container[] var3 = conHosts; int var4 = conHosts.length; for (int var5 = 0 ; var5 < var4; ++var5) { Container conHost = var3[var5]; Host host = (Host)conHost; if (!LifecycleState.NEW.equals(host.getState())) { this .registerHost(host); } } } }
看代码中的命名,engine 应该就是我们在 Tomcat 中常常看到的 container。
mapperListener 启动时首先调用 findDefaultHost:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 private void findDefaultHost () { Engine engine = this .service.getContainer(); String defaultHost = engine.getDefaultHost(); boolean found = false ; if (defaultHost != null && defaultHost.length() > 0 ) { Container[] containers = engine.findChildren(); Container[] var5 = containers; int var6 = containers.length; for (int var7 = 0 ; var7 < var6; ++var7) { Container container = var5[var7]; Host host = (Host)container; if (defaultHost.equalsIgnoreCase(host.getName())) { found = true ; break ; } String[] aliases = host.findAliases(); String[] var11 = aliases; int var12 = aliases.length; for (int var13 = 0 ; var13 < var12; ++var13) { String alias = var11[var13]; if (defaultHost.equalsIgnoreCase(alias)) { found = true ; break ; } } } } if (found) { this .mapper.setDefaultHostName(defaultHost); } else { log.error(sm.getString("mapperListener.unknownDefaultHost" , new Object []{defaultHost, this .service})); } }
可以看到,只有在 engine 的 children 中找到一个其 name 和 defaultHost 相对应的,才会给 this.mapper 赋值。
engine 的 children 是通过 addChild 添加的,具体的解析规则可以到 HostRuleSet 类中找到:
1 2 3 4 digester.addObjectCreate(this .prefix + "Host" , "org.apache.catalina.core.StandardHost" , "className" ); digester.addSetProperties(this .prefix + "Host" ); ... digester.addSetNext(this .prefix + "Host" , "addChild" , "org.apache.catalina.Container" );
简单来说它的 children 就是 Host,因为 Host 中配置的 name 和 engine 中的 defaultHost 相同,所以会将 defaultHost 赋给 mapper。
findDefaultHost 结束后,调用 addListeners 将自身这个 MapperListener 对象放入 Host 及 Host 的 children 中(默认配置时应该为空)。
最后调用 registerHost,将 Host 注册进 mapper 中(感觉 MapperListener 跟 Host 互相套娃的)。
回到 Service,下一步执行的是 connector 的 start:
1 2 3 4 5 6 7 8 9 10 11 12 13 protected void startInternal () throws LifecycleException { if (this .getPortWithOffset() < 0 ) { throw new LifecycleException (sm.getString("coyoteConnector.invalidPort" , new Object []{this .getPortWithOffset()})); } else { this .setState(LifecycleState.STARTING); try { this .protocolHandler.start(); } catch (Exception var2) { throw new LifecycleException (sm.getString("coyoteConnector.protocolHandlerStartFailed" ), var2); } } }
这里启动了 protocolHandler,看名字就是用于处理协议的类,是在 connector 的构造函数中初始化的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ProtocolHandler p = null ;try { p = ProtocolHandler.create(protocol, apr); } catch (Exception var5) { log.error(sm.getString("coyoteConnector.protocolHandlerInstantiationFailed" ), var5); }if (p != null ) { this .protocolHandler = p; this .protocolHandlerClassName = this .protocolHandler.getClass().getName(); } else { this .protocolHandler = null ; this .protocolHandlerClassName = protocol; }
而配置的协议为 HTTP/1.1:
1 2 3 4 5 6 7 8 9 10 static ProtocolHandler create (String protocol, boolean apr) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { if (protocol == null || "HTTP/1.1" .equals(protocol) || !apr && Http11NioProtocol.class.getName().equals(protocol) || apr && Http11AprProtocol.class.getName().equals(protocol)) { return (ProtocolHandler)(apr ? new Http11AprProtocol () : new Http11NioProtocol ()); } else if ("AJP/1.3" .equals(protocol) || !apr && AjpNioProtocol.class.getName().equals(protocol) || apr && AjpAprProtocol.class.getName().equals(protocol)) { return (ProtocolHandler)(apr ? new AjpAprProtocol () : new AjpNioProtocol ()); } else { Class<?> clazz = Class.forName(protocol); return (ProtocolHandler)clazz.getConstructor().newInstance(); } }
默认没有开启 apr 的情况下,处理 HTTP 协议的类应该就是 Http11NioProtocol。
shutdown 回到 Catalina 类的 start 函数,最后会执行 await 开始监听请求:
1 2 3 4 if (this .await) { this .await(); this .stop(); }
this.await 在 Bootstrap 对象执行 daemon.setAwait(true) 时赋值为 true,await 函数会执行到 Server 的 await 函数,首先在 8005 端口创建了一个监听本地请求的 ServerSocket:
1 this .awaitSocket = new ServerSocket (this .getPortWithOffset(), 1 , InetAddress.getByName(this .address));
然后从 client 读取字符,如果收到的命令为 shutdown 就会中断监听并关闭 socket:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 boolean match = command.toString().equals(this .shutdown);if (match) { log.info(sm.getString("standardServer.shutdownViaPort" )); var32 = false ; break ; } ... serverSocket = this .awaitSocket;this .awaitThread = null ;this .awaitSocket = null ;if (serverSocket != null ) { try { serverSocket.close(); } catch (IOException var65) { } }
这里的死循环结束之后,就会结束 await 函数并调用 this.stop:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public void stop () { try { if (this .useShutdownHook) { Runtime.getRuntime().removeShutdownHook(this .shutdownHook); LogManager logManager = LogManager.getLogManager(); if (logManager instanceof ClassLoaderLogManager) { ((ClassLoaderLogManager)logManager).setUseShutdownHook(true ); } } } catch (Throwable var3) { ExceptionUtils.handleThrowable(var3); } try { Server s = this .getServer(); LifecycleState state = s.getState(); if (LifecycleState.STOPPING_PREP.compareTo(state) > 0 || LifecycleState.DESTROYED.compareTo(state) < 0 ) { s.stop(); s.destroy(); } } catch (LifecycleException var4) { log.error(sm.getString("catalina.stopError" ), var4); } }
调用 Server 的 stop 和 destroy,大体来说就是一级级下去分别调用了 Service、Engine 等模块类的停止函数,就把整个 Tomcat 服务关闭了。
当我们使用 stop 命令关闭 Tomcat 时,Catalina 类会解析配置文件,获取 IP 端口等信息并传输 shutdown 命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 try { Socket socket = new Socket (s.getAddress(), s.getPortWithOffset()); Throwable var4 = null ; try { OutputStream stream = socket.getOutputStream(); Throwable var6 = null ; try { String shutdown = s.getShutdown(); for (int i = 0 ; i < shutdown.length(); ++i) { stream.write(shutdown.charAt(i)); } stream.flush(); } ... } } ... }
这样一来就实现了关闭 Tomcat。
await 回到 connector 中执行的这一句代码:
1 this .protocolHandler.start();
这里就开始启动 web 部分的监听了,来到 Http11NioProtocol 父类 AbstractProtocol 的 start 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public void start () throws Exception { if (this .getLog().isInfoEnabled()) { this .getLog().info(sm.getString("abstractProtocolHandler.start" , new Object []{this .getName()})); this .logPortOffset(); } this .endpoint.start(); this .monitorFuture = this .getUtilityExecutor().scheduleWithFixedDelay(new Runnable () { public void run () { if (!AbstractProtocol.this .isPaused()) { AbstractProtocol.this .startAsyncTimeout(); } } }, 0L , 60L , TimeUnit.SECONDS); }
首先调用了 endpoint.start,看下 endpoint 是个什么对象:
1 2 3 public Http11NioProtocol () { super (new NioEndpoint ()); }
然后来到 Http11NioProtocol 类的 startInternal 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public void startInternal () throws Exception { if (!this .running) { this .running = true ; this .paused = false ; if (this .socketProperties.getProcessorCache() != 0 ) { this .processorCache = new SynchronizedStack (128 , this .socketProperties.getProcessorCache()); } if (this .socketProperties.getEventCache() != 0 ) { this .eventCache = new SynchronizedStack (128 , this .socketProperties.getEventCache()); } if (this .socketProperties.getBufferPool() != 0 ) { this .nioChannels = new SynchronizedStack (128 , this .socketProperties.getBufferPool()); } if (this .getExecutor() == null ) { this .createExecutor(); } this .initializeConnectionLatch(); this .poller = new NioEndpoint .Poller(); Thread pollerThread = new Thread (this .poller, this .getName() + "-ClientPoller" ); pollerThread.setPriority(this .threadPriority); pollerThread.setDaemon(true ); pollerThread.start(); this .startAcceptorThread(); } }
Tomcat 中的客户端请求处理是通过 Acceptor 和 Poller 两部分实现的,我们先看看 Acceptor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public void run () { byte errorDelay = 0 ; while (this .endpoint.isRunning()) { ... this .state = Acceptor.AcceptorState.RUNNING; try { this .endpoint.countUpOrAwaitConnection(); if (!this .endpoint.isPaused()) { Object socket = null ; try { socket = this .endpoint.serverSocketAccept(); } catch (Exception var6) { this .endpoint.countDownConnection(); if (!this .endpoint.isRunning()) { break ; } this .handleExceptionWithDelay(errorDelay); throw var6; } errorDelay = 0 ; if (this .endpoint.isRunning() && !this .endpoint.isPaused()) { if (!this .endpoint.setSocketOptions(socket)) { this .endpoint.closeSocket(socket); } } else { this .endpoint.destroySocket(socket); } } } ... } this .state = Acceptor.AcceptorState.ENDED; }
调用 this.endpoint.serverSocketAccept 获取一个 SocketChannel:
1 2 3 protected SocketChannel serverSocketAccept () throws Exception { return this .serverSock.accept(); }
accept 函数会一直阻塞直到新的请求到来。serverSock 的初始化在 initServerSocket 函数中(往上可以追溯到 Connector 的 this.protocolHandler.init() 这一句):
1 2 3 4 this .serverSock = ServerSocketChannel.open();this .socketProperties.setProperties(this .serverSock.socket());InetSocketAddress addr = new InetSocketAddress (this .getAddress(), this .getPortWithOffset());this .serverSock.socket().bind(addr, this .getAcceptCount());
可以看到监听的地址和端口来自自身的两个属性,他们的赋值方式要追溯到 connector 的创建去了,解析 XML 时:
1 2 3 4 Connector con = new Connector (protocolName); ...this .digester.push(con);
创建一个 Connector 并放入栈中(如上文所说,protocolHandler 就是在 connector 构造函数中创建的),然后下一条规则:
1 digester.addSetProperties("Server/Service/Connector" , new String []{"executor" , "sslImplementationName" , "protocol" });
除了这三个属性,其他都会进行赋值,SetPropertiesRule 的赋值处理如下:
1 2 3 4 5 if (!IntrospectionUtils.setProperty(top, name, value, true , actualMethod)) { if (this .digester.getRulesValidation() && !"optional" .equals(name)) { this .digester.log.warn(sm.getString("rule.noProperty" , new Object []{this .digester.match, name, value})); } }
IntrospectionUtils 的处理比较别致,找不到特定属性的 setter 时会调用 setProperty:
1 2 3 4 5 6 7 if ("setProperty" .equals(method.getName())) { if (method.getReturnType() == Boolean.TYPE) { setPropertyMethodBool = method; } else { setPropertyMethodVoid = method; } }
对于 connector 来说就是:
1 2 3 public boolean setProperty (String name, String value) { return this .protocolHandler == null ? false : IntrospectionUtils.setProperty(this .protocolHandler, name, value); }
对于 protocolHandler(Http11NioProtocol)来说就是:
1 2 3 public void setAddress (InetAddress ia) { this .endpoint.setAddress(ia); }
所以一层层反射下去就会给 endpoint 赋值。
不过又有个问题,connector 反射的参数类型为 String,而 protocolHandler 却是 InetAddress,所以中间还存在一个类型转换的操作。再回到了 IntrospectionUtils,这里又有个对 InetAddress 类型参数的别致操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 if ("java.net.InetAddress" .equals(paramType.getName())) { try { params[0 ] = InetAddress.getByName(value); } catch (UnknownHostException var20) { if (log.isDebugEnabled()) { log.debug("IntrospectionUtils: Unable to resolve host name:" + value); } ok = false ; } if (actualMethod != null ) { actualMethod.append(method.getName()).append("(InetAddress.getByName(\"" ).append(value).append("\"))" ); } }
所以在 connector 中配置 address 可以设置 Tomcat 的监听地址。
不过看 server.xml,里面是没有配置 address 的,所以就是默认的 0.0.0.0:
1 2 3 4 5 6 public InetSocketAddress (InetAddress addr, int port) { holder = new InetSocketAddressHolder ( null , addr == null ? InetAddress.anyLocalAddress() : addr, checkPort(port)); }
回到 Acceptor,在接收到请求并获取到一个 SocketChannel 之后,会调用 setSocketOptions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 protected boolean setSocketOptions (SocketChannel socket) { Object socketWrapper = null ; try { NioChannel channel = null ; if (this .nioChannels != null ) { channel = (NioChannel)this .nioChannels.pop(); } if (channel == null ) { SocketBufferHandler bufhandler = new SocketBufferHandler (this .socketProperties.getAppReadBufSize(), this .socketProperties.getAppWriteBufSize(), this .socketProperties.getDirectBuffer()); if (this .isSSLEnabled()) { channel = new SecureNioChannel (bufhandler, this .selectorPool, this ); } else { channel = new NioChannel (bufhandler); } } NioEndpoint.NioSocketWrapper newWrapper = new NioEndpoint .NioSocketWrapper((NioChannel)channel, this ); ((NioChannel)channel).reset(socket, newWrapper); this .connections.put(socket, newWrapper); socket.configureBlocking(false ); this .socketProperties.setProperties(socket.socket()); newWrapper.setReadTimeout((long )this .getConnectionTimeout()); newWrapper.setWriteTimeout((long )this .getConnectionTimeout()); newWrapper.setKeepAliveLeft(this .getMaxKeepAliveRequests()); this .poller.register((NioChannel)channel, newWrapper); return true ; } ... }
新建一个 NioChannel,将 SocketChannel 和 NioSocketWrapper 放进去,再将新生成的 channel 和 wrapper 一起放入 connections Map 并注册进 Poller 中,注册进 Poller 中的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public void register (NioChannel socket, NioEndpoint.NioSocketWrapper socketWrapper) { socketWrapper.interestOps(1 ); NioEndpoint.PollerEvent event = null ; if (NioEndpoint.this .eventCache != null ) { event = (NioEndpoint.PollerEvent)NioEndpoint.this .eventCache.pop(); } if (event == null ) { event = new NioEndpoint .PollerEvent(socket, 256 ); } else { event.reset(socket, 256 ); } this .addEvent(event); }
简单来说就是会添加进 events 这个队列中,Acceptor 的 run 函数大致结束了,接下来看看 Poller 的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public void run () { while (true ) { boolean hasEvents = false ; label59: { try { if (!this .close) { hasEvents = this .events(); ... } if (!this .close) { break label59; } ... } ... } ... } }
看看 events 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public boolean events () { boolean result = false ; NioEndpoint.PollerEvent pe = null ; int i = 0 ; for (int size = this .events.size(); i < size && (pe = (NioEndpoint.PollerEvent)this .events.poll()) != null ; ++i) { result = true ; NioChannel channel = pe.getSocket(); NioEndpoint.NioSocketWrapper socketWrapper = channel.getSocketWrapper(); int interestOps = pe.getInterestOps(); if (interestOps == 256 ) { try { channel.getIOChannel().register(this .getSelector(), 1 , socketWrapper); } catch (Exception var12) { NioEndpoint.log.error(AbstractEndpoint.sm.getString("endpoint.nio.registerFail" ), var12); } } else { ... } if (NioEndpoint.this .running && !NioEndpoint.this .paused && NioEndpoint.this .eventCache != null ) { pe.reset(); NioEndpoint.this .eventCache.push(pe); } } return result; }
调用 poll 从中队列中取出一个 event(并从队列中删除),关注这一句:
1 channel.getIOChannel().register(this .getSelector(), 1 , socketWrapper);
这里将 channel 注册到了 selector 上,并监听 read 事件:
1 public static final int OP_READ = 1 << 0 ;
继续看 run 函数:
1 2 3 4 5 6 7 if (this .wakeupCounter.getAndSet(-1L ) > 0L ) { this .keyCount = this .selector.selectNow(); } else { this .keyCount = this .selector.select(NioEndpoint.this .selectorTimeout); }this .wakeupCounter.set(0L );
wakeupCounter 是一个初始为 0 的计数器,每次 addEvent 就会 +1,没有 event 时就会为 0,从而进入 this.selector.select 的阻塞,超时时间为 1 秒。
如果存在有效事件,keyCount 就是有效事件的数量,后面就开始处理这些事件:
1 2 3 4 5 6 7 8 9 10 11 12 Iterator iterator = this .keyCount > 0 ? this .selector.selectedKeys().iterator() : null ;while (iterator != null && iterator.hasNext()) { SelectionKey sk = (SelectionKey)iterator.next(); NioEndpoint.NioSocketWrapper socketWrapper = (NioEndpoint.NioSocketWrapper)sk.attachment(); if (socketWrapper == null ) { iterator.remove(); } else { iterator.remove(); this .processKey(sk, socketWrapper); } }
看看 processKey 函数,关注 read 的部分:
1 2 3 4 5 6 7 8 9 if (sk.isReadable()) { if (socketWrapper.readOperation != null ) { if (!socketWrapper.readOperation.process()) { closeSocket = true ; } } else if (!NioEndpoint.this .processSocket(socketWrapper, SocketEvent.OPEN_READ, true )) { closeSocket = true ; } }
下一步 processSocket:
1 2 3 4 5 6 7 8 9 10 11 12 if (handshake == 0 ) { AbstractEndpoint.Handler.SocketState state = AbstractEndpoint.Handler.SocketState.OPEN; if (this .event == null ) { state = NioEndpoint.this .getHandler().process(this .socketWrapper, SocketEvent.OPEN_READ); } else { state = NioEndpoint.this .getHandler().process(this .socketWrapper, this .event); } if (state == AbstractEndpoint.Handler.SocketState.CLOSED) { poller.cancelledKey(socket.getIOChannel().keyFor(poller.getSelector()), this .socketWrapper); } }
handshake 为 0 代表 TCP 握手完成(大概),然后调用 handler 的 process 函数,这里的 handler 是个 ConnectionHandler 类,在 protocolHandler 实例化时赋值(AbstractHttp11Protocol 类构造函数):
1 2 3 AbstractProtocol.ConnectionHandler<S> cHandler = new AbstractProtocol .ConnectionHandler(this );this .setHandler(cHandler);this .getEndpoint().setHandler(cHandler);
process:
1 2 3 4 5 6 7 if (processor == null ) { processor = this .getProtocol().createProcessor(); this .register(processor); if (this .getLog().isDebugEnabled()) { this .getLog().debug(AbstractProtocol.sm.getString("abstractConnectionHandler.processorCreate" , new Object []{processor})); } }
第一次访问时没有 processor,所以会调用 createProcessor 生成一个,AbstractHttp11Protocol 类的 createProcessor 函数如下:
1 2 3 4 protected Processor createProcessor () { Http11Processor processor = new Http11Processor (this , this .adapter); return processor; }
生成的是 Http11Processor,然后调用其 process 函数:
1 state = processor.process(wrapper, status);
然后来到 AbstractProcessorLight 类的 process:
1 2 3 else if (status == SocketEvent.OPEN_READ) { state = this .service(socketWrapper); }
回到 Http11Processor 类的 service 函数:
1 this .getAdapter().service(this .request, this .response);
可以看到,这里有 request 和 response 两个属性,都来自 org.apache.coyote 包,找一下他们的生成方式,在父类 AbstractProcessor 的构造函数中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public AbstractProcessor (Adapter adapter) { this (adapter, new Request (), new Response ()); }protected AbstractProcessor (Adapter adapter, Request coyoteRequest, Response coyoteResponse) { this .hostNameC = new char [0 ]; this .asyncTimeout = -1L ; this .asyncTimeoutGeneration = 0L ; this .socketWrapper = null ; this .errorState = ErrorState.NONE; this .adapter = adapter; this .asyncStateMachine = new AsyncStateMachine (this ); this .request = coyoteRequest; this .response = coyoteResponse; this .response.setHook(this ); this .request.setResponse(this .response); this .request.setHook(this ); this .userDataHelper = new UserDataHelper (this .getLog()); }
可以看到 response 就存放在 request 中,Http11Processor 放在 request 和 response 中。不过仔细观察这个 request 中的属性,会发现他们似乎并不是我们习惯的那个 request。
而 Adapter 的赋值要追溯到 Connector 的 initInternal 函数:
1 this .adapter = new CoyoteAdapter (this );
跟进 CoyoteAdapter 类的 service 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 Request request = (Request)req.getNote(1 );Response response = (Response)res.getNote(1 );if (request == null ) { request = this .connector.createRequest(); request.setCoyoteRequest(req); response = this .connector.createResponse(); response.setCoyoteResponse(res); request.setResponse(response); response.setRequest(request); req.setNote(1 , request); res.setNote(1 , response); req.getParameters().setQueryStringCharset(this .connector.getURICharset()); }
可以看到我们习惯的 request(org.apache.catalina.connector),第一次访问时 request(org.apache.coyote)是刚刚生成的,所以 note 属性里面没有保存 request(org.apache.catalina.connector),这时会调用 createRequest/createResponse 从 connector 生成并保存起来:
1 2 3 4 5 6 7 8 public Request createRequest () { return new Request (this ); }public Response createResponse () { int size = this .protocolHandler.getDesiredBufferSize(); return size > 0 ? new Response (size) : new Response (); }
request 和 response 互相嵌套,connector 套进了 request 中。
然后开始处理 HTTP 请求:
1 2 3 4 5 postParseSuccess = this .postParseRequest(req, request, res, response);if (postParseSuccess) { request.setAsyncSupported(this .connector.getService().getContainer().getPipeline().isAsyncSupported()); this .connector.getService().getContainer().getPipeline().getFirst().invoke(request, response); }
调用 postParseRequest 解析请求,如果解析成功就会开始反射,看这一长串链式执行,可以简化成 engine.getPipeline().getFirst().invoke(request, response),我们回到 engine 去看看:
1 protected final Pipeline pipeline = new StandardPipeline (this );
StandardPipeline 类的 getFirst 函数如下:
1 2 3 public Valve getFirst () { return this .first != null ? this .first : this .basic; }
调试可以发现,这里的 first 为 null,basic 来自 engine 的构造函数:
1 this .pipeline.setBasic(new StandardEngineValve ());
所以最后的反射函数如下:
1 2 3 4 5 6 7 8 9 10 public final void invoke (Request request, Response response) throws IOException, ServletException { Host host = request.getHost(); if (host != null ) { if (request.isAsyncSupported()) { request.setAsyncSupported(host.getPipeline().isAsyncSupported()); } host.getPipeline().getFirst().invoke(request, response); } }
看看这里的 host 是怎么生成的,回到 postParseRequest,里面有一句:
1 this .connector.getService().getMapper().map(serverName, decodedURI, version, request.getMappingData());
会来到 Mapper 类的 internalMap 函数:
1 2 3 4 5 Mapper.MappedHost[] hosts = this .hosts; Mapper.MappedHost mappedHost = (Mapper.MappedHost)exactFindIgnoreCase(hosts, host); ... mappingData.host = (Host)mappedHost.object;
hosts 是一个数组,通过 addHost 函数进行添加,再往上回到 MapperListener 类的 registerHost 函数中,在前面 start 的时候会注册进去:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public void startInternal () throws LifecycleException { this .setState(LifecycleState.STARTING); Engine engine = this .service.getContainer(); if (engine != null ) { this .findDefaultHost(); this .addListeners(engine); Container[] conHosts = engine.findChildren(); Container[] var3 = conHosts; int var4 = conHosts.length; for (int var5 = 0 ; var5 < var4; ++var5) { Container conHost = var3[var5]; Host host = (Host)conHost; if (!LifecycleState.NEW.equals(host.getState())) { this .registerHost(host); } } } }
可以看到源数据来自 engine,即 server.xml 中配置并解析生成的 standardHost 对象。然后再调用 exactFindIgnoreCase 从这些 hosts 中获取该次请求对应的 host:
1 2 3 4 5 6 7 8 9 10 11 private static final <T, E extends Mapper .MapElement<T>> E exactFindIgnoreCase (E[] map, CharChunk name) { int pos = findIgnoreCase(map, name); if (pos >= 0 ) { E result = map[pos]; if (name.equalsIgnoreCase(result.name)) { return result; } } return null ; }
简单来说就是将访问的 host 和 sever.xml 中配置的 host 匹配一下。
除了 host,mappingData 中还存放了其他重要属性,比如说 context,我们继续往下看 internalMap 函数:
1 2 3 Mapper.ContextList contextList = mappedHost.contextList; Mapper.MappedContext[] contexts = contextList.contexts;int pos = find(contexts, (CharChunk)uri);
这里出现了一个 host 中的属性 contextList,它是在 registerHost 的时候注册的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private void registerHost (Host host) { String[] aliases = host.findAliases(); this .mapper.addHost(host.getName(), aliases, host); Container[] var3 = host.findChildren(); int var4 = var3.length; for (int var5 = 0 ; var5 < var4; ++var5) { Container container = var3[var5]; if (container.getState().isAvailable()) { this .registerContext((Context)container); } } this .findDefaultHost(); ... }
最后在 addContextVersion 函数中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Mapper.ContextVersion newContextVersion = new Mapper .ContextVersion(version, path, slashCount, context, resources, welcomeResources);if (wrappers != null ) { this .addWrappers(newContextVersion, wrappers); } Mapper.ContextList contextList = mappedHost.contextList; Mapper.MappedContext mappedContext = (Mapper.MappedContext)exactFind(contextList.contexts, (String)path);if (mappedContext == null ) { mappedContext = new Mapper .MappedContext(path, newContextVersion); Mapper.ContextList newContextList = contextList.addContext(mappedContext, slashCount); if (newContextList != null ) { this .updateContextList(mappedHost, newContextList); this .contextObjectToContextVersionMap.put(context, newContextVersion); } }
context 和 contextVersion 放入 MappedContext,再放入 contextList 中。
context 配置在 server.xml 的 host 模块下面,代表一个 web 项目(war 包或其解压后的目录),而因为 Tomcat 开启了 autoDeploy 和 unpackWARs,所以文件中没有静态配置,而是由 Tomcat 进行动态加载。顺带一提的是 context 的 name 就是配置中的 path,在 setPath 函数中:
1 2 3 if (this .getName() == null ) { this .setName(this .path); }
继续 internalMap,通过 uri 匹配到对应的 context 后,会将其放入 mappingData 中。(standardContext 就保存在 contextVersion 中)
还有一个重要属性 wrapper,获取 context 之后,会调用 internalMapWrapper 获取相应的 wrapper,先来看看 wrapper 的生成方式,context 中的 wrapper 在 ContextConfig 类中生成,这个类会解析 web.xml 并做相应处理,其 configureContext 函数一部分如下:
1 2 3 4 5 6 7 8 9 var35 = webxml.getServlets().values().iterator(); Iterator var7;while (var35.hasNext()) { ServletDef servlet = (ServletDef)var35.next(); Wrapper wrapper = this .context.createWrapper(); ... this .context.addChild(wrapper); }
生成 wrapper 然后用 addChild 添加到 context 中,可以发现 wrapper 和 servlet 是对应的,中间有一大段将 servlet 相关数据存储在 wrapper 中的代码被我省略了,接下来回到 standardContext 类的 createWrapper 函数:
1 wrapper = new StandardWrapper ();
wrapper 是一个 StandardWrapper 类,其他的没有什么特别操作了。在注册 context 的时候(registerContext),这些 wrapper 会经过 prepareWrapperMappingInfo 函数处理,合成一个 List:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private void prepareWrapperMappingInfo (Context context, Wrapper wrapper, List<WrapperMappingInfo> wrappers) { String wrapperName = wrapper.getName(); boolean resourceOnly = context.isResourceOnlyServlet(wrapperName); String[] mappings = wrapper.findMappings(); String[] var7 = mappings; int var8 = mappings.length; for (int var9 = 0 ; var9 < var8; ++var9) { String mapping = var7[var9]; boolean jspWildCard = wrapperName.equals("jsp" ) && mapping.endsWith("/*" ); wrappers.add(new WrapperMappingInfo (mapping, wrapper, jspWildCard, resourceOnly)); } }
然后通过 addWrappers 分门别类组装成 MappedWrapper 放入 contextVersion:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 private void addWrappers (Mapper.ContextVersion contextVersion, Collection<WrapperMappingInfo> wrappers) { Iterator var3 = wrappers.iterator(); while (var3.hasNext()) { WrapperMappingInfo wrapper = (WrapperMappingInfo)var3.next(); this .addWrapper(contextVersion, wrapper.getMapping(), wrapper.getWrapper(), wrapper.isJspWildCard(), wrapper.isResourceOnly()); } }protected void addWrapper (Mapper.ContextVersion context, String path, Wrapper wrapper, boolean jspWildCard, boolean resourceOnly) { synchronized (context) { String name; Mapper.MappedWrapper newWrapper; Mapper.MappedWrapper[] oldWrappers; Mapper.MappedWrapper[] newWrappers; if (path.endsWith("/*" )) { name = path.substring(0 , path.length() - 2 ); newWrapper = new Mapper .MappedWrapper(name, wrapper, jspWildCard, resourceOnly); oldWrappers = context.wildcardWrappers; newWrappers = new Mapper .MappedWrapper[oldWrappers.length + 1 ]; if (insertMap(oldWrappers, newWrappers, newWrapper)) { context.Wrappers = newWrappers; int slashCount = slashCount(newWrapper.name); if (slashCount > context.nesting) { context.nesting = slashCount; } } } else if (path.startsWith("*." )) { name = path.substring(2 ); newWrapper = new Mapper .MappedWrapper(name, wrapper, jspWildCard, resourceOnly); oldWrappers = context.extensionWrappers; newWrappers = new Mapper .MappedWrapper[oldWrappers.length + 1 ]; if (insertMap(oldWrappers, newWrappers, newWrapper)) { context.extensionWrappers = newWrappers; } } else if (path.equals("/" )) { Mapper.MappedWrapper newWrapper = new Mapper .MappedWrapper("" , wrapper, jspWildCard, resourceOnly); context.defaultWrapper = newWrapper; } else { if (path.length() == 0 ) { name = "/" ; } else { name = path; } newWrapper = new Mapper .MappedWrapper(name, wrapper, jspWildCard, resourceOnly); oldWrappers = context.exactWrappers; newWrappers = new Mapper .MappedWrapper[oldWrappers.length + 1 ]; if (insertMap(oldWrappers, newWrappers, newWrapper)) { context.exactWrappers = newWrappers; } } } }
主要有几个分类:
wildcardWrappers:/* 通配符结尾的路径
extensionWrappers:某一类后缀的路径
defaultWrapper:/ 结尾的默认(首页?)路径
exactWrappers:全匹配的详细路径
现在回到 internalMapWrapper,可以看到匹配成功后,会将 wrapper 放入 mappingData 中。
回到反射,下一步是 StandardHostValve 类,获取的 context 就是前面 internalMap 函数匹配到的那个:
1 2 3 Context context = request.getContext(); ... context.getPipeline().getFirst().invoke(request, response);
然后是 StandardContextValve 类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public final void invoke (Request request, Response response) throws IOException, ServletException { MessageBytes requestPathMB = request.getRequestPathMB(); if (!requestPathMB.startsWithIgnoreCase("/META-INF/" , 0 ) && !requestPathMB.equalsIgnoreCase("/META-INF" ) && !requestPathMB.startsWithIgnoreCase("/WEB-INF/" , 0 ) && !requestPathMB.equalsIgnoreCase("/WEB-INF" )) { Wrapper wrapper = request.getWrapper(); if (wrapper != null && !wrapper.isUnavailable()) { try { response.sendAcknowledgement(); } catch (IOException var6) { this .container.getLogger().error(sm.getString("standardContextValve.acknowledgeException" ), var6); request.setAttribute("javax.servlet.error.exception" , var6); response.sendError(500 ); return ; } if (request.isAsyncSupported()) { request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported()); } wrapper.getPipeline().getFirst().invoke(request, response); } else { response.sendError(404 ); } } else { response.sendError(404 ); } }
然后是 StandardWrapperValve 类,首先会生成一个 Servlet 对象:
1 2 3 4 5 try { if (!unavailable) { servlet = wrapper.allocate(); } }
wrapper 的 allocate 函数中,会先检查自身的 instance 属性中有没有加载好的 servlet,没有则会调用 loadServlet 进行加载:
1 servlet = (Servlet)instanceManager.newInstance(this .servletClass);
这里的 servletClass 来自 web.xml 中配置的类。
获取到 servlet 后,会调用 createFilterChain 创建 filterChain:
1 ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
createFilterChain:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ApplicationFilterChain filterChain = null ;if (request instanceof Request) { Request req = (Request)request; if (Globals.IS_SECURITY_ENABLED) { filterChain = new ApplicationFilterChain (); } else { filterChain = (ApplicationFilterChain)req.getFilterChain(); if (filterChain == null ) { filterChain = new ApplicationFilterChain (); req.setFilterChain(filterChain); } } } else { filterChain = new ApplicationFilterChain (); }
首先该 request 对象中有没有存放有 filterChain,如果没有(比如第一次访问)则生成一个 ApplicationFilterChain 对象。继续往下看:
1 2 3 4 filterChain.setServlet(servlet); filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());StandardContext context = (StandardContext)wrapper.getParent(); FilterMap[] filterMaps = context.findFilterMaps();
将 servlet 放入 filterChain,取出 context,再从 context 中取出 filterMaps,filterMaps 的赋值可以回到 configureContext:
1 2 3 4 5 6 var2 = webxml.getFilterMappings().iterator();while (var2.hasNext()) { FilterMap filterMap = (FilterMap)var2.next(); this .context.addFilterMap(filterMap); }
简单来说也是从 web.xml 中来的。回到 createFilterChain:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 String servletName = wrapper.getName(); FilterMap[] var10 = filterMaps;int var11 = filterMaps.length;int var12; FilterMap filterMap; ApplicationFilterConfig filterConfig;for (var12 = 0 ; var12 < var11; ++var12) { filterMap = var10[var12]; if (matchDispatcher(filterMap, dispatcher) && matchFiltersURL(filterMap, requestPath)) { filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName()); if (filterConfig != null ) { filterChain.addFilter(filterConfig); } } }
匹配上 DispatcherType 和 requestPath 就会加入 filterChain 中。
回到 StandardWrapperValve,创建完 filterChain 之后执行:
1 filterChain.doFilter(request.getRequest(), response.getResponse());
即:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 if (this .pos < this .n) { ApplicationFilterConfig filterConfig = this .filters[this .pos++]; try { Filter filter = filterConfig.getFilter(); if (request.isAsyncSupported() && "false" .equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) { request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED" , Boolean.FALSE); } if (Globals.IS_SECURITY_ENABLED) { Principal principal = ((HttpServletRequest)request).getUserPrincipal(); Object[] args = new Object []{request, response, this }; SecurityUtil.doAsPrivilege("doFilter" , filter, classType, args, principal); } else { filter.doFilter(request, response, this ); } } catch (ServletException | RuntimeException | IOException var15) { throw var15; } catch (Throwable var16) { Throwable e = ExceptionUtils.unwrapInvocationTargetException(var16); ExceptionUtils.handleThrowable(e); throw new ServletException (sm.getString("filterChain.filter" ), e); } }
依次调用这些 filter 的 doFilter 函数,在处理完 filter 之后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 try { if (ApplicationDispatcher.WRAP_SAME_OBJECT) { lastServicedRequest.set(request); lastServicedResponse.set(response); } if (request.isAsyncSupported() && !this .servletSupportsAsync) { request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED" , Boolean.FALSE); } if (request instanceof HttpServletRequest && response instanceof HttpServletResponse && Globals.IS_SECURITY_ENABLED) { Principal principal = ((HttpServletRequest)request).getUserPrincipal(); Object[] args = new Object []{request, response}; SecurityUtil.doAsPrivilege("service" , this .servlet, classTypeUsedInService, args, principal); } else { this .servlet.service(request, response); } }
就开始调用 servlet 的 service 函数,往后就是项目的具体实现代码了,就不提了。
Orz