版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/u010597819/article/details/90745546
springboot启动刷新上下文也是走的AbstractApplicationContext细节不再多说,我们直接进入正题,在onRefresh这个环节是springboot接入内嵌web容器的环节。在创建上下文时系统会判断当前是否是web环境,如果是则创建AnnotationConfigEmbeddedWebApplicationContext默认的web上下文。该上下文中的onRefresh环节进行创建内嵌的容器
@Override
protected void onRefresh() {
super.onRefresh();
try {
createEmbeddedServletContainer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start embedded container",
ex);
}
}
创建内嵌的Servlet容器
- 获取内嵌的容器工厂EmbeddedServletContainerFactory,假定为jetty环境即JettyEmbeddedServletContainerFactory
- 使用工厂方法获取内嵌的servlet容器,入参为_ServletContextInitializer上下文初始化对象列表_
获取spring-web自身的ServletContextInitializer列表
org.springframework.boot.web.servlet.ServletContextInitializer
- 创建ServletContextInitializerBeans
- 根据类型ServletContextInitializer从工厂中获取对象列表遍历
- 如果是ServletRegistrationBean类型以Servlet类型为key添加至initializers
- 如果是FilterRegistrationBean类型,获取对象的Filter属性并以Filter类型添加至initializers
- 如果是DelegatingFilterProxyRegistrationBean类型同Filter相同,source需要通过代理的getTargetBeanName获取
- 如果是ServletListenerRegistrationBean类型,获取对象的listener属性并以EventListener类型添加至initializers
- 其他按照ServletContextInitializer类型添加至initializers
- addAdaptableBeans:根据类型(Servlet、Filter、EventListener)从工厂中获取bean,将bean封装为对应的RegistrationBean并添加至initializers(后面会遍历列表注册bean至ServletContext上下文)
- 排序initializers
springboot自动注册Servlet至ServletContext
回调spring-web自身ServletContextInitializer.onStartup
遍历ServletContextInitializer回调onStartup方法入参为ServletContext,与Servlet对接
- 创建ServletContextInitializerBeans:根据类型(ServletContextInitializer)从工厂中获取ServletContextInitializer列表
- 遍历ServletContextInitializerBeans集合,调用结合元素的onStartup方法,即sorted列表,常见的列表(ServletContextInitializer的ServletRegistrationBean实现类型):characterEncodingFilter、hiddenHttpMethodFilter、httpPutFormContentFilter、springSecurityFilterChain、dispatcherServlet、requestContextListener、SessionRepositoryFilter(共享session的Filter)
@Override
public Iterator<ServletContextInitializer> iterator() {
//sortedList排序后的ServletRegistrationBean列表
return this.sortedList.iterator();
}
- springmvc的dispatchServlet由DispatcherServletAutoConfiguration配置自动注册,其余由应用本身配置注册或其他自动配置注册
- 均为ServletRegistrationBean类型。ServletRegistrationBean类的onStartup方法将绑定的servlet注册至ServletContext上下文并配置
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
Assert.notNull(this.servlet, "Servlet must not be null");
String name = getServletName();
...
logger.info("Mapping servlet: '" + name + "' to " + this.urlMappings);
//注册至ServletContext上下文
Dynamic added = servletContext.addServlet(name, this.servlet);
if (added == null) {
logger.info("Servlet " + name + " was not registered "
+ "(possibly already registered?)");
return;
}
//配置servlet,urlMapping、setLoadOnStartup、setMultipartConfig
configure(added);
}
Jetty内嵌容器工厂获取容器
- 获取容器JettyEmbeddedServletContainerFactory.getEmbeddedServletContainer
- 创建应用上下文JettyEmbeddedWebAppContext(ServletContextHandler类型),在后面的handle链调用中回调。Server调用handle链的根节点(即第一个handle)
- 父类ServletContextHandler执行relinkHandlers,doSetHandler将handler关联至当前handler的_handler属性,并更新注册至当前容器的_beans中,下面均相同逻辑不赘述
- 创建SessionHandler并关联至_sessionHandler,doSetHandler
- 创建SecurityHandler并关联至_securityHandler,doSetHandler
- 创建GzipHandler并关联至_gzipHandler,doSetHandler
- 创建ServletHandler并关联至_servletHandler,doSetHandler
- 获取端口号port默认为0
- 根据地址与端口创建SocketAddress实例InetSocketAddress
- 如果Server对象存在ThreadPool类型入参的构造器则使用jetty9的工厂方法创建Server(org.eclipse.jetty.server.Server);否则使用jetty8(8使用set方法注入线程池);入参为工厂线程池属性。如果连接池为空,构造器中会指定QueuedThreadPool为默认的线程池,最大200线程,最小线程数为8,空闲超时时间60000毫秒(1分钟),线程池名称为qtp前缀+hashcode,线程名称为qtp前缀+线程id
- 根据InetSocketAddress与Server创建Connector,jetty8使用org.eclipse.jetty.server.nio.SelectChannelConnector,jetty9使用org.eclipse.jetty.server.ServerConnector
- Server绑定connector
- 配置WebAppContext上下文configureWebAppContext
- web上下文设置contextPath,如果有则设置,默认为根"/"
- 如果指定则添加默认servlet:addDefaultServlet
- 如果需要注册JspServlet则添加,并添加JasperInitializer bean
- 添加字符集
- 设置ServletContextInitializerConfiguration入参为Initializer列表
- 配置session:configureSession;上下文获取SessionHandler句柄,如果存在超时则设置超时时间,默认为-1;如果指定persistSession需要持久化session则创建默认缓存DefaultSessionCache,为缓存设置文件存储FileSessionDataStore实例。将session缓存绑定至SessionHandler句柄
- 为Server设置handler句柄
- 如果启用了压缩,使用GzipHandler包装JettyEmbeddedWebAppContext(原始handler)
- 如果header包含text,使用ServerHeaderHandler包装handler
- _handler更新后更新bean:updateBean,添加至当前Server的_beans中在Server容器start启动时同时启动_beans列表中的管理bean
- 如果启用ssl,配置ssl并使用ssl的Connector覆盖原Server的Connector
- 获取JettyServerCustomizer列表,为server绑定自定义配置
- 如果启用useForwardHeaders,为server绑定ForwardHeadersCustomizer自定义配置
- 创建JettyEmbeddedServletContainer实例,入参为Server。如果端口大于0则设置自动启动容器为true。
创建容器JettyEmbeddedServletContainer
- 初始化
- 启动server:org.eclipse.jetty.server.Server
- 调用父类启动方法AbstractLifeCycle.start
- 调用子类Server重写的doStart方法
- 配置ErrorHandler
- 调用父类doStart,遍历ContainerLifecycleLifecycle的_beans,如果bean是managed类型并且没启动调用start方法启动,如果是auto类型,running状态则移除该bean,否则置为managed类型并启动bean
- 遍历Server的connectors列表,启动connector,假定为jetty9的ServerConnector
for (Connector connector : _connectors)
{
try
{
connector.start();
}
catch(Throwable e)
{
mex.add(e);
}
}
服务器连接器ServerConnector
创建ServerConnector
- 根据工厂acceptors数量、selector数量创建连接器
- 调用父类构造器AbstractConnector
- 将server添加至ContainerLifecycle中的beans,类型为UNMANAGED
- 将_executor添加至ContainerLifecycle中的beans,类型为:如果已经running则为UNMANAGED,否则为AUTO类型,如果不是Lifecycle类型则类型为POJO
- _scheduler注册至beans同_executor逻辑
- _byteBufferPool(默认为ArrayByteBufferPool类型)注册至beans同_executor逻辑
- 如果acceptors处理数量指定数量小于0,取1与(4与核心数量除以8之间的最小值)两者中的最大值
- 根据acceptor数量创建线程数组_acceptors
- 创建新的Selector管理器ServerConnectorManager,根据executor、scheduler、selectors数量
- 添加_manager至beans,类型为Managed
- 为Connector设置host、port
启动ServerConnector
- 调用父类start方法回调子类doStart方法
- AbstractNetworkConnector父类回调子类open方法,调用父类doStart方法
- 如果_acceptChannel为空创建,打开ServerSocketChannel通道绑定地址端口,设置_acceptChannel阻塞为true
- 添加_acceptChannel至beans,类型为managed
- 继续调用父类AbstractConnector doStart方法
- 继续调用父类ContainerLifecycle doStart方法,即调用新注册进来bean的start方法,如果存在auto类型并且running状态且不是unmanaged类型,是InheritedListener类型则移除bean,否则置为unmanaged类型。否则调用start方法
- 根据_acceptors长度创建CountDownLatch监听_stopping
- 根据_acceptors长度创建Acceptor,并将Acceptor注册至bean(POJO类型),获取线程池执行Acceptor
执行Acceptor
- 如果不是处于accepting并且是running状态,_setAccepting等待通知
- 回调ServerConnector连接器accept方法
@Override
public void accept(int acceptorID) throws IOException
{
ServerSocketChannel serverChannel = _acceptChannel;
if (serverChannel != null && serverChannel.isOpen())
{
SocketChannel channel = serverChannel.accept();
accepted(channel);
}
}
- accept打开channel并设置为非阻塞类型
- 设置socket的SocketOptions.TCP_NODELAY属性打开
- SelectorManager选择管理器ServerConnectorManager监听服务器通道accept(channel)
//SelectorManager监听通道
public void accept(SelectableChannel channel, Object attachment) {
ManagedSelector selector = this.chooseSelector();
Objects.requireNonNull(selector);
//将ServerSocketChannel封装为Accept提交至updates,等待消费
selector.submit(new Accept(selector, channel, attachment));
}
//SelectorManager容器管理初始化
protected SelectorManager(Executor executor, Scheduler scheduler, int selectors) {
this._selectorIndex = new AtomicInteger();
this._acceptListeners = new ArrayList();
this._connectTimeout = 15000L;
if (selectors <= 0) {
//选择器默认数量
//如果线程池是SizedThreadPool类型:为cpu/2与线程池最大线程数/16之间的最小值
//否则cpu/2
selectors = defaultSelectors(executor);
}
this.executor = executor;
this.scheduler = scheduler;
this._selectors = new ManagedSelector[selectors];
//选择索引算法为index递增对长度取余即轮询方法
this._selectorIndexUpdate = (index) -> {
return (index + 1) % this._selectors.length;
};
}
//容器管理的bean会在初始化管理器之后,启动管理选择器ManagedSelector,也是通过容器启动
protected void doStart() throws Exception {
this._lease = ThreadPoolBudget.leaseFrom(this.getExecutor(), this, this._selectors.length);
for(int i = 0; i < this._selectors.length; ++i) {
ManagedSelector selector = this.newSelector(i);
this._selectors[i] = selector;
this.addBean(selector);
}
super.doStart();
}
- 选择ManagedSelector容器管理的Selector,提交Accept至_updates(此时attachment为null),如果选择器_selecting为ture则选择当前选择器并唤醒选择器Selector的select阻塞
//ManagedSelector提交监听的Accept对象至队列ArrayDeque
public void submit(ManagedSelector.SelectorUpdate update) {
...
Selector selector = null;
synchronized(this) {
this._updates.offer(update);
//processUpdates处理之后将_selecting置为true开始唤醒selector的select阻塞
if (this._selecting) {
selector = this._selector;
this._selecting = false;
}
}
if (selector != null) {
...
//唤醒selector select阻塞
selector.wakeup();
}
}
//容器初始化时启动bean
protected void doStart() throws Exception {
super.doStart();
//调用选择管理器newSelector方法创建打开选择器Selector
_selector = _selectorManager.newSelector();
// The producer used by the strategies will never
// be idle (either produces a task or blocks).
// The normal strategy obtains the produced task, schedules
// a new thread to produce more, runs the task and then exits.
//策略:EatWhatYouKill,通常为从不idle空闲,要么生产task,要么阻塞
_selectorManager.execute(_strategy::produce);
// Set started only if we really are started
Start start = new Start();
submit(start);
start._started.await();
}
- ServerConnectorManager容器管理启动时,初始化创建ManagedSelector,ManagedSelector构造器中调用_selectorManager打开Selector,容器管理的doStart中执行EatWhatYouKill策略的produce生产方法,即SelectorProducer
选择器生产者SelectorProducer
EatWhatYouKill不断的调用SelectorProducer执行生产方法返回的任务
processSelected
- 获取已存在的SelectionKey(channel注册至selector上时返回的SelectionKey,attachment为SocketChannelEndPoint)
- 如果SelectionKey.attachment对象是ManagedSelector.Selectable类型返回task
- 回调attachment的onSelected方法,即SocketChannelEndPoint.onSelected,调用父类的ChannelEndPoint.onSelected
- 根据SelectionKey的操作类型返回读(_runFillable)、写(_runCompleteWrite)或读写(_runCompleteWriteFillable)双工的入任务
- 执行Callback回调任务,例如:ReadCallback回调succeeded,调用连接的onFillable方法,假定为HttpConnection,连接读取数据
- 如果是isConnectable则processConnect
- 如果是EndPoint则关闭EndPoint
processUpdates
将_updates队列中的SelectorUpdate数据封装的channel注册至指定的selector,并将当前channel与SelectionKey、Selector封装为相应的EndPoint对象(例如:SocketChannelEndPoint),将EndPoint绑定至SelectionKey的attachment
- 处理updates队列
- 将updates队列转移至updateable队列
- 遍历_updateable调用Start的update方法,设置ManagedSelector.this.started为true
- Start._started执行countdown
- 如果_updates不为空则唤醒selector(同Accept类型消息处理逻辑)
- 处理updates队列(Accept类型消息)
- 调用Accept的update方法,将Accept绑定的channel注册至当前选择的Selector,返回的SelectionKey的attachment设置为后面创建的EndPoint
- 调用_selectorManager执行当前Accept,即提交任务至线程池
- 创建EndPoint:选择管理器_selectorManager将当前服务器channel及SelectionKey封装为相应的EndPoint(例如:Selectable、isConnectable、EndPoint类型)绑定至SelectionKey
- 创建Connection:例如:HttpConnection
- 打开SocketChannelEndPoint.onOpen
- 将endPoint添加至连接器的_endPoints
- 打开连接器onOpened
- 将ReadCallback注册至endPoint的_fillInterest
- 遍历监听器_acceptListeners回调onAccept方法
- 清空_updateable
- 如果_updates为空则设置_selecting标识为true,表示正在选择,并唤醒selector,即通知selector的select方法阻塞消费
updateKeys
select
- 执行生产者的select选择方法,执行Selector的select方法
- 阻塞在selector的select方法中等待数据到达
- 如果存在数据则将selectedKeys绑定至_keys
- 将_keys列表的迭代器赋值至_cursor游标等待processSelected消费游标中的数据
- 如果selector已关闭返回false不再继续消费
HttpConnection连接
- onFillable读取数据
- fillRequestBuffer填充request的堆外内存buffer
- parseRequestBuffer解析requestBuffer,解析器:HttpParser
- handle通道事件channel event,如果不能handle事件继续读取,如果可以则执行handle
- HttpChannelOverHttp处理事件:handle(),调用父类HttpChannel.handle
- HttpChannelState获取Http通道状态Action:HttpChannelState.Action
- 根据Action类型处理事件,例如:Dispatch事件,将事件封装为Request请求
- 遍历监听器列表回调监听器accept方法入参为Request对象
- get获取Server处理(handle)当前通道,假定为异步请求事件
- Server.handleAsync处理HttpChannel通道
- 解析事件获取HttpServletRequest、HttpServletResponse、HttpChannel通道的Request、target资源路径,调用handle
public void handleAsync(HttpChannel channel) throws IOException, ServletException
{
final HttpChannelState state = channel.getRequest().getHttpChannelState();
final AsyncContextEvent event = state.getAsyncContextEvent();
final Request baseRequest=channel.getRequest();
final String path=event.getPath();
if (path!=null)
{
// this is a dispatch with a path
ServletContext context=event.getServletContext();
String query=baseRequest.getQueryString(); baseRequest.setURIPathQuery(URIUtil.addEncodedPaths(context==null?null:URIUtil.encodePath(context.getContextPath()), path));
HttpURI uri = baseRequest.getHttpURI();
baseRequest.setPathInfo(uri.getDecodedPath());
if (uri.getQuery()!=null)
baseRequest.mergeQueryParameters(query,uri.getQuery(), true); //we have to assume dispatch path and query are UTF8
}
final String target=baseRequest.getPathInfo();
final HttpServletRequest request=(HttpServletRequest)event.getSuppliedRequest();
final HttpServletResponse response=(HttpServletResponse)event.getSuppliedResponse();
...
handle(target, baseRequest, request, response);
...
}
handle处理
- handle链例如:JettyEmbeddedWebAppContext->ServletContextHandler->SessionHandler->SecurityHandler->ServletHandler
- 调用handle链,第一个handle是前面讲到的:JettyEmbeddedWebAppContext,Server容器启动时回调handle的WebAppContext.doStart方法
- 调用ServletContextHandler.doStart方法,ContextHandler调用startContext方法,获取getChildHandlerByClass子节点(ScopedHandler类型),
- ServletHandle由ContainerLifecycle管理启动doStart,doStart中执行ServletHandle初始化方法
- ServletHandler.doStart执行
- 更新NameMapping:updateNameMappings,更新filterNameMapping、servletNameMapping
- 更新updateMappings,如果contextHandler不为空并且已经启动,则执行initialize初始化方法
- 执行FilterHolder.start方法
- 执行FilterHolder.initialize方法
- 遍历ServletHolder列表
- 执行ServletHolder.start方法
- 执行doStart方法
- 执行ServletHolder.initailize方法
- 如果_initOnStartup则执行initServlet方法,即执行servlet的init方法
- 获取Holder beans,遍历执行start、initialize方法
- 调用handle链,第一个handle是前面讲到的:JettyEmbeddedWebAppContext,其他的handle节点,例如:ScopedHandler句柄处理,handle方法调用doHandle方法
- 调用ContextHandler.doHandle方法
- 调用ServletHandler.doHandle方法
- 执行ServletHandler.prepare方法:如果处于running状态并且_unavailable不等于0或者不是_initOnStartup则执行servlet的init方法
- 如果过滤器链不为空调用过滤链,执行filter.doFilter方法,否则调用handle方法:执行servlet的service方法
- springboot与jetty对接:org.springframework.boot.context.embedded.jetty.JettyEmbeddedWebAppContext,上下文在springboot获取内嵌Servlet容器时注册至Server(也是handle链中的一个节点);JettyEmbeddedServletContainerFactory.getEmbeddedServletContainer
总结
- 整体可以看到jetty容器为我们做了很多事情,端口的监听已经服务的线程池,网络io处理。而servlet则是我们的应用控制器,处理请求和响应(图片摘自Head First Servlet and JSP)