Tomcat源码:SocketProcessor、ConnectionHandler与Http11Processor

2023-11-15

 前文:

《Tomcat源码:启动类Bootstrap与Catalina的加载》

《Tomcat源码:容器的生命周期管理与事件监听》

《Tomcat源码:StandardServer与StandardService》

《Tomcat源码:Container接口》

《Tomcat源码:StandardEngine、StandardHost、StandardContext、StandardWrapper》

 《Tomcat源码:Pipeline与Valve》

《Tomcat源码:连接器与Executor、Connector》

《Tomcat源码:ProtocolHandler与Endpoint》

《Tomcat源码:Acceptor与Poller、PollerEvent》

前言

        前文中我们介绍了Acceptor与Poller,其中Acceptor负责监听socket连接,并将请求转交到Poller中调用processSocket方法处理。

        结合我们之前介绍连接器时的讲解,EndPoint 接收到 Socket 连接后,生成一个 SocketProcessor 任务提交到线程池去处理,SocketProcessor 的 Run 方法会调用 Processor 组件去解析应用层协议,这一操作的起点就是processSocket方法,下面我们就从该方法开始讲起。

目录

前言

一、SocketProcessor

        1、processSocket

        2、createSocketProcessor

        3、doRun

        3.1、isHandshakeComplete        

        3.2、ConnectionHandler        

         3.3、registerReadInterest、registerWriteInterest        

二、ConnectionHandler

        1、构造方法

        2、process

        2.1、removeWaitingProcessor、addWaitingProcessor

        2.2、longPoll

        2.3、release

三、Http11Processor

        1、Http11Processor的创建

        1.1、createProcessor

        1.2、构造方法

        2、process

        3、service


一、SocketProcessor

        1、processSocket

        首先尝试从processorCache(AbstractEndpoint中的成员变量,前文中有过介绍,类型为SynchronizedStack<SocketProcessorBase<S>>)中获取现有的SocketProcessorBase对象,如果没有则调用createSocketProcessor方法创建,有的话则调用rest方法重置socket包装类与监听事件。

        在SocketProcessor创建完了后,根据dispatch是否为true与 executor 是否为空来判断是否将当前连接的处理放入线程池中等待处理还是直接进行方法调用处理。

public abstract class AbstractEndpoint<S,U> {
    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);
            getLog().error(sm.getString("endpoint.process.fail"), t);
            return false;
        }
        return true;
    }
}

        2、createSocketProcessor

        可以看到这个方法直接new了一个SocketProcessor对象出来

public class NioEndpoint extends AbstractJsseEndpoint<NioChannel,SocketChannel> {
    protected SocketProcessorBase<NioChannel> createSocketProcessor(
        SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
            return new SocketProcessor(socketWrapper, event);
        }
    }
}

         SocketProcessor的构造方法使用了super来调用父类构造方法,而父类SocketProcessorBase的构造方法则是通过rest方法给socketWrapper与event这两个成员变量来赋值。

protected class SocketProcessor extends SocketProcessorBase<NioChannel> {
    public SocketProcessor(SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
         super(socketWrapper, event);
        }
}

public abstract class SocketProcessorBase<S> implements Runnable {
    protected SocketWrapperBase<S> socketWrapper;
    protected SocketEvent event;

    public SocketProcessorBase(SocketWrapperBase<S> socketWrapper, SocketEvent event) {
        reset(socketWrapper, event);
    }

    public void reset(SocketWrapperBase<S> socketWrapper, SocketEvent event) {
        Objects.requireNonNull(event);
        this.socketWrapper = socketWrapper;
        this.event = event;
    }
}

        3、doRun

        run方法的实现在父类SocketProcessorBase中,可以看到这里将处理逻辑交给了doRun来实现

public abstract class SocketProcessorBase<S> implements Runnable {
    public final void run() {
        synchronized (socketWrapper) {
            if (socketWrapper.isClosed()) {
                return;
            }
            doRun();
        }
    }
}

        doRun方法的实现在子类SocketProcessor 中,步骤比较长,但可以简单归结为两步,首先是根据socketWrapper与event的状态给handshake 赋值,然后根据handshake 的值走不同的处理逻辑。

