tomcat(14)服务器组件和服务组件

【0】README
0.1)本文部分文字描述转自 “how tomcat works”,旨在学习 “tomcat(14)服务器组件和服务组件” 的基础知识;
0.2)for complete source code ,please visit  https://github.com/pacosonTang/HowTomcatWorks/tree/master/chapter14

【1】服务器组件(org.apache.catalina.Server)
1)org.apache.catalina.Server接口的实例表示 Catalina的整个servlet引擎,囊括了所有的组件。服务器组件使用了一种优雅的方法来启动/关闭 整个系统,不需要再对连接器和容器分别启动/ 关闭;
2)启动/关闭机制的工作原理:当启动服务器组件时,它会启动其中所有的组件,然后它就无限期地等待关闭命令。如果想要关闭系统,可以向指定端口发送一条关闭命令。服务器组件接收到关闭命令后,就会关闭其中所有的组件;
3)服务组件:服务器组件引入了服务组件来包含其他组件,如一个容器组件和一个或多个连接器组件;(干货——注意区分服务器组件和服务组件,以下代码摘自某测试用例,StandardService表服务组件,而StandardServer表服务器组件)
Service service = new StandardService();service.setName("Stand-alone Service");Server server = new StandardServer();server.addService(service);

4)服务器组件Server接口定义
package org.apache.catalina;
import org.apache.catalina.deploy.NamingResources; 
public interface Server { public String getInfo();public NamingResources getGlobalNamingResources(); public void setGlobalNamingResources (NamingResources globalNamingResources);  public int getPort(); public void setPort(int port);  public String getShutdown(); public void setShutdown(String shutdown); public void addService(Service service); public void await();   public Service findService(String name);   public Service[] findServices();    public void removeService(Service service);public void initialize() throws LifecycleException;
}
对以上代码的分析(Analysis):
A1)shutdown属性:保存了必须发送给Server 实例用来关闭整个系统的关闭命令;
A2)port属性:定义了服务器组件会从哪个端口获取关闭命令;
A3)addService() 和 removeService()方法:为服务器组件添加或移除服务组件;
A4)findService()方法:返回添加到该服务器组件中的所有服务组件;
A5)initialize()方法:包含在系统启动前要执行的一些代码;

【2】StandardServer类
1)intro:一个服务器组件可以有0个或多个服务组件;(干货——一个服务器组件可以有0个或多个服务组件)
2)StandardServer类有4个与生命周期相关的方法,分别是initialize()方法,start()方法,stop()方法 和 await()方法;

