精华内容
下载资源
问答
  • Tomcat:第章:Tomcat组件

    万次阅读 多人点赞 2020-10-24 11:54:48
    tomcat组件: Server shutdown:关闭命令。基于telent 执行SHUTDOWN 命令即可关闭(必须大写) telnet 127.0.0.1 8005 SHUTDOWN Service Connector 接受客户端链接并接受消息报文,消息报文经由它解析后...

    tomcat组件:

    <Server>
        <Listener /><!-- 监听器 -->
        <GlobaNamingResources> <!-- 全局资源 -->
        </GlobaNamingResources
        <Service>          <!-- 服务 用于 绑定 连接器与 Engine -->
            <Connector 8080/> <!-- 连接器-->
            <Connector 8010 /> <!-- 连接器-->
            <Connector 8030/> <!-- 连接器-->
            
            <Engine>      <!-- 执行引擎-->
                <Logger />
                <Realm />
                   <host "www.tl.com" appBase="">  <!-- 虚拟主机-->
                       <Logger /> <!-- 日志配置-->
                       <Context "/luban" path=""/> <!-- 上下文配置-->
                   </host>
            </Engine>
        </Service>
    </Server>

     Server 

    shutdown:关闭命令。基于telent 执行SHUTDOWN 命令即可关闭(必须大写) telnet 127.0.0.1 8005 SHUTDOWN

    Service

    Connector

    接受客户端链接并接受消息报文,消息报文经由它解析后送往容器中处理

    关于Connector的几个重要点:

    • 监听的IP和Port;
    • 处理请求的最大线程数;如果所有线程都忙,则会丢弃新的请求;
    • 所有的Connector接收到请求后,转换成统一的模式,再交给唯一的Engine处理;Engine负责处理请求并产生响应;
    • Connector将Engine产生的响应按合适的协议发送到客户端;
    主要属性:
    • protocol 监听的协议,默认是http/1.1
    • port 指定服务器端要创建的端口号
    • minSpareThreads服务器启动时创建的处理请求的线程数
    • maxThreads 最大可以创建的处理请求的线程数
    • enableLookups 如果为true,则可以通过调用request.getRemoteHost()进行DNS查询来得到远程客户端的实际主机名,若为false则不进行DNS查询,而是返回其ip地址
    • redirectPort 指定服务器正在处理http请求时收到了一个SSL传输请求后重定向的端口号
    • acceptCount 指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理
    • connectionTimeout 指定超时的时间数(以毫秒为单位)
    • SSLEnabled 是否开启 sll 验证,在Https 访问时需要开启。
    • 生成证书: keytool ­genkey ­v ­alias testKey ­keyalg RSA ­validity 3650 ­keystore D:\test.keystore
    <Connector port="8860" protocol="org.apache.coyote.http11.Http11NioProtoc ol" 
    	connectionTimeout="20000" 
    	redirectPort="8862" 
    	URIEncoding="UTF‐8" 
    	useBodyEncodingForURI="true" 
    	compression="on" compressionMinSize="2048" 
    	compressableMimeType="text/html,text/xml,text/plain,text/javascript,text/ css,application/x‐json,application/json,application/x‐javascript" 
    	maxThreads="1024" minSpareThreads="200" 
    	acceptCount="800" 
    	enableLookups="false"/>
    

    Tomcat内部有4个级别的容器,分别是Engine、Host、Context和Wrapper。

    Engine

    引擎:用于处理连接的执行器,默认的引擎是catalina。全局Servlet引擎。每个Service组件只能包含一个Engine容器组件,但Engine组件可以包含若干Host容器组件。 主要属性:name 引擎名称 defaultHost 默认host

    Host

    虚拟机:基于域名匹配至指定虚拟机。类似于nginx 当中的server,默认的虚拟机是localhost。演示配置多个Host :
     
    <Host name="www.wukong.com" appBase="/usr/www/wukong" unpackWARs="true" autoDeploy="true"> 
     <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="www.wukong.com.access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> 
    </Host>

    当Host获得一个针对特定Host请求时,将会在该Host环境下把请求匹配到对应的Context上;然后把请求交给这个Context来处理。

    Context

    应用上下文:一个host 下可以配置多个Context ,每个Context 都有其独立的classPath。相互隔离,以免造成ClassPath 冲突。 主要属性:
     
    <Context path="/testweb" docBase="testweb.war" reloadbale="true"/>

    一个 Context对应一个Web Application;它由多个Servlet组成;在创建Context时,将根据conf/web.xml和webapps/${context path}/WEB-INF/web.xml加载Servlet并创建映射表;

    Wrapper

    Wrapper是Context的子元素,代表了一个Servlet(或一个JSP被编译后的Servlet);它负责加载Servlet、实例化Servlet、以及触发生命周期方法的调用,如init(), service(), destory()方法;另外,Wrapper也负责调用与Servlet相关的Filter。

    Valve

    阀门:可以理解成的过滤器,具体配置要基于具体的Valve 接口的子类。以下即为一个访问日志的Valve
     
    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" 
       prefix="www.wukong.com.access_log" suffix=".txt" 
       pattern="%h %l %u %t "%r" %s %b" />

    Tomcat处理一个HTTP请求的过程

    假设来自客户的请求为: http://localhost:8080/wsota/wsota_index.jsp 
    1) 请求被发送到本机端口8080,被在那里侦听的Coyote HTTP/1.1 Connector获得 
    2) Connector把该请求交给它所在的Service的Engine来处理,并等待来自Engine的回应 
    3) Engine获得请求localhost/wsota/wsota_index.jsp,匹配它所拥有的所有虚拟主机Host 
    4) Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机) 
    5) localhost Host获得请求/wsota/wsota_index.jsp,匹配它所拥有的所有Context 
    6) Host匹配到路径为/wsota的Context(如果匹配不到就把该请求交给路径名为""的Context去处理) 
    7) path="/wsota"的Context获得请求/wsota_index.jsp,在它的mapping table中寻找对应的servlet 
    8) Context匹配到URL PATTERN为*.jsp的servlet,对应于JspServlet类 
    9) 构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法 
    10)Context把执行完了之后的HttpServletResponse对象返回给Host 
    11)Host把HttpServletResponse对象返回给Engine 
    12)Engine把HttpServletResponse对象返回给Connector 
    13)Connector把HttpServletResponse对象返回给客户browser

    展开全文
  • Springboot默认使用的就是嵌入式servlet容器即tomcat,对于web项目,如果使用的是外部tomcat,相关配置比如访问端口、资源路径等可以在tomcat的conf文件下配置。但是在boot中,tomcat配置又两种方式 第一种:通过...

    原文地址:http://www.javayihao.top/detail/172

    1.tomcat配置

    Springboot默认使用的就是嵌入式servlet容器即tomcat,对于web项目,如果使用的是外部tomcat,相关配置比如访问端口、资源路径等可以在tomcat的conf文件下配置。但是在boot中,tomcat配置又两种方式

    第一种:通过配置文件直接配置(推荐)

    #如果是tomcat相关的设置用server.tomcat.xx
    server.tomcat.uri-encoding=UTF-8
    #如果是servlet相关的配置用server.xx
    server.port=80

    第二种:通过配置类的方式


    3.Springboot中定义拦截器组件

    先定义一个拦截器组件
    package com.javayihao.top.blog.interceptor;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.lang.Nullable;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    @Component
    public class LoginInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            //判断session中是否存在用户
            if (request.getSession().getAttribute("user") == null) {
                response.sendRedirect("/admin");
                return false;
            }
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    
        }
    }

    然后将这个组件加入到boot中,在boot1版本中 通过继承WebmvcConfigureAdapter实现一个web配置,例如我们配置上面的拦截器

    @Configuration //声明这是一个配置
    public class LoginInterceptorConfig extends WebMvcConfigurerAdapter {
    @Resource
    private LoginInterceptor loginInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor).addPathPatterns("/admin/**").excludePathPatterns("/admin").excludePathPatterns("/admin/login");
    }
        }

     

    或者直接使用匿名类的方式

    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * 自定义一个登陆拦截器
     */
    @Configuration //声明这是一个配置
    public class LoginInterceptor extends WebMvcConfigurerAdapter {
        /*
        用来添加拦截器的方法
        InterceptorRegistry registry拦截器注册
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            //使用匿名内部类创建要给拦截器
            HandlerInterceptor loginInterceptor = new HandlerInterceptor() {
                @Override
                public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
                    //判断session中是否存在用户
                    if (request.getSession().getAttribute("user") == null) {
                        response.sendRedirect("/admin");
                        return false;
                    }
                    return true;
                }
    
                @Override
                public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
    
                }
    
                @Override
                public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    
                }
            };
            registry.addInterceptor(loginInterceptor).addPathPatterns("/admin/**").excludePathPatterns("/admin").excludePathPatterns("/admin/login");
        }
    }

     

    对于Sprinboot2版本,第一步还是定义一个拦截器组件

    第二不再是通过继承WebmvcConfigureAdapter实现一个web配置,而是实现接口WebMvcConfigurer增加一个配置

    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
    //引入我们的拦截器组件
        @Resource
        private LoginInterceptor loginInterceptor;
    //实现拦截器配置方法
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(loginInterceptor).addPathPatterns("/admin/**").excludePathPatterns("/admin").excludePathPatterns("/admin/login");
    
        }
    }

    4.Springboot中定义组件的方式

    第一种是通过xml的配置方式;第二种是通过全注解的方式

    建立一个测试类

    public class TestService {
    }

    新建一个beans.xml,写一个service的bean配置

     

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="testService"></bean>
    </beans>
    然后可以Application类里直接引用,也可以加载Configuration配置类上面
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.ImportResource;
    //Springboot中没有Spring配置文件,我们要想使自己写的文件配置进去,就通过ImportResource让配置文件里面的内容生效
    @SpringBootApplication
    @ImportResource(locations = {"classpath:beans.xml"})
    public class SpringbootPropertiesConfigApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringbootPropertiesConfigApplication.class, args);
        }
    
    }
    @SpringBootTest
    class SpringbootPropertiesConfigApplicationTests {
    
        //装载ioc容器
        @Autowired
        ApplicationContext ioc;
    
        @Test
        void contextLoads() {
            //测试这个bean是否已经加载到Spring容器
            boolean flag =  ioc.containsBean("testService");
            System.out.println(flag);
        }
    
    }

     

    经过测试,返回的是true,ok,换Springboot注解的方式实现

    新建一个PropertiesConfig配置类,注意:组件的id就是方法名

    import com.example.springboot.properties.service.TestService;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration //@Configuration注解实践上也是一个Component
    public class PerpertiesConfig {
        //通过@Bean注解将组件添加到Spring容器,组件的id就是方法名
        @Bean
        public TestService testService1(){
            return new TestService();
        }
    }

    测试

    @SpringBootTest
    class SpringbootPropertiesConfigApplicationTests {
    
        @Autowired
        ApplicationContext ioc;
    
        @Test
        void contextLoads() {
            //传方法名testService1
            boolean flag =  ioc.containsBean("testService1");
            System.out.println(flag);
        }
    
    }

     

    Junit测试,返回的还是TRUE,如果改下name为testService就是返回FALSE的,因为组件名称就是@Bean注解对应的方法名

    其实以前写Spring项目的时候,很显然也可以用@Service或者@Controller注解将组件添加到容器里,如果你去点一下源码,其实这些注解都有一个共同点就是都引入了@Component注解,而@Configuration注解,本质上也是引入了@Component注解,而@Bean是没有引入的,所以,如果你只加@Bean,而不加@Configuration注解的情况,是不可以将组件添加到Spring容器的

     

    总结:关于Springboot自动配置组件的时候注意的三点

    1.先看容器中有没有自己需要的组件,如果又,直接使用即可。如果没有自己需要的组件,自己配置

    2.Springboot中有许多的xxxConfiguer帮助我们进行扩展配置

    3.Springboot中有许多的xxxCustomizer帮助我们进行定制配置

    展开全文
  • Connector组件Tomcat核心组件之一,其主要负责监听服务器指定端口请求,解析请求报文为Request对象与Response对象。后将Request与Response传递至Container组件进行后续处理。 一、Connector组件

    Tomcat Connector组件的初始化


    前言

    Connector组件是Tomcat两大核心组件之一,其主要负责监听服务器指定端口的请求,解析请求报文为Request对象与Response对象。后将Request与Response传递至Container组件进行后续处理。


    一、Connector组件整体结构

    Connector运行基本流程如下所示。Endpoint负责监听请求,并将Socket传递至Processor。Processor对Socket中的请求报文进行解析,封装出Tomcat Request与Tomcat Response对象。Adaptor将获取到到的Tomcat Request与Tomcat Response对象进行进一步的解析,获得Servlet Request与Servlet Response,后继续向Container组件传递。
    在这里插入图片描述

    二、Connector组件的初始化

    1.构造方法

    在Connector类的构造方法中使用反射构造出Protocol实例(默认使用org.apache.coyote.http11.Http11NioProtocol类)。经类型转换后将ProtocolHandle对象的引用赋值给Connector对象的protocolHandle属性。

    public Connector(String protocol) {
            setProtocol(protocol);
            // Instantiate protocol handler
            ProtocolHandler p = null;
            try {
                Class<?> clazz = Class.forName(protocolHandlerClassName);
                p = (ProtocolHandler) clazz.getConstructor().newInstance();
            } catch (Exception e) {
                log.error(sm.getString(
                        "coyoteConnector.protocolHandlerInstantiationFailed"), e);
            } finally {
                this.protocolHandler = p;
            }
    
            if (Globals.STRICT_SERVLET_COMPLIANCE) {
                uriCharset = StandardCharsets.ISO_8859_1;
            } else {
                uriCharset = StandardCharsets.UTF_8;
            }
    
            // Default for Connector depends on this (deprecated) system property
            if (Boolean.parseBoolean(System.getProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "false"))) {
                encodedSolidusHandling = EncodedSolidusHandling.DECODE;
            }
        }
    
    

    在Http11NioProtocol的构造函数中调用了父类的构造函数,为Http11NioProtocol的endPoint属性赋值。我们可以一直追溯下去,直到AbstractHttp11Protocol类,我们可以看到AbstractHttp11Protocol的构造函数在调用了父类的构造函数后还为EndPoint类创建了Handle实例的引用。

       public Http11NioProtocol() {
            super(new NioEndpoint());
        }
    
        public AbstractHttp11Protocol(AbstractEndpoint<S> endpoint) {
            super(endpoint);
            setConnectionTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
            //创建Handle实例
            ConnectionHandler<S> cHandler = new ConnectionHandler<>(this);
            setHandler(cHandler);
            //为endpoint设置handle引用
            getEndpoint().setHandler(cHandler);
        }
    

    2.初始化触发

    Connector组件的初始化操作,由StandardService的initInternal()调用connector.init()触发:

        @Override
        protected void initInternal() throws LifecycleException {
    
            super.initInternal();
    
            if (engine != null) {
                engine.init();
            }
    
            // Initialize any Executors
            for (Executor executor : findExecutors()) {
                if (executor instanceof JmxEnabled) {
                    ((JmxEnabled) executor).setDomain(getDomain());
                }
                executor.init();
            }
    
            // Initialize mapper listener
            mapperListener.init();
    
            // Initialize our defined Connectors
            synchronized (connectorsLock) {
                for (Connector connector : connectors) {
                    try {
                    	//触发Connector进行初始化操作
                        connector.init();
                    } catch (Exception e) {
                        String message = sm.getString(
                                "standardService.connector.initFailed", connector);
                        log.error(message, e);
    
                        if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
                            throw new LifecycleException(message);
                    }
                }
            }
        }
    

    3.Connector的initInternal方法

    由于Connector类继承了LifecycleBase抽象类,故connector.init()其实是调用了从LifecycleBase继承来的init()方法。
    在init()方法内部,LifecycleBase又通过调用各个实现类 各自实现的initInternal()方法,来完成各个组件的定制化的初始化过程。

    在这里插入图片描述

    public final synchronized void init() throws LifecycleException {
          if (!state.equals(LifecycleState.NEW)) {
              invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
          }
    
          try {
              setStateInternal(LifecycleState.INITIALIZING, null, false);
              //在connector初始化过程中,这里会调用Connector的initInternal()方法
              initInternal();
              setStateInternal(LifecycleState.INITIALIZED, null, false);
          } catch (Throwable t) {
              handleSubClassException(t, "lifecycleBase.initFail", toString());
          }
    }
    
    protected void initInternal() throws LifecycleException {
    
            super.initInternal();
    
            // 初始化Adapter组件
            adapter = new CoyoteAdapter(this);
            protocolHandler.setAdapter(adapter);
    
            // Make sure parseBodyMethodsSet has a default
            if (null == parseBodyMethodsSet) {
                setParseBodyMethods(getParseBodyMethods());
            }
    
            if (protocolHandler.isAprRequired() && !AprLifecycleListener.isInstanceCreated()) {
                throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprListener",
                        getProtocolHandlerClassName()));
            }
            if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {
                throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprLibrary",
                        getProtocolHandlerClassName()));
            }
            if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.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 available
                    jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
                }
            }
    
            try {
            	//初始化protocolHandle组件
                protocolHandler.init();
            } catch (Exception e) {
                throw new LifecycleException(
                        sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
            }
      }
    
    

    4.protocolHandler.init()

    protocolHandler.init()主要调用了AbstractProtocol抽象类的init()方法,其主要是用于触发endpoint的初始化方法
    在这里插入图片描述

     public void init() throws Exception {
            if (getLog().isInfoEnabled()) {
                getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
            }
    
            if (oname == null) {
                // Component not pre-registered so register it
                oname = 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的初始化方法
            endpoint.init();
        }
    

    5.endpoint.bind()

    endpoint.init()由NioEndpoint从AbstractEndpoint抽象类继承而来,该方法内部主要调用了AbstractEndpoint中的抽象方法bind(),bind()方法的具体实现由具体的实现类进行复写。
    在此处默认由NioEndpoint类实现对bind()方法的复写。
    在这里插入图片描述

    public void bind() throws Exception {
    
            if (!getUseInheritedChannel()) {
            	//绑定要监听的端口地址 如:0.0.0.0/0.0.0.0:8080
                serverSock = ServerSocketChannel.open();
                socketProperties.setProperties(serverSock.socket());
                InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
                serverSock.socket().bind(addr,getAcceptCount());
            } else {
                // Retrieve the channel provided by the OS
                Channel ic = System.inheritedChannel();
                if (ic instanceof ServerSocketChannel) {
                    serverSock = (ServerSocketChannel) ic;
                }
                if (serverSock == null) {
                    throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
                }
            }
            serverSock.configureBlocking(true); //mimic APR behavior
    
            // Initialize thread count defaults for acceptor, poller
            if (acceptorThreadCount == 0) {
                // FIXME: Doesn't seem to work that well with multiple accept threads
                acceptorThreadCount = 1;
            }
            if (pollerThreadCount <= 0) {
                //minimum one poller thread
                pollerThreadCount = 1;
            }
            setStopLatch(new CountDownLatch(pollerThreadCount));
    
            // Initialize SSL if needed
            initialiseSsl();
    
            selectorPool.open();
     }
    

    参考文献

    链接: tomcat之Connector的结构.
    链接: Tomcat8源码分析系列-启动分析(二) Catalina初始化.

    展开全文
  • 前面一篇文章中记载了tomcat中几大组件是如何初始化,这里记载一下组件如何启动。通过前面我们知道组件初始化其实调用生命周期中init方法,那么我们可以猜测启动是不是也是调用生命周期中start方法。...

    前面一篇文章中记载了tomcat中几大组件是如何初始化的,这里记载一下组件如何启动。通过前面我们知道组件的初始化其实调用的生命周期中的init方法,那么我们可以猜测启动是不是也是调用的生命周期中的start方法。

    调用engine和connector的start()入口

    Bootstrap调用start 方法 进入Catalina然后调用StandardServer的start,再通过StandardServer启动StandardServer中的service来启动engine和connector

    容器部分的启动

    engine的启动过程

    engine的启动过程其实是调用的父类ContainerBase的startInternal方法。
    在这里插入图片描述
    接下来我们到源码中验证一下上图的流程
    StandardService
    当我们跟到StandardEngine的startInternal方法会发现其实里面只是调用了父类的startInternal方法
    StandardEngine
    跟入父类的startInternal
    ContainerBase
    这个里面有2个比较关键的方法;

    1. 根据下面这段代码 我们很清楚的可以看出其实这个for循环就是在调用子容器的start方法
    // 这里只摘取代码片段
        results.add(startStopExecutor.submit(new StartChild(children[i])));
    	private static class StartChild implements Callable<Void> {
           private Container child;
           public StartChild(Container child) {this.child = child;}
                 @Override
           public Void call() throws LifecycleException {
               child.start();
               return null;
           }
       }
    
    1. 谈到第二个关键的地方之前我们先回忆一下,在init过程中,我们的容器部分只初始化到了engine,而host 、context、wrapper都没有进行初始化调用,其实这3个组件的初始化发生在这个阶段。
      首先我们关注一下LifecycleBase 这个类,这个类是Lifecycle接口的最底层实现,我们可以看到一行关键的代码
      // 默认组件的生命周期是NEW
        private volatile LifecycleState state = LifecycleState.NEW;
        
      
      然后我们在去看此类中的start方法默认实现,我们会发现上面问题的答案就包含在这里
      LifecycleBase
      这样在启动容器的时候如果当前生命周期为new的时候会调用初始化方法,初始化组件。
      回到我们刚才ContainerBase 的 startInternal方法
      ContainerBase
      这个地方使用到了事件驱动编程,当tomcat中组件每切换一个状态都会通知它的生命周期监听器去做一些响应。

    host启动

    与engine中不同的是在 StandardHost的startInternal方法中多了一个对ErrorReportValve的处理,我们后面在讨论value的作用。
    然后我们会发现后面又到了在service中启动engine中降到的类ContainerBase的startInternal方法,由于上面我们分析过了engnie的启动过程,那么这里我们很容易得出结论,此处是在启动host的时候启动了context。所以我们需要关注Host在启动的时候 它的监听器干了什么。
    HostConfig
    在这里我们会发现一个很敏感的词汇 deploy,然后 我们再跟进去,会发现这里实现了tomcat 3种部署方式
    HostConfig
    具体的部署代码太多了,这里只拿我们关心的几行来看一看

    1. 此处就是为什么startStopThreads属性可以控制同时有多少个应用并发部署。HostConfig
    2. context的解析时机
      HostConfig
      这行代码是不是分外熟悉,在上一节tomcat的组件初始化中提到过,tomcat通过Digester实现了对xml配置文件的解析。然后我们在跟下去。
      HostConfig
      这里就实现了向host中加入context 完成应用的部署我们由上面的uml图知道容器会先findChildren,然后再启动这些子容器,最后再发出通知,当看到这个地方的时候是不是会有一个疑惑,我的子容器都已经start完毕了,这个时候才去通知监听器做应用部署,那不是没有时机启动?我们接着跟下去 会发现一切豁然开朗。
      ContainerBase
      ContainerBase

    context启动

    context启动过程中于前面的engine和host有点不同,StandardContext中的startInternal有自己的实现,虽然这个方法巴拉巴拉一大堆,但是我们此次关心的只有其中一部分。

    1. 构建一个webApp 级别的类加载器,这里又验证了在Bootstrap在load时候打破双亲委派机制的类加载器关系
      ContextConfig
    2. 注入wrapper容器并启动
      ContextConfig
      这个里面关键的代码在ContextConfig的webConfig方法,这个方法里面同样是一大堆逻辑,它大概的意思就是将配置信息组成了一个WebXml对象,同样进入我们所关心的核心方法configureContext
      ContextConfig
      在这个方法内我们又可以看到很多熟悉的东西,
      ContextConfig
      继续往下,我们终于发现了关键的代码
      ContextConfig
      在这段代码中出现了3个关键信息,createWrapper,setServletClass,addChild。至此我们的context中就加入了wrapper组件。

    wrapper 启动

    看了这么多关于context中如何创建注入wrapper的图,我们再来回顾下context创建wrapper的入口
    ContextConfig
    紧接着fireLifecycleEvent的执行完毕,我们的wrapper也随之启动。
    wrapper的启动相对context而言就要简单的多了
    StandardWrapper

    连接器部分的启动

    相对于容器部分的启动而言,连接器部分就要简单很多,因为连接器需要干的事情在init阶段已经干的差不多了。

    Connector的启动

    连接器中主要干的一件事是启动ProtocolHandler
    Connector

    ProtocolHandler的启动

    ProtocolHandler 启动时干的事情主要是通过父类AbstractProtocol
    启动EndpointProtocolHandler

    Endpoint的启动

    虽然endpoint启动的时候可以去调用bind(),但是如果设置了bindOnInit = true,在init阶段它就会去执行bind()
    AbstractEndPoint
    至于startInternal就要根据io的不同执行不同的子类,这里我们以NioEndpoint为例子
    NioEndPoint
    这个方法里面主要就是构造和启动acceptor、poller线程

    NioEndPoint
    tomcat组件启动时序图,没有详细画host构建context,context构建wrapper的过程

    展开全文
  • JavaWeb三大组件

    2019-07-16 22:09:10
    一、JavaWeb三大组件 Servlet,Listener,Filter.它们在JavaWeb开发中分别提供不同功能. JavaWeb三大组件都必须在Web.xml中配置 二、三大组件 1、Servlet Servlet作用是处理请求,服务器会把接收到请求交给...
  • JavaEE的三大组件一、Servlet1.JDBC简单实现过程2.JDBC在工程中的实现3.PreparedStatement对象二、数据库连接池1.简单的数据库连接池实现2.常见的开源数据库连接池1、DBCP数据库连接池2、C3P0数据库连接池3、Druid...
  • Spring boot注册三大组件

    千次阅读 2018-09-16 20:58:42
    注册三大组件 Spring boot 以jar包方式 使用嵌入式的Tomcat启动Web应用 没有Web应用目录结构,没有web.xml文件 Web应用 是一个Web应用目录结构 存在目录文件src/main/webapp/WEB-INF/web.xml 可以把三大...
  • JavaWeb三大组件的关系

    千次阅读 2018-04-11 13:39:37
    Servlet、ServletConfig、ServletContext、Filter、EventListener、web.xml、Tomcat 服务器之间关系图
  • SpringBoot中默认内置了Servlet容器,Tomcat。SpringBoot默认是以jar包的方式启动内置的...使用ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean来实现servle的三大组件。 Cust...
  • 友情提醒:再看这边文章之前,请先去看我的上一篇文章《(重温)JavaWeb--Servlet技术(一)(JavaWeb 的三大组件之一)》 1.HttpServletRequest 类 a)HttpServletRequest 类有什么作用。 每次只要有请求进入 ...
  • 过滤器JavaWeb三大组件之一,它与Servlet很相似!不它过滤器是用来拦截请求,而不是处理请求。 当用户请求某个Servlet时,会先执行部署在这个请求上Filter,如果Filter“放行”,那么会继承执行用户请求...
  • JavaWeb三大组件 ...而接下来要介绍javaweb三大组件都是运行再后端; Servlet:用java编写服务器端程序,依赖于Servlet容器(例如Tomcat),响应多个HTTP请求,生成动态Web内容; Filter:Filter是javaw...
  • 首先我们需要新建一个Web项目,并需要配置好Tomcat,以及引入我们所需 servlet-api.jar 包,目录结构如下 然后我们在com.test.servlet包下,新建一个TestServlet.class,并实现其HttpServlet接口,然后我们选择性...
  • servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)规则 将来我们自定义一个类,实现Servlet接口,复写方法 快速入门 创建JavaEE项目 2.定义一个类,实现Servlet接口 public class ServletDemo1 ...
  • http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/Servlet.html 一、Servlet继承结构体系图 从结构图中可以看出,实现一个自定义Servlet主要有3中方式: 实现最顶层Servlet接口,并...
  • Servlet概述 之所以学习Servlet是因为他是很多框架基础,例如SpringMVC...void init(ServletConfig config):初始化方法,Web容器如Tomcat创建Servlet时该方法执行一次; void service(ServletRequest requ...
  • 在SpringBoot中,使用是默认的Tomcat;在SpringBoot2.3.3版本里,默认是9.0.37; 1. 在配置文件中修改 如果想要修改相关默认配制,在SpringBoot中配置文件配置就可以了,方法基本上都是s
  • 4 Tomcat实现一键式启停 回顾一下总体架构和一个请求流转过程: 想让一个系统能够对外提供服务,需要创建、组装并...最直观做法就是将图上所有的组件按照先小后、先内后外顺序创建出来,然后组装在一起。 ...
  • Filter是JavaWeb的三大web组件之一Servlet、Filter、Listener > Filter的作用是在请求到达WEB资源(HTML、CSS、Servlet、JSP)之前进行拦截。 > Filter需要交给Tomcat访问器来管理 > Filter...
  • Filter需要交给Tomcat访问器来管理 作用 Filter可以在请求到达目标资源之前进行拦截 Filter也可以放行请求 Filter可以在响应到达浏览器之前做一个预处理 二、Filter创建和使用 创建 鼠标右键单机src->点击New-&...
  • 过滤器JavaWeb三大组件之一,它与Servlet很相似!不它过滤器是用来拦截请求,而不是处理请求。  当用户请求某个Servlet时,会先执行部署在这个请求上Filter,如果Filter“放行”,那么会继承执行用户请求...
  • SpringBoot配置嵌入式容器及三大组件

    千次阅读 2018-09-11 15:04:14
    配置端口号,项目名等我们除了可以通过application.properties配置文件配置外,我们可以用代码自定义配置tomcat的相关设置。 server的基本配置 通过配置文件配置端口号 server.port= 9999 spring.thym...
  • 文章目录Listener:监听器观察者模式:JavaWeb中监听器JavaWeb中完成编写监听器:感知监听(感知自身状态变化)配置Tomcat钝化session参数 Listener:监听器 监听器: 它是一个接口,内容由我们来实现; 它...
  • 三大Web组件 Servlet,listener,filter Listener 监听器 Web开发过程中监听器 监听对象: 监听事件: 监听者: 触发事件: 使用:编写一个类实现;注册 怎么做到? servlet创建代码是tomcat内部 多态?? 将...
  • Filter的三个方法 void init(FilterConfig):在Tomcat启动时被调用; void destroy():在Tomcat关闭时被调用; void doFilter(ServletRequest,ServletResponse,FilterChain):每次有请求时都调用该方法; ...
  • Tomcat的系统架构简介

    2020-09-29 17:20:47
    连接器的三大组件1.ProtocolHandler 组件1.ProtocolHandler 组件之Endpoint2.ProtocolHandler 组件之Processor2.Adapter 组件3.Tomcat的组件-Servlet容器1.Tomcat容器的层次结构 1.Servlet 在介
  • 关于嵌入式Servlet容器,在我们曾经传统web项目中,需要将项目打包成war包,部署在Tomcat中,并配置好Tomcat环境,将其作为容器,外部启动Tomcat就可以运行项目了。 而我们SpringBoot是无需外部启动Tomcat,使用...
  • 定义tomcat的两种方式:一种:在配置文件中改: 这种的原理是实现WebServerFactoryCustomizer 2.0之前的是EmbeddedServletContainerCustomizer优先级小于下面这种鞋配置类。 我们再看第二种,实现较为简单: @...

空空如也

空空如也

1 2 3 4 5 ... 19
收藏数 370
精华内容 148
关键字:

tomcat的三大组件