protected class SocketProcessor extends SocketProcessorBase<NioChannel> {
        protected void doRun() {
            Poller poller = NioEndpoint.this.poller;
            if (poller == null) {
                socketWrapper.close();
                return;
            }

            try {
                int handshake = -1;
                try {
                    if (socketWrapper.getSocket().isHandshakeComplete()) {
                        handshake = 0;
                    } else if (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||
                            event == SocketEvent.ERROR) {
                        handshake = -1;
                    } else {
                        handshake = socketWrapper.getSocket().handshake(event == SocketEvent.OPEN_READ, event == SocketEvent.OPEN_WRITE);
                        event = SocketEvent.OPEN_READ;
                    }
                } catch (IOException x) {
                    handshake = -1;
                    if (logHandshake.isDebugEnabled()) {
                        logHandshake.debug(sm.getString("endpoint.err.handshake",
                                socketWrapper.getRemoteAddr(), Integer.toString(socketWrapper.getRemotePort())), x);
                    }
                } catch (CancelledKeyException ckx) {
                    handshake = -1;
                }
                if (handshake == 0) {
                    SocketState state = SocketState.OPEN;
                    // Process the request from this socket
                    if (event == null) {
                        state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
                    } else {
                        state = getHandler().process(socketWrapper, event);
                    }
                    if (state == SocketState.CLOSED) {
                        poller.cancelledKey(getSelectionKey(), socketWrapper);
                    }
                } else if (handshake == -1 ) {
                    getHandler().process(socketWrapper, SocketEvent.CONNECT_FAIL);
                    poller.cancelledKey(getSelectionKey(), socketWrapper);
                } else if (handshake == SelectionKey.OP_READ){
                    socketWrapper.registerReadInterest();
                } else if (handshake == SelectionKey.OP_WRITE){
                    socketWrapper.registerWriteInterest();
                }
            } catch (CancelledKeyException cx) {
                poller.cancelledKey(getSelectionKey(), socketWrapper);
            } catch (VirtualMachineError vme) {
                ExceptionUtils.handleThrowable(vme);
            } catch (Throwable t) {
                log.error(sm.getString("endpoint.processing.fail"), t);
                poller.cancelledKey(getSelectionKey(), socketWrapper);
            } finally {
                socketWrapper = null;
                event = null;
                //return to cache
                if (running && processorCache != null) {
                    processorCache.push(this);
                }
            }
        }
}

        3.1、isHandshakeComplete        

        isHandshakeComplete分别在NioChannel 与其子类SecureNioChannel中进行了重写,这里我们按标准的请求流程来看下NioChannel中的实现,可以看到这里始终为true。后续调用的handshake方法也一样,在NioChannel中始终为0。

public class NioChannel implements ByteChannel, ScatteringByteChannel, GatheringByteChannel {
    public boolean isHandshakeComplete() {
        return true;
    }

    public int handshake(boolean read, boolean write) throws IOException {
        return 0;
    }
}

        3.2、ConnectionHandler        

        看过了上面NioChannel中的两个方法实现后,可以确定在doRun方法中handshake为0。然后我们看到这里如果event为空则默认当作读事件处理,处理方法为调用Handler的process方法。然后得到一个 SocketState 对象 state,如果 state 的值为SocketState.CLOSED,则执行 close(socket, key) 方法。

      if (handshake == 0) {
            SocketState state = SocketState.OPEN;
            // Process the request from this socket
            if (event == null) {
                state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
             } else {
                state = getHandler().process(socketWrapper, event);
             }
             if (state == SocketState.CLOSED) {
                poller.cancelledKey(getSelectionKey(), socketWrapper);
             }
      } else if (handshake == -1 ) {
            getHandler().process(socketWrapper, SocketEvent.CONNECT_FAIL);
            poller.cancelledKey(getSelectionKey(), socketWrapper);
      } else if (handshake == SelectionKey.OP_READ){
            socketWrapper.registerReadInterest();
      } else if (handshake == SelectionKey.OP_WRITE){
            socketWrapper.registerWriteInterest();
       }

        这里的getHandler获取的是AbstractProtocol构造方法中传入的ConnectionHandler,该类我们会在下文介绍。

    public AbstractProtocol(AbstractEndpoint<S, ?> endpoint) {
        this.endpoint = endpoint;
        ConnectionHandler<S> cHandler = new ConnectionHandler<>(this);
        setHandler(cHandler);
        getEndpoint().setHandler(cHandler);
        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
    }

         3.3、registerReadInterest、registerWriteInterest        

        如果 handshake 的值是 SelectionKey.OP_READ 或者 SelectionKey.OP_WRITE 的话,就调用 socketWrapper.registerReadInterest() 或者 socketWrapper.registerWriteInterest() 通过poller重新注册感兴趣事件。