【2.1】initialize()方法
1)intro:用于初始化添加到其中的服务器组件;
 public void initialize()  throws LifecycleException {  //org.apache.catalina.StandardServer.initialize().if (initialized)throw new LifecycleException (sm.getString("standardServer.initialize.initialized"));initialized = true;// Initialize our defined Servicesfor (int i = 0; i < services.length; i++) {services[i].initialize();}}
【2.2】start() 方法
1)intro:该方法用于启动服务器组件,即启动所有的服务组件,逐个启动所有组件,如连接器组件和servlet容器;
 public void start() throws LifecycleException {  //org.apache.catalina.StandardServer.start().// Validate and update our current component stateif (started)throw new LifecycleException(sm.getString("standardServer.start.started"));// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);lifecycle.fireLifecycleEvent(START_EVENT, null);started = true;// Start our defined Servicessynchronized (services) {for (int i = 0; i < services.length; i++) {if (services[i] instanceof Lifecycle)((Lifecycle) services[i]).start();}}// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);}
【2.3】stop()方法
1)intro:用于关闭服务器组件;(调用stop方法会关闭所有的服务组件,并reset started,这样,才能再次启动服务器组件;参见start()方法)
public void stop() throws LifecycleException {   //org.apache.catalina.StandardServer.stop().// Validate and update our current component stateif (!started)throw new LifecycleException(sm.getString("standardServer.stop.notStarted"));// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);lifecycle.fireLifecycleEvent(STOP_EVENT, null);started = false;// Stop our defined Servicesfor (int i = 0; i < services.length; i++) {if (services[i] instanceof Lifecycle)((Lifecycle) services[i]).stop();}// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);}
【2.4】await()方法
1)intro:该方法负责等待关闭整个tomcat 部署的命令;
2)await()方法:调用该方法后,会一直阻塞,直到它从 8085 端口(也可以是其他端口)上接收到关闭命令(accept()方法);当await()方法返回时,会运行stop方法来关闭其下的所有子组件;
public void await() {  //org.apache.catalina.StandardServer.await().// Set up a server socket to wait onServerSocket serverSocket = null;try {serverSocket =new ServerSocket(port, 1,InetAddress.getByName("127.0.0.1"));} catch (IOException e) {System.err.println("StandardServer.await: create[" + port+ "]: " + e);e.printStackTrace();System.exit(1);}// Loop waiting for a connection and a valid commandwhile (true) {// Wait for the next connectionSocket socket = null;InputStream stream = null;try {socket = serverSocket.accept(); //highlight line.socket.setSoTimeout(10 * 1000);  // Ten secondsstream = socket.getInputStream();} catch (AccessControlException ace) {System.err.println("StandardServer.accept security exception: "+ ace.getMessage());continue;} catch (IOException e) {System.err.println("StandardServer.await: accept: " + e);e.printStackTrace();System.exit(1);}// Read a set of characters from the socketStringBuffer command = new StringBuffer();int expected = 1024; // Cut off to avoid DoS attackwhile (expected < shutdown.length()) {if (random == null)random = new Random(System.currentTimeMillis());expected += (random.nextInt() % 1024);}while (expected > 0) {int ch = -1;try {ch = stream.read();} catch (IOException e) {System.err.println("StandardServer.await: read: " + e);e.printStackTrace();ch = -1;}if (ch < 32)  // Control character or EOF terminates loopbreak;command.append((char) ch);expected--;}// Close the socket now that we are done with ittry {socket.close();} catch (IOException e) {;}// Match against our command stringboolean match = command.toString().equals(shutdown); //highlight line.if (match) {break;} elseSystem.err.println("StandardServer.await: Invalid command '" +command.toString() + "' received");}// Close the server socket and returntry {serverSocket.close();} catch (IOException e) {;}}
对以上代码的调用过程分析(steps):
step1)创建一个ServerSocket对象,监听8085端口,并在while循环中调用它的accept()方法;
step2)当在指定端口上接收到消息时,accept()方法才会返回;
step3)将接收到的消息与关闭命令的字符串相比较,相同的话就跳出while循环,关闭 ServerSocket,否则会再次循环,继续等待消息;

【3】Service接口(服务组件接口——org.apache.catalina.core.Service)
1)intro:一个服务组件可以有一个servlet容器 和 多个连接器实例;(干货——一个服务器组件可以有0个或多个服务组件,而一个服务组件可以有一个servlet容器 和 多个连接器实例)
2)可以自由地把连接器添加到服务组件中,所有的连接器都会与这个servlet容器相关联; 
package org.apache.catalina; 
public interface Service { public Container getContainer();   public void setContainer(Container container);   public String getInfo(); public String getName(); public void setName(String name); public Server getServer();   public void setServer(Server server);     public void addConnector(Connector connector); public Connector[] findConnectors();   public void removeConnector(Connector connector);    public void initialize() throws LifecycleException;
}
【4】StandardService类(org.apache.catalina.core.StandardService)
1)StandardService.initialize()方法:用于初始化添加到其中的所有连接器;
2)StandardService.start()方法:启动连接器和所有servlet容器;