public static class NioSocketWrapper extends SocketWrapperBase<NioChannel> {
        public void registerReadInterest() {
            if (log.isDebugEnabled()) {
                log.debug(sm.getString("endpoint.debug.registerRead", this));
            }
            getPoller().add(this, SelectionKey.OP_READ);
        }

        public void registerWriteInterest() {
            if (log.isDebugEnabled()) {
                log.debug(sm.getString("endpoint.debug.registerWrite", this));
            }
            getPoller().add(this, SelectionKey.OP_WRITE);
        }
}

        可以看出,SocketProcessor这里的作用是作为连接池中的一个执行单位来提交poller中传递过来的请求,而在其异步线程的处理步骤中,又会将任务递交给ConnectionHandler。

 

二、ConnectionHandler

        1、构造方法

        ConnectionHandler的构造方法中传入了Http11NioProtocol对象

    protected static class ConnectionHandler<S> implements AbstractEndpoint.Handler<S> {
        private final AbstractProtocol<S> proto;
        public ConnectionHandler(AbstractProtocol<S> proto) {
            this.proto = proto;
        }
    }

        2、process

        process的过程比较复杂,这里单独拎出来我们本次要讲的标准HTTP1.1的流程来看。

        首先是获取processor 对象,如果有的话将其从waitingProcessors中剔除,如果没有再尝试从缓存recycledProcessors中获取,都没有则调用createProcessor创建一个。processor对象有了之后调用其process方法,并返回方法结果state,后续根据该结果进行不同的处理。

protected static class ConnectionHandler<S> implements AbstractEndpoint.Handler<S> {
    public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
		// ...
        S socket = wrapper.getSocket();
        Processor processor = (Processor) wrapper.takeCurrentProcessor();
		// ...
		try{
			// ...
            if (processor != null) {
                getProtocol().removeWaitingProcessor(processor);
            } else if (status == SocketEvent.DISCONNECT || status == SocketEvent.ERROR) {
                return SocketState.CLOSED;
            }
			// ...
            if (processor == null) {
                processor = recycledProcessors.pop();
            }
            if (processor == null) {
                processor = getProtocol().createProcessor();
                register(processor);
            }
			// ...
            SocketState state = SocketState.CLOSED;
            do {
                state = processor.process(wrapper, status);
                if (state == SocketState.UPGRADING) {
                        // ...
                }
            } while (state == SocketState.UPGRADING);

            if (state == SocketState.LONG) {
                longPoll(wrapper, processor);
                if (processor.isAsync()) {
                    getProtocol().addWaitingProcessor(processor);
                }
            } else if (state == SocketState.OPEN) {
                release(processor);
                processor = null;
                wrapper.registerReadInterest();
            } else if (state == SocketState.SENDFILE) {
            } else if (state == SocketState.UPGRADED) {
                if (status != SocketEvent.OPEN_WRITE) {
                   longPoll(wrapper, processor);
                   getProtocol().addWaitingProcessor(processor);    
				}
            } else if (state == SocketState.SUSPENDED) {
            } else {
            }

            if (processor != null) {
                wrapper.setCurrentProcessor(processor);
            }
            return state;
        }

        release(processor);
        return SocketState.CLOSED;
    }
}

        2.1、removeWaitingProcessor、addWaitingProcessor

        removeWaitingProcessor、addWaitingProcessor都是操作WaitingProcessor的方法

public abstract class AbstractProtocol<S> implements ProtocolHandler, MBeanRegistration { 
   public void removeWaitingProcessor(Processor processor) {
        waitingProcessors.remove(processor);
    }

    public void addWaitingProcessor(Processor processor) {
        waitingProcessors.add(processor);
    }
}

        2.2、longPoll

        longPoll 判断是否异步,这里默认为false因此会调用 socket.registerReadInterest() 方法,该方法在上篇文章里讲过了,这里就不多赘述。 