【4.1】connectors 和 container
1)StandardService实例中有两种组件:连接器和servlet容器;其中servlet容器只有一个,而连接器则可以有多个;(干货——StandardService实例中有两种组件:连接器和servlet容器)
2)多个连接器使tomcat 可以为多种不同的请求协议提供服务;如,一个连接器处理HTTP请求,而另一个连接器处理HTTPS请求;(干货——多个连接器使tomcat 可以为多种不同的请求协议提供服务)
3)变量connectors 和 container:StandardService类使用 container 变量来指向一个Container接口的实例,而数组connectors 来保存所有连接器的引用;(干货——变量connectors 和 container的用途)
 private Connector connectors[] = new Connector[0]; // defined in StandardService.
4)setContainer()方法:需要调用setContainer()方法 将servlet容器与服务组件相关联;
public void setContainer(Container container) { // org.apache.catalina.core.StandardService.setContainer().Container oldContainer = this.container;if ((oldContainer != null) && (oldContainer instanceof Engine))((Engine) oldContainer).setService(null);this.container = container;if ((this.container != null) && (this.container instanceof Engine))((Engine) this.container).setService(this);if (started && (this.container != null) &&(this.container instanceof Lifecycle)) {try {((Lifecycle) this.container).start();} catch (LifecycleException e) {;}}synchronized (connectors) {for (int i = 0; i < connectors.length; i++)connectors[i].setContainer(this.container);}if (started && (oldContainer != null) &&(oldContainer instanceof Lifecycle)) {try {((Lifecycle) oldContainer).stop();} catch (LifecycleException e) {;}}// Report this property change to interested listenerssupport.firePropertyChange("container", oldContainer, this.container);}
对以上代码的补充描述(description):与服务组件相关联的servlet容器的实例将被传给每个连接器对象的 setContainer()方法,这样在服务组件中就可以形成每个连接器和 servlet容器间的关联关系;
5)addConnector()方法和removeConnector()方法:前者将连接器添加到服务组件中, 后者将某个连接器移除;
(补充:addConnector()方法会初始化并启动添加到其中的连接器,参见下面的start()方法——highlight line.)
public void addConnector(Connector connector) { //org.apache.catalina.core.StandardService.addConnector().synchronized (connectors) {connector.setContainer(this.container);connector.setService(this);Connector results[] = new Connector[connectors.length + 1];System.arraycopy(connectors, 0, results, 0, connectors.length);results[connectors.length] = connector;connectors = results;if (initialized) {try {connector.initialize(); //highlight line.} catch (LifecycleException e) {e.printStackTrace(System.err);}}if (started && (connector instanceof Lifecycle)) {try {((Lifecycle) connector).start();//highlight line.} catch (LifecycleException e) {;}}// Report this property change to interested listenerssupport.firePropertyChange("connector", null, connector);}}
public void removeConnector(Connector connector) {  //org.apache.catalina.core.StandardService.removeConnector().synchronized (connectors) {int j = -1;for (int i = 0; i < connectors.length; i++) {if (connector == connectors[i]) {j = i;break;}}if (j < 0)return;if (started && (connectors[j] instanceof Lifecycle)) {try {((Lifecycle) connectors[j]).stop();} catch (LifecycleException e) {;}}connectors[j].setContainer(null);connector.setService(null);int k = 0;Connector results[] = new Connector[connectors.length - 1];for (int i = 0; i < connectors.length; i++) {if (i != j)results[k++] = connectors[i];}connectors = results;// Report this property change to interested listenerssupport.firePropertyChange("connector", connector, null);}}
【4.2】与生命周期有关的方法(start() + stop() + initialize())
1)intro: 与生命周期有关的方法包括Lifecycle.start() + Lifecycle.stop()+ initialize()方法;其中initialize()方法会调用该服务组件中所有连接器的 initialize()方法;
2)org.apache.catalina.core.StandardService.initialize()源码如下:
public void initialize()  throws LifecycleException {  //org.apache.catalina.core.StandardService.initialize().if (initialized) throw new LifecycleException (sm.getString("standardService.initialize.initialized"));initialized = true;// Initialize our defined Connectorssynchronized (connectors) {for (int i = 0; i < connectors.length; i++) {connectors[i].initialize(); //highlight line.}}}
public void initialize()  throws LifecycleException { //org.apache.catalina.connector.HttpConnector.initialize().if (initialized)throw new LifecycleException (sm.getString("httpConnector.alreadyInitialized"));this.initialized=true;Exception eRethrow = null;// Establish a server socket on the specified porttry {serverSocket = open();} catch (IOException ioe) {log("httpConnector, io problem: ", ioe);eRethrow = ioe;} catch (KeyStoreException kse) {log("httpConnector, keystore problem: ", kse);eRethrow = kse;} catch (NoSuchAlgorithmException nsae) {log("httpConnector, keystore algorithm problem: ", nsae);eRethrow = nsae;} catch (CertificateException ce) {log("httpConnector, certificate problem: ", ce);eRethrow = ce;} catch (UnrecoverableKeyException uke) {log("httpConnector, unrecoverable key: ", uke);eRethrow = uke;} catch (KeyManagementException kme) {log("httpConnector, key management problem: ", kme);eRethrow = kme;}if ( eRethrow != null )throw new LifecycleException(threadName + ".open", eRethrow);}
3)org.apache.catalina.core.StandardService.start()源码如下:该方法负责启动被添加到 该服务组件中的连接器和servlet容器;
 public void start() throws LifecycleException {  // org.apache.catalina.core.StandardService.start()// Validate and update our current component stateif (started) {throw new LifecycleException(sm.getString("standardService.start.started"));}// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);System.out.println(sm.getString("standardService.start.name", this.name));lifecycle.fireLifecycleEvent(START_EVENT, null);started = true;// Start our defined Container firstif (container != null) {synchronized (container) {if (container instanceof Lifecycle) {((Lifecycle) container).start(); //highlight line.}}}// Start our defined Connectors secondsynchronized (connectors) {for (int i = 0; i < connectors.length; i++) {if (connectors[i] instanceof Lifecycle)((Lifecycle) connectors[i]).start(); //highlight line.}}// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);}
4)org.apache.catalina.core.StandardService.stop()源码如下:用于关闭与该服务组件相关联的servlet容器和所有连接器;
public void stop() throws LifecycleException { // org.apache.catalina.core.StandardService.stop()// Validate and update our current component stateif (!started) {throw new LifecycleException(sm.getString("standardService.stop.notStarted"));}// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);lifecycle.fireLifecycleEvent(STOP_EVENT, null);System.out.println(sm.getString("standardService.stop.name", this.name));started = false;// Stop our defined Connectors firstsynchronized (connectors) {for (int i = 0; i < connectors.length; i++) {if (connectors[i] instanceof Lifecycle)((Lifecycle) connectors[i]).stop();//highlight line.}}// Stop our defined Container secondif (container != null) {synchronized (container) {if (container instanceof Lifecycle) {((Lifecycle) container).stop(); //highlight line.}}}// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);}
【5】应用程序 
1)intro:本应用程序旨在说明如何使用服务器组件和服务组件,特别是在 StandardServer类中如何利用 启动和关闭机制;
2)共有3个类:Bootstrap类+Stopper类+SimpleContextConfig类(它其实是作为 LifecycleListener的实例)
2.1)Bootstrap类源码
public final class Bootstrap {public static void main(String[] args) {System.setProperty("catalina.base", System.getProperty("user.dir"));Connector connector = new HttpConnector();Wrapper wrapper1 = new StandardWrapper();wrapper1.setName("Primitive");wrapper1.setServletClass("PrimitiveServlet");Wrapper wrapper2 = new StandardWrapper();wrapper2.setName("Modern");wrapper2.setServletClass("ModernServlet");Context context = new StandardContext();// StandardContext's start method adds a default mappercontext.setPath("/app1");context.setDocBase("app1");context.addChild(wrapper1);context.addChild(wrapper2);LifecycleListener listener = new SimpleContextConfig();((Lifecycle) context).addLifecycleListener(listener);Host host = new StandardHost();host.addChild(context);host.setName("localhost");host.setAppBase("webapps");Loader loader = new WebappLoader();context.setLoader(loader);// context.addServletMapping(pattern, name);context.addServletMapping("/Primitive", "Primitive");context.addServletMapping("/Modern", "Modern");Engine engine = new StandardEngine();engine.addChild(host);engine.setDefaultHost("localhost");Service service = new StandardService();service.setName("Stand-alone Service");Server server = new StandardServer();server.addService(service);service.addConnector(connector);//StandardService class's setContainer will call all its connector's setContainer methodservice.setContainer(engine);// Start the new serverif (server instanceof Lifecycle) {try {server.initialize();((Lifecycle) server).start();server.await();// the program waits until the await method returns,// i.e. until a shutdown command is received.}catch (LifecycleException e) {e.printStackTrace(System.out);}}// Shut down the serverif (server instanceof Lifecycle) {try {((Lifecycle) server).stop();}catch (LifecycleException e) {e.printStackTrace(System.out);}}}
}
对以上代码的调用过程分析(steps):
step1)该方法会创建一个连接器,两个Wrapper实例,一个Context容器,一个Host容器和一个Engine容器;
step2)将两个Wrapper 实例添加到 Context容器中, 将Context容器添加到 Host容器中,再将Host容器添加到Engine容器中;(干货——到此,tomcat的4种容器创建完毕)
step3)但是,这并没有将连接器和最顶层的容器Engine相关联;
step4)相反,main()方法创建了一个Service对象,设置其名称,再创建一个 Server对象,将Service实例添加到 Server实例中:
 Service service = new StandardService();service.setName("Stand-alone Service");Server server = new StandardServer();server.addService(service);