protected void longPoll(SocketWrapperBase<?> socket, Processor processor) {
    if (!processor.isAsync()) {
        socket.registerReadInterest();
    }
}

        2.3、release

        从方法名可以推测出该方法用于释放process资源,如果不是升级协议则放入recycledProcessors缓存中供后续请求使用。

        private void release(Processor processor) {
            if (processor != null) {
                processor.recycle();
                if (processor.isUpgrade()) {
                    getProtocol().removeWaitingProcessor(processor);
                } else {
                    recycledProcessors.push(processor);
                    if (getLog().isDebugEnabled()) {
                        getLog().debug("Pushed Processor [" + processor + "]");
                    }
                }
            }
        }

        ConnectionHandler也没有做什么操作,也是简单的讲请求递交给processor 组件来处理。

三、Http11Processor

        1、Http11Processor的创建

        1.1、createProcessor

        上文中ConnectionHandler#process会调用createProcessor创建processor 对象,因为我们走的标准HTTP1.1所以这里的实现类为Http11Processor。在使用构造方法创建了Http11Processor对象后会为其设置相关属性,包括之前创建的Adapter对象等。

public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
    protected Processor createProcessor() {
        Http11Processor processor = new Http11Processor(this, getEndpoint());
        processor.setAdapter(getAdapter());
        processor.setMaxKeepAliveRequests(getMaxKeepAliveRequests());
        processor.setConnectionUploadTimeout(getConnectionUploadTimeout());
        processor.setDisableUploadTimeout(getDisableUploadTimeout());
        processor.setRestrictedUserAgents(getRestrictedUserAgents());
        processor.setMaxSavePostSize(getMaxSavePostSize());
        return processor;
    }
}

        1.2、构造方法

        Http11Processor构造方法第一步使用super调用了父类AbstractProcessor的构造方法,可以看到里面使用new创建了request(org.apache.coyote.Request)与response(org.apache.coyote.Response)对象,而子类中使用这两个对象创建httpParser(HttpParser)、inputBuffer(Http11InputBuffer)、outputBuffer(Http11OutputBuffer)等组件。

    public Http11Processor(AbstractHttp11Protocol<?> protocol, AbstractEndpoint<?, ?> endpoint) {
        super(endpoint);
        this.protocol = protocol;

        httpParser = new HttpParser(protocol.getRelaxedPathChars(), protocol.getRelaxedQueryChars());
        inputBuffer = new Http11InputBuffer(request, protocol.getMaxHttpRequestHeaderSize(),
                protocol.getRejectIllegalHeader(), httpParser);
        request.setInputBuffer(inputBuffer);
        outputBuffer = new Http11OutputBuffer(response, protocol.getMaxHttpResponseHeaderSize(),
                protocol.getSendReasonPhrase());
        response.setOutputBuffer(outputBuffer);
        inputBuffer.addFilter(new IdentityInputFilter(protocol.getMaxSwallowSize()));
        outputBuffer.addFilter(new IdentityOutputFilter());
        inputBuffer.addFilter(
                new ChunkedInputFilter(protocol.getMaxTrailerSize(), protocol.getAllowedTrailerHeadersInternal(),
                        protocol.getMaxExtensionSize(), protocol.getMaxSwallowSize()));
        outputBuffer.addFilter(new ChunkedOutputFilter());
        inputBuffer.addFilter(new VoidInputFilter());
        outputBuffer.addFilter(new VoidOutputFilter());
        inputBuffer.addFilter(new BufferedInputFilter(protocol.getMaxSwallowSize()));
        outputBuffer.addFilter(new GzipOutputFilter());
        pluggableFilterIndex = inputBuffer.getFilters().length;
    }
public abstract class AbstractProcessor extends AbstractProcessorLight implements ActionHook {
    protected final AbstractEndpoint<?, ?> endpoint;
    protected final Request request;
    protected final Response response;

    public AbstractProcessor(AbstractEndpoint<?, ?> endpoint) {
        this(endpoint, new Request(), new Response());
    }

    protected AbstractProcessor(AbstractEndpoint<?, ?> endpoint, Request coyoteRequest, Response coyoteResponse) {
        this.endpoint = endpoint;
        asyncStateMachine = new AsyncStateMachine(this);
        request = coyoteRequest;
        response = coyoteResponse;
        response.setHook(this);
        request.setResponse(response);
        request.setHook(this);
        userDataHelper = new UserDataHelper(getLog());
    }

}

        2、process

        process方法在一个 do-while 循环里,根据不同的条件,分别处理,其中重要的处理是调用 dispatch 方法或者 service 方法。

public abstract class AbstractProcessorLight implements Processor {
    public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status) throws IOException {

        SocketState state = SocketState.CLOSED;
        Iterator<DispatchType> dispatches = null;
        do {
            if (dispatches != null) {
                DispatchType nextDispatch = dispatches.next();
                state = dispatch(nextDispatch.getSocketStatus());
                if (!dispatches.hasNext()) {
                    state = checkForPipelinedData(state, socketWrapper);
                }
            } else if (status == SocketEvent.DISCONNECT) {
            } else if (isAsync() || isUpgrade() || state == SocketState.ASYNC_END) {
                state = dispatch(status);
                state = checkForPipelinedData(state, socketWrapper);
            } else if (status == SocketEvent.OPEN_WRITE) {
                state = SocketState.LONG;
            } else if (status == SocketEvent.OPEN_READ) {
                state = service(socketWrapper);
            } else if (status == SocketEvent.CONNECT_FAIL) {
                logAccess(socketWrapper);
            } else {
                state = SocketState.CLOSED;
            }

            if (isAsync()) {
                state = asyncPostProcess();
            }

            if (dispatches == null || !dispatches.hasNext()) {
                dispatches = getIteratorAndClearDispatches();
            }
        } while (state == SocketState.ASYNC_END || dispatches != null && state != SocketState.CLOSED);

        return state;
    }
}

         从注释里可以看出,dispatch 方法是处理非标准 HTTP 模式下的正在处理中的请求,这是在 Servlet 3.0 Async 和 HTTP 升级连接里用到的。

    /**
     * Process an in-progress request that is not longer in standard HTTP mode. Uses currently include Servlet 3.0 Async
     * and HTTP upgrade connections. Further uses may be added in the future. These will typically start as HTTP
     * requests.
     *
     * @param status The event to process
     *
     * @return The state the caller should put the socket in when this method returns
     *
     * @throws IOException If an I/O error occurs during the processing of the request
     */
    protected abstract SocketState dispatch(SocketEvent status) throws IOException;

        与 dispatch 方法相对立,service 方法是用来处理标准的 HTTP 请求的。service 方法的实现 Http11Processor 里。 

    /**
     * Service a 'standard' HTTP request. This method is called for both new requests and for requests that have
     * partially read the HTTP request line or HTTP headers. Once the headers have been fully read this method is not
     * called again until there is a new HTTP request to process. Note that the request type may change during
     * processing which may result in one or more calls to {@link #dispatch(SocketEvent)}. Requests may be pipe-lined.
     *
     * @param socketWrapper The connection to process
     *
     * @return The state the caller should put the socket in when this method returns
     *
     * @throws IOException If an I/O error occurs during the processing of the request
     */
    protected abstract SocketState service(SocketWrapperBase<?> socketWrapper) throws IOException;

        3、service

        service先执行一下初步工作setSocketWrapper,内部调用了inputBuffer.init(socketWrapper) 、 outputBuffer.init(socketWrapper) 等,然后就进入一个 while 循环。在 while 循环里,是在 try-catch 语句块里执行inputBuffer.parseRequestLine(用来处理请求行)以及一些状态和属性的设置。

        然后就执行调用 prepareRequestProtocol() 方法来对请求就行初步处理,也就是针对请求头里的一些属性加入一些 InputFilter 到 Http11InputBuffer 里。比如解析请求头里的 host,transfer-encoding,content-length 等。

        最后就调用 Adapter 的方法进行处理了,也就是getAdapter().service(request, response);

public class Http11Processor extends AbstractProcessor {
    public SocketState service(SocketWrapperBase<?> socketWrapper) throws IOException {
        RequestInfo rp = request.getRequestProcessor();
        rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);

        // Setting up the I/O
        setSocketWrapper(socketWrapper);

        // Flags
        keepAlive = true;
        openSocket = false;
        readComplete = true;
        boolean keptAlive = false;
        SendfileState sendfileState = SendfileState.DONE;