step5)main()方法将连接器和Engine容器添加到 Service实例中;
    service.addConnector(connector);service.setContainer(engine);
step6)main()方法调用 Server.intialize() 和 Server.start()方法,初始化连接器,并启动连接器和servlet容器;
step7)接下来,main()方法调用Server.await()方法,进入循环等待,监听8085端口等待关闭命令;(干货——参见上述【2.4】await()方法)
Attention)此时连接器已经处于运行状态,等待另一个端口8080上的 http 请求;(监听8085和8080端口分别处于不同的线程)
// Start the new serverif (server instanceof Lifecycle) {try {server.initialize(); // step6((Lifecycle) server).start(); // step6server.await(); // step7// the program waits until the await method returns,// i.e. until a shutdown command is received.}catch (LifecycleException e) {e.printStackTrace(System.out);}}
step8)除非收到了正确的关闭命令,否则await()方法是不会返回的。当 await()方法返回时,main()方法调用 Server.stop()方法,实际上关闭其所有组件;
// Shut down the serverif (server instanceof Lifecycle) {try {((Lifecycle) server).stop();}catch (LifecycleException e) {e.printStackTrace(System.out);}}
2.2)Stopper类 源码
2.2.1)intro:Stopper类提供了更加优雅的方式来关闭Catalina服务器,也保证了所有的生命周期组件中的stop() 方法都能够调用;
public class Stopper {public static void main(String[] args) {// the following code is taken from the Stop method of// the org.apache.catalina.startup.Catalina classint port = 8005;try {Socket socket = new Socket("127.0.0.1", port);OutputStream stream = socket.getOutputStream();String shutdown = "SHUTDOWN";for (int i = 0; i < shutdown.length(); i++)stream.write(shutdown.charAt(i));stream.flush();stream.close();socket.close();System.out.println("The server was successfully shut down.");}catch (IOException e) {System.out.println("Error. The server has not been started.");}}
}
对以上代码的分析(Analysis): Stopper类的main()方法会创建一个 Socket对象,然后将正确 的关闭命令 “SHOTDOWN”字符串发送到 端口8085。Catalina 服务器在接收到关闭命令后,就会执行相应的关闭操作;

Attention)本文还是习惯性地总结了上述测试用例的调用流程图



A1)HttpConnector.start()方法的调用过程如下(下面的图借用自 tomcat(11) 中章节【1】的4.1小节的图):


A2)StandardsEngine.start()方法的调用过程如下(下面的第二张图借用自tomcat(13)中的章节【10】 ,两张图可以结合起来看):

(补充,需要了解 HttpConnector.initialize() 和 start() 方法 以及 StandardContext.start()方法的具体调用过程,参见 tomcat(supplement)HttpConnector.initialize() 和 start() 方法 以及 StandardContext.start()方法的分析)


【6】执行结果
1)console info
E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;lib/catalina-5.5.4.jar;lib/naming-common.
jar;lib/commons-collections.jar;lib/naming-resources.jar;lib/commons-digester.jar;lib/catalina.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWok
s\weroot com.tomcat.chapter14.startup.Bootstrap
HttpConnector Opening server socket on all host IP addresses
Starting service Stand-alone Service
Apache Tomcat/4.1.24
WebappLoader[/app1]: Deploying class repositories to work directory E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src\work\_\localhost\app1
WebappLoader[/app1]: Deploy class files /WEB-INF/classes to E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src\webapps\app1\WEB-INF\classes
StandardManager[/app1]: Seeding random number generator class java.security.SecureRandom
StandardManager[/app1]: Seeding of random number generator has been completed
StandardManager[/app1]: IOException while loading persisted sessions: java.io.EOFException
java.io.EOFExceptionat java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)at java.io.ObjectInputStream$BlockDataInputStream.readShort(Unknown Source)at java.io.ObjectInputStream.readStreamHeader(Unknown Source)at java.io.ObjectInputStream.<init>(Unknown Source)at org.apache.catalina.util.CustomObjectInputStream.<init>(CustomObjectInputStream.java:103)at org.apache.catalina.session.StandardManager.load(StandardManager.java:408)at org.apache.catalina.session.StandardManager.start(StandardManager.java:655)at org.apache.catalina.core.StandardContext.start(StandardContext.java:3570)at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1188)at org.apache.catalina.core.StandardHost.start(StandardHost.java:738)at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1188)at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:347)at org.apache.catalina.core.StandardService.start(StandardService.java:497)at org.apache.catalina.core.StandardServer.start(StandardServer.java:2190)at com.tomcat.chapter14.startup.Bootstrap.main(Bootstrap.java:77)
StandardManager[/app1]: Exception loading sessions from persistent storage
java.io.EOFExceptionat java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)at java.io.ObjectInputStream$BlockDataInputStream.readShort(Unknown Source)at java.io.ObjectInputStream.readStreamHeader(Unknown Source)at java.io.ObjectInputStream.<init>(Unknown Source)at org.apache.catalina.util.CustomObjectInputStream.<init>(CustomObjectInputStream.java:103)at org.apache.catalina.session.StandardManager.load(StandardManager.java:408)at org.apache.catalina.session.StandardManager.start(StandardManager.java:655)at org.apache.catalina.core.StandardContext.start(StandardContext.java:3570)at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1188)at org.apache.catalina.core.StandardHost.start(StandardHost.java:738)at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1188)at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:347)at org.apache.catalina.core.StandardService.start(StandardService.java:497)at org.apache.catalina.core.StandardServer.start(StandardServer.java:2190)at com.tomcat.chapter14.startup.Bootstrap.main(Bootstrap.java:77)
HttpConnector[8080] Starting background thread
ModernServlet -- init
StandardHost[localhost]: MAPPING configuration error for request URI /favicon.ico
2)执行效果




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

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