        while (!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null &&
                sendfileState == SendfileState.DONE && !endpoint.isPaused()) {
            // Parsing the request header
            try {
                if (!inputBuffer.parseRequestLine(keptAlive)) {
                    if (inputBuffer.getParsingRequestLinePhase() == -1) {
                        return SocketState.UPGRADING;
                    } else if (handleIncompleteRequestLineRead()) {
                        break;
                    }
                }
                // Process the Protocol component of the request line
                // Need to know if this is an HTTP 0.9 request before trying to
                // parse headers.
                prepareRequestProtocol();
            } 
            // Has an upgrade been requested?
			// ...

            // Process the request in the adapter
            if (getErrorState().isIoAllowed()) {
                try {
                    rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
                    getAdapter().service(request, response);

                    if (keepAlive && !getErrorState().isError() && !isAsync() &&
                            statusDropsConnection(response.getStatus())) {
                        setErrorState(ErrorState.CLOSE_CLEAN, null);
                    }
                } 
            }
        }

    }
}

        可以看出Processor 接收来自 EndPoint 的 Socket,读取字节流解析成 Tomcat Request 和 Response 对象,最后将请求传递给了Adapter做进一步的处理。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Tomcat源码:SocketProcessor、ConnectionHandler与Http11Processor 的相关文章

  • 如何让 BlazeDS 忽略属性?

    我有一个 java 类 它有一个带有 getter 和 setter 的字段 以及第二对 getter 和 setter 它们以另一种方式访问 该字段 public class NullAbleId private static final
  • 在 Java 中克隆对象 [3 个问题]

    这样做会调用Asub的clone方法吗 或者Asub深度克隆是否正确 如果没有的话 有没有办法通过这种方法对Asub进行深度克隆呢 abstract class Top extends TopMost protected Object cl
  • 日期语句之间的 JPQL SELECT [关闭]

    Closed 这个问题是无法重现或由拼写错误引起 help closed questions 目前不接受答案 我想将此 SQL 语句转换为等效的 JPQL SELECT FROM events WHERE events date BETWE
  • 为 java 游戏创建交互式 GUI

    大家好 我正在创建一个类似于 java 中的 farmville 的游戏 我只是想知道如何实现用户通常单击以与游戏客户端交互的交互式对象 按钮 我不想使用 swing 库 通用 Windows 看起来像对象 我想为我的按钮导入自定义图像 并
  • 如何在java中将一个数组列表替换为另一个不同大小的数组列表

    我有两个大小不同的数组列表 如何从此替换 ArrayList
  • Pig Udf 显示结果

    我是 Pig 的新手 我用 Java 编写了一个 udf 并且包含了一个 System out println 其中的声明 我必须知道在 Pig 中运行时该语句在哪里打印 假设你的UDF 扩展了 EvalFunc 您可以使用从返回的 Log
  • 谷歌应用程序引擎会话

    什么是java应用程序引擎 默认会话超时 如果我们将会话超时设置为非常非常长的时间 会不会产生不良影响 因为谷歌应用程序引擎会话默认情况下仅存储在数据存储中 就像facebook一样 每次访问该页面时 会话仍然永远存在 默认会话超时设置为
  • 在 Bash 中监控 tomcat,直到它完成部署 war 或应用程序

    怎么可能Tomcat在 bash 脚本中进行监控以检测它是否完成了战争或应用程序的部署 应用场景 Tomcat 开始于systemd Tomcat 开始于catalina sh 使用 Tomcat 管理器 Tomcat从Eclipse启动
  • 在接口中使用默认方法是否违反接口隔离原则?

    我正在学习 SOLID 原则 ISP 指出 客户端不应被迫依赖于他们所使用的接口 不使用 在接口中使用默认方法是否违反了这个原则 我见过类似的问题 但我在这里发布了一个示例 以便更清楚地了解我的示例是否违反了 ISP 假设我有这个例子 pu
  • 从最终实体获取根证书和中间证书

    作为密码学的菜鸟 我每天都会偶然发现一些简单的事情 今天只是那些日子之一 我想用 bouncy castle 库验证 java 中的 smime 消息 我想我几乎已经弄清楚了 但此时的问题是 PKIXparameters 对象的构建 假设我
  • 无法创建请求的服务[org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]-MySQL

    我是 Hibernate 的新手 我目前正在使用 Spring boot 框架并尝试通过 hibernate 创建数据库表 我知道以前也问过同样的问题 但我似乎无法根据我的环境找出如何修复错误 休眠配置文件
  • 在 junit 测试中获取 javax.lang.model.element.Element 类

    我想测试我的实用程序类 ElementUtils 但我不知道如何将类作为元素获取 在 AnnotationProcessors 中 我使用以下代码获取元素 Set
  • jdbc mysql loginTimeout 不起作用

    有人可以解释一下为什么下面的程序在 3 秒后超时 因为我将其设置为在 3 秒后超时 12秒 我特意关闭了mysql服务器来测试mysql服务器无法访问的这种场景 import java sql Connection import java
  • Hibernate 的 PersistentSet 不使用 hashCode/equals 的自定义实现

    所以我有一本实体书 public class Book private String id private String name private String description private Image coverImage pr
  • 如何访问JAR文件中的Maven资源? [复制]

    这个问题在这里已经有答案了 我有一个使用 Maven 构建的 Java 应用程序 我有一个资源文件夹com pkg resources 我需要从中访问文件 例如directory txt 我一直在查看各种教程和其他答案 但似乎没有一个对我有
  • 为什么 Java 8 不允许非公共默认方法?

    让我们举个例子 public interface Testerface default public String example return Hello public class Tester implements Testerface
  • Eclipse 选项卡宽度不变

    我浏览了一些与此相关的帖子 但它们似乎并不能帮助我解决我的问题 我有一个项目 其中 java 文件以 2 个空格的宽度缩进 我想将所有内容更改为 4 空格宽度 我尝试了 正确的缩进 选项 但当我将几行修改为 4 空格缩进时 它只是将所有内容
  • Cucumber 0.4.3 (cuke4duke) 与 java + maven gem 问题

    我最近开始为 Cucumber 安装一个示例项目 并尝试使用 maven java 运行它 我遵循了这个指南 http www goodercode com wp using cucumber tests with maven and ja
  • Android:无法使用 DbHelper 和 Contract 类将数据插入 SQLite

    public class Main2Activity extends AppCompatActivity private EditText editText1 editText2 editText3 editText4 private Bu
  • 双枢轴快速排序和快速排序有什么区别?

    我以前从未见过双枢轴快速排序 是快速排序的升级版吗 双枢轴快速排序和快速排序有什么区别 我在 Java 文档中找到了这个 排序算法是双枢轴快速排序 作者 弗拉基米尔 雅罗斯拉夫斯基 乔恩 本特利和约书亚 布洛赫 这个算法 在许多数据集上提供