相关文章

组合模式的安全模式与透明模式

转载自 树形结构的处理——组合模式&#xff08;四&#xff09; 1 透明组合模式与安全组合模式 通过引入组合模式&#xff0c;Sunny公司设计的杀毒软件具有良好的可扩展性&#xff0c;在增加新的文件类型时&#xff0c;无须修改现有类库代码&#xff0c;只需增加一个新的文件类…

spring react_使用Spring WebFlux构建React性REST API –第1部分

spring react在本文中&#xff0c;我们将看到如何使用Spring WebFlux构建响应式REST API。 在进入React式API之前&#xff0c;让我们看看系统是如何发展的&#xff0c;传统REST实现遇到的问题以及现代API的需求。 如果您查看从旧版系统到下文所述的现代系统的期望&#xff0c;…

tomcat(15)Digester库

【0】README0.1&#xff09;本文部分文字描述转自 “how tomcat works”&#xff0c;旨在学习 “tomcat(15)Digester库” 的基础知识&#xff1b;2&#xff09;problemsolution&#xff1a;2.1&#xff09;problem&#xff1a;如下面的代码&#xff0c;Bootstrap类实例化连接器…

api restful_HATEOAS的RESTful服务。 记录超媒体API

api restful1.简介 希望本教程的前一部分不仅揭示了超媒体和HATEOAS的深远意义&#xff0c;而且使我们确信这些都是RESTful Web服务和API的基本构建模块。 在这一部分中&#xff0c;我们将继续侧重于文档方面&#xff0c;以解决如何预先传递Web服务或API功能的问题。 目录 1.…

模版方法模式

转载自 23种设计模式&#xff08;6&#xff09;&#xff1a;模版方法模式定义&#xff1a;定义一个操作中算法的框架&#xff0c;而将一些步骤延迟到子类中&#xff0c;使得子类可以不改变算法的结构即可重定义该算法中的某些特定步骤。 类型&#xff1a;行为类模式 类图&#…

java并发编程实践(1)intro

【0】README0.1&#xff09;本文部分文字描述转自“java并发编程实践”&#xff0c;旨在学习“java并发编程实践(1)intro”的相关知识&#xff1b;【3】线程带来的风险【3.1】安全性问题1&#xff09;intro&#xff1a;在没有充足同步的case下&#xff0c;多个线程的操作执行顺…

读入的字节都写入字节数组中_使用Java将文件读入字节数组的7个示例

读入的字节都写入字节数组中嗨&#xff0c;大家好&#xff0c;Java程序员经常在现实世界中遇到编程问题&#xff0c;他们需要将文件中的数据加载到字节数组中&#xff0c;该文件可以是文本文件或二进制文件。 一个示例是将文件的内容转换为String以显示。 不幸的是&#xff0c;…

迭代子模式

转载自 《JAVA与模式》之迭代子模式 在阎宏博士的《JAVA与模式》一书中开头是这样描述迭代子&#xff08;Iterator&#xff09;模式的&#xff1a; 迭代子模式又叫游标(Cursor)模式&#xff0c;是对象的行为模式。迭代子模式可以顺序地访问一个聚集中的元素而不必暴露聚集的内部…

java并发编程实践(2)线程安全性

【0】README 0.0&#xff09;本文部分文字描述转自&#xff1a;“java并发编程实战”&#xff0c; 旨在学习“java并发编程实践(2)线程安全性” 的相关知识&#xff1b;0.1&#xff09;几个术语&#xff08;terms&#xff09;t1&#xff09;对象的状态&#xff1a;是指存储在状…

自动配置jdk_JDK 15中自动自动发送更好的NullPointerException消息

自动配置jdk我讨论了期待已久的和高度赞赏改进的NullPointerException &#xff08; NPE在帖&#xff09;消息“ 更好的默认NullPointerException异常消息即将到Java&#xff1f; ”和“ JDK 14中更好的NPE消息 ”。 当此JEP 358驱动的功能添加到JDK 14中时 &#xff0c;想要从…

tomat(16)关闭钩子