随机推荐

  • Verilog数据类型

    作者 anekin 原作网址 http blog sina com cn s blog 615047920100ih0k html Verilog HDL有下列四种基本的值 1 0 逻辑0或 假 状态 2 1 逻辑1或 真 状态 3 x X
  • 因果推断 - 反事实

    目录 基础知识 案例实战 版权 转载前请联系作者获得授权 声明 部分内容出自因果关系之梯 已获得原作者授权 参考书籍 The Book of Why Judea Pearl 基础知识 定义 对于包含外生变量 U U U和内生变量 X X
  • mysql 显示用户_在Mysql中如何显示所有用户?

    这是一个mysql初学者经常问到的一个问题 今天我们就带大家看看是如何在Mysql中显示所有用户的 通常我们在mysql中使用SHOW DATABASES可以显示所有的数据库 SHOW TABLES将会显示所有的数据表 那么你是不是会猜测显
  • 软件版本号讲解:什么是Alpha, Beta, RC,Release

    1 软件版本阶段说明 Alpha版 此版本表示该软件在此阶段主要是以实现软件功能为主 通常只在软件开发者内部交流 一般而言 该版本软件的Bug较多 需要继续修改 Beta版 该版本相对于 版已有了很大的改进 消除了严重的错误 但还是存在着一
  • LayUI图片上传接口

    前端样式 div class layui upload drag i class layui icon xe67c i p 点击上传 或将文件拖拽到此处 p div js var uploadInst upload render elem
  • 10种React组件之间通信的方法

    组件间通信方式总结 父组件 gt 子组件 1 Props 2 Instance Methods refs 子组件 gt 父组件 3 Callback Functions 回调函数 4 Event Bubbling 事件冒泡机制 兄弟组件之间
  • 成都瀚网科技:抖店怎么上精选联盟?

    在抖音电商平台上 选定的联盟是一个非常重要的入口 对于商家来说 能够进入选定的联盟意味着更多的曝光度和流量 从而获得更好的销售机会 那么 抖店是如何进入精选联盟的呢 1 抖店如何加入特色联盟 提供优质的商品和服务 首先 抖店想要入驻所选联盟
  • 如何判断两个IP地址是否在同一个网段?什么是子网掩码?

    子网的概念挺复杂的 日常只要知道 网络传输时 只要对方IP与自己不在同一个子网 数据就会转交给网关处理 也就是路由器转发的意思 一 什么是子网掩码 在了解ip地址的网段之前 我们先来了解子网掩码 很多对网络了解不深的朋友都对子网掩码有些迷惑
  • JS的动画

    d1 hide 隐藏 d1 toggle 隐藏的显示 显示的隐藏 d1 fadeToggle 淡入淡出 d1 fadeTo fast 0 4 透明化 使用后 淡入淡出都无效包括再次透明化 d1 fadeOut fast 淡出 逐渐消失 d1
  • 都说C++难,那是没有学习数据结构【单链表】

    单链表 可有可无的目录 前言 一 链表是什么 链表的分类 二 链表的实现 总结 前言 上篇顺序表结尾了解了顺序表的诸多缺点 链表的特性很好的解决了这些问题 本期我们来认识单链表 一 链表是什么 链表是一种物理存储结构上非连续 非顺序的存储结
  • Latex 表格内文字过长自动换行

    法一 plain view plain copy begin tabular m 5cm 法二 plain view plain copy begin tabular p 0 9 columnwidth 法三 multirow 宏包 pla
  • 【嵌入式开源库】MultiTimer 的使用,一款可无限扩展的软件定时器

    MultiTimer 简介 下载 使用介绍 工程移植 代码分析 核心代码 实验效果 总结 简介 MultiTimer 是一个软件定时器扩展模块 可无限扩展你所需的定时器任务 取代传统的标志位判断方式 更优雅更便捷地管理程序的时间触发时序 M
  • UVA437 The Tower of Babylon

    UVA437 The Tower of Babylon 题目链接 动态规划 题目 有n n 30 种立方体 每种都有无穷多个 要求选一些立方体摞成一根尽量高的柱子 可以自行选择哪一条边作为高 使得每个立方体的底面长宽分别严格小于它下方立方体
  • OpenCV(三)——图像分割(一)

    目录 1 图像分割 2 固定阈值法 直方图双峰法 3 自动阈值法 3 1 自适应阈值法
  • AngularJs双向绑定原理

    AngularJs双向绑定 一 什么是AngularJS 二 什么是数据绑定 三 什么是双向绑定 四 双向绑定的实现 一 什么是AngularJS AngularJS是一个JavaScript框架 它诞生于2009年 由Misko Heve
  • ubuntu 20.04 安装 高版本cuda 11.7 和 cudnn最新版

    一 安装显卡驱动 参考另一篇文章 Ubuntu20 04安装Nvidia显卡驱动教程 ytusdc的博客 CSDN博客 二 安装CUDA 英伟达官网 最新版 CUDA Toolkit 12 2 Update 1 Downloads NVID
  • 在 Chrome (谷歌浏览器) 中模拟微信内置浏览器

    原文链接 在 Chrome 谷歌浏览器 中模拟微信内置浏览器 高先生的猫的博客 CSDN博客 chrome模拟微信内置浏览器 1 ios QQ 内置浏览器UA Mozilla 5 0 iPhone CPU iPhone OS 7 1 2 l
  • AndroidQ 获取、设置锁屏密码

    AndroidQ 获取 设置锁屏密码 本文中贴出的源码均为AndroidQ 9 0 源码 如果想要使用需要引入Framework的jar包 LockPatternUtils 说到密码相关的一定要提到LockPatternUtils这个类 它
  • qt中的QT的setWindowFlags的几种属性的总结

    参考博客 https www cnblogs com 132818Creator p 8194603 html 以下是大神博客的原话 setWindowFlags Qt CustomizeWindowHint 设置窗口标题栏自定义 setW
  • Tomcat源码:SocketProcessor、ConnectionHandler与Http11Processor

    前文 Tomcat源码 启动类Bootstrap与Catalina的加载 Tomcat源码 容器的生命周期管理与事件监听 Tomcat源码 StandardServer与StandardService Tomcat源码 Container接