【0】REAMDE0&#xff09;本文部分文字描述转自&#xff1a;“how tomcat works”&#xff0c;旨在学习“tomat(16)关闭钩子”的相关知识&#xff1b;1&#xff09;problemsolution&#xff1a;1.1&#xff09;problem&#xff1a;在很多实际环境中&#xff0c;当用户关闭应用程…

apache.camel_Apache Camel 3.1 – XML路由的快速加载

apache.camelCamel 3.1中添加的一项功能是能够更快地加载XML路由。 这是我们为使Camel变得更小&#xff0c;更快而进行的总体工作的一部分。 您可能会说ewww XML。 但坦率地说&#xff0c;有很多Camel用户使用XML定义路由来构建应用程序。 在Camel 2.x中&#xff0c;您将不得不…

程序中 设置jvm 参数_高效应用程序的7个JVM参数

程序中 设置jvm 参数在撰写本文时&#xff08;2020年3月&#xff09;&#xff0c;围绕垃圾收集和内存&#xff0c;您可以将600多个参数传递给JVM。 如果您包括其他方面&#xff0c;则JVM参数总数将很容易超过1000个。 &#x1f60a;。 任何人都无法消化和理解太多的论据。 在本…

tomcat(17)启动tomcat

【0】README 1&#xff09;本文部分文字描述转自“how tomcat works”&#xff0c;旨在学习“tomcat(17)启动tomcat”的相关知识&#xff1b;2&#xff09;本文重点关注启动Tomcat时会用到的两个类&#xff0c;分别是Catalina类和 Bootstrap类&#xff1b;&#xff08;干货——…

八爪鱼 是java做的吗_章鱼扫描仪:Java构建工具和恶意软件

八爪鱼 是java做的吗Alvaro Munoz最近在GitHub Security Lab网站上发布了“ Octopus扫描程序恶意软件&#xff1a;攻击开源供应链 ”。 我发现这篇文章很有趣&#xff0c;原因有很多&#xff0c;其中包括对Octopus Scanner恶意软件的工作原理和发现方式的详细介绍&#xff0c; …

spring(1)Spring之旅

【0】README0.1&#xff09;本文部分文字描述转自&#xff1a;“Spring In Action&#xff08;中/英文版&#xff09;”&#xff0c;旨在reviewSpring(1)Spring之旅 的相关知识&#xff1b;【1】简化java 开发1&#xff09;为了降低java开发的复杂性&#xff0c;Spring采取了以…

apache.camel_Apache Camel K 1.0在这里–您为什么要关心

apache.camel昨天我们发布了Apache Camel K 1.0&#xff0c;并在社交媒体和Camel网站上宣布了该版本。 那么&#xff0c;骆驼K是什么&#xff0c;为什么你要在乎呢&#xff1f; 这是一个很好的问题&#xff0c;我想通过提及伟大的思想来帮助回答这个问题。 Hugo Guerrero发表…

DRF之初识

目录 一、序列化和反序列化 【1】序列化 【2】反序列化 【3】小结 二、DRF的安装和快速使用 (1) 安装DRF&#xff1a; (2) 配置DRF&#xff1a; (3) 创建序列化器(Serializer)&#xff1a; (4) 创建视图(View)&#xff1a; (5) 配置URL路由&#xff1a; 【补充】下载…

spring(2)装配Bean

【0】README0&#xff09;本文部分文字描述转自&#xff1a;“Spring In Action&#xff08;中/英文版&#xff09;”&#xff0c;旨在review spring(2)装配Bean 的相关知识&#xff1b;1&#xff09;在spring中&#xff0c;对象无需自己查找或创建与其所关联的其他对象。相反…

restful和rest_HATEOAS的RESTful服务:JVM上的REST API和超媒体

restful和rest1.简介 到目前为止&#xff0c;我们已经花了很多时间谈论了相当数量的关于角色的的超媒体和HATEOAS在REST风格的 Web服务和API&#xff0c;扫视不同规格和可用性方面。 听起来好像支持超媒体和HATEOAS并不难&#xff0c;只需选择您的收藏夹就可以了&#xff01; …