网站组织结构图,app一键生成平台免费软件,设计师如何做自己的个人网站,兴义城乡建设部网站实现Tomcat和Jetty的切换
前言
上一篇文章我们聊到#xff0c;SpringBoot中内置了web服务器#xff0c;包括Tomcat、Jetty#xff0c;并且实现了SpringBoot启动Tomcat的流程。
那么SpringBoot怎样自动切换成Jetty服务器呢#xff1f;
接下来我们继续学习如何实现Tomcat…实现Tomcat和Jetty的切换
前言
上一篇文章我们聊到SpringBoot中内置了web服务器包括Tomcat、Jetty并且实现了SpringBoot启动Tomcat的流程。
那么SpringBoot怎样自动切换成Jetty服务器呢
接下来我们继续学习如何实现Tomcat和Jetty的自动切换。
定义WebServer接口并实现
package com.ber.springboot; import org.springframework.web.context.WebApplicationContext; /** * Author 鳄鱼儿 * Description TODO * date 2023/8/19 19:44 * Version 1.0 */public interface WebServer { void start(WebApplicationContext applicationContext);
}将BerSpringApplication类中startTomcat写到TomcatWebServer实现类中。
package com.ber.springboot; import org.apache.catalina.*;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.startup.Tomcat;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet; /** * Author 鳄鱼儿 * Description TODO * date 2023/8/19 19:45 * Version 1.0 */
public class TomcatWebServer implements WebServer{ Override public void start(WebApplicationContext applicationContext) { System.out.println(启动Tomcat); Tomcat tomcat new Tomcat(); Server server tomcat.getServer(); Service service server.findService(Tomcat); Connector connector new Connector(); connector.setPort(8023); Engine engine new StandardEngine(); engine.setDefaultHost(localhost); Host host new StandardHost(); host.setName(localhost); String contextPath ; Context context new StandardContext(); context.setPath(contextPath); context.addLifecycleListener(new Tomcat.FixContextListener()); host.addChild(context); engine.addChild(host); service.setContainer(engine); service.addConnector(connector); tomcat.addServlet(contextPath, dispatcher, new DispatcherServlet(applicationContext)); context.addServletMappingDecoded(/*, dispatcher); try { tomcat.start(); } catch (LifecycleException e) { e.printStackTrace(); } }
}JettyWebServer类同样实现WebServer接口不过具体启动Jetty代码省略不在本文探讨范围内。
package com.ber.springboot; /** * Author 鳄鱼儿 * Description TODO * date 2023/8/19 19:46 * Version 1.0 */
public class JettyWebServer implements WebServer{ Override public void start() { System.out.println(启动Jetty); }
}修改BerSpringApplication类
package com.ber.springboot; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import java.util.Map; /** * Author 鳄鱼儿 * Description TODO * date 2023/8/19 14:08 * Version 1.0 */
public class BerSpringApplication { public static void run(Class clazz) { // 1. 创建Spring 容器 AnnotationConfigWebApplicationContext applicationContext new AnnotationConfigWebApplicationContext(); applicationContext.register(clazz); applicationContext.refresh(); // 2. 获取特定WebServer类型的Bean WebServer webServer getWebServer(applicationContext); // 3. 调用start方法 webServer.start(applicationContext); } private static WebServer getWebServer(AnnotationConfigWebApplicationContext applicationContext) { // key为beanName, value为Bean对象 MapString, WebServer webServers applicationContext.getBeansOfType(WebServer.class); if (webServers.isEmpty()) { throw new NullPointerException(); } if (webServers.size() 1) { throw new IllegalStateException(); } return webServers.values().stream().findFirst().get(); }
}在run方法中获取到特定的web服务器并通过start方法进行 启动。
getWebServer方法实现判断web服务器并处理特殊情况——没有web服务器或者出现多个web服务器。
条件注解
package com.ber.springboot; import org.springframework.context.annotation.Conditional; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /** * Author 鳄鱼儿 * Description TODO * date 2023/8/19 20:06 * Version 1.0 */
Target({ElementType.TYPE, ElementType.METHOD})
Retention(RetentionPolicy.RUNTIME)
Conditional(BerOnClassConsition.class)
public interface BerConditionalOnClass { String value() default ;
}具体步骤为
拿到BerConditionalOnClass中的value属性类加载器进行加载加载到了特定的类名则符合条件否则不符合条件
package com.ber.springboot; import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata; import java.util.Map; /** * Author 鳄鱼儿 * Description TODO * date 2023/8/19 20:08 * Version 1.0 */
public class BerOnClassConsition implements Condition { Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { MapString, Object annotationAttributes metadata.getAnnotationAttributes(BerConditionalOnClass.class.getName()); // 1. 拿到BerConditionalOnClass中的value属性 String className (String) annotationAttributes.get(value); // 2. 类加载器进行加载 try { // 2.1 加载到了特定的类名则符合条件 true context.getClassLoader().loadClass(className); return true; } catch (ClassNotFoundException e) { // 2.2 加载不到则不符合条件 false return false; } }
}自动配置类
package com.ber.springboot; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; /** * Author 鳄鱼儿 * Description TODO * date 2023/8/19 20:34 * Version 1.0 */
Configuration
public class WebServiceAutoConfiguration implements AutoConfiguration{ Bean BerConditionalOnClass(org.apache.catalina.startup.Tomcat) public TomcatWebServer tomcatWebServer() { return new TomcatWebServer(); } Bean BerConditionalOnClass(org.eclipse.jetty.server.Server) public JettyWebServer jettyWebServer() { return new JettyWebServer(); }
}自动配置类在Spring Boot应用程序中起着关键的作用它们是实现自动化配置的核心组件。
这里定义满足各自条件的Bean当org.apache.catalina.startup.Tomcat类存在时TomcatWebServer的Bean才存在另一个亦是如此。
当spring容器存在Bean时就可以通过BerSpringApplication类getWebServer方法中的applicationContext.getBeansOfType(WebServer.class)获取到并由此可以进行对web服务器是否存在的判断。
SPI机制发现WebServiceAutoConfiguration
刚刚我们定义了自动配置类但运行user模块的Userapplication启动类时发现是无法发现WebServiceAutoConfiguration配置类的。
这是因为我们传入了Userapplication作为配置类扫描路径为Userapplication所在的包路径是无法扫描到WebServiceAutoConfiguration类的。
在springboot中实现了类似SPI的思想就是项目中的spring.factories文件提供了一种可插拔的扩展机制使开发人员能够轻松地定制应用程序的行为和功能同时又能保持主应用程序的稳定性。
这里我们可以借助JDK的SPI机制实现发现WebServiceAutoConfiguration类。
在springboot模块中增加resources/META-INF/services/com.ber.springboot.AutoConfiguration文件具体路径如图所示 com.ber.springboot.WebServiceAutoConfiguration增加AutoConfiguration接口类和实现类。
package com.ber.springboot; /** * Author 鳄鱼儿 * Description TODO * date 2023/8/19 21:08 * Version 1.0 */
public interface AutoConfiguration {
}package com.ber.springboot; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; /** * Author 鳄鱼儿 * Description TODO * date 2023/8/19 20:34 * Version 1.0 */
Configuration
public class WebServiceAutoConfiguration implements AutoConfiguration{ Bean BerConditionalOnClass(org.apache.catalina.startup.Tomcat) public TomcatWebServer tomcatWebServer() { return new TomcatWebServer(); } Bean BerConditionalOnClass(org.eclipse.jetty.server.Server) public JettyWebServer jettyWebServer() { return new JettyWebServer(); }
}并在注解类BerSpringBootApplication上增加Import(BerImportSelect.class)注解BerImportSelect类从com.ber.springboot.AutoConfiguration文件中获取类名然后添加到spring容器。
package com.ber.springboot; import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.core.type.AnnotationMetadata; import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader; /** * Author 鳄鱼儿 * Description * date 2023/8/19 21:15 * Version 1.0 */
public class BerImportSelect implements DeferredImportSelector { Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { /** 使用Java的ServiceLoader机制加载实现了AutoConfiguration接口的类 * AutoConfiguration是Spring Boot中用于自动配置的接口 * AutoConfiguration的实现类通常包含了一些配置信息帮助应用程序在不需要显式配置的情况下自动完成一些功能 */ ServiceLoaderAutoConfiguration serviceLoader ServiceLoader.load(AutoConfiguration.class); ListString list new ArrayList(); for (AutoConfiguration autoConfiguration : serviceLoader) { list.add(autoConfiguration.getClass().getName()); } // 返回包含所有加载的AutoConfiguration实现类名的字符串数组 return list.toArray(new String[0]); }
}添加Jetty依赖
修改user模块的依赖如下
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd parent artifactIdsimulate-springboot/artifactId groupIdorg.example/groupId version1.0-SNAPSHOT/version /parent modelVersion4.0.0/modelVersion artifactIduser/artifactId properties maven.compiler.source8/maven.compiler.source maven.compiler.target8/maven.compiler.target /properties dependencies dependency groupIdorg.example/groupId artifactIdspringboot/artifactId version1.0-SNAPSHOT/version exclusions exclusion groupIdorg.apache.tomcat.embed/groupId artifactIdtomcat-embed-core/artifactId /exclusion /exclusions /dependency dependency groupIdorg.eclipse.jetty/groupId artifactIdjetty-server/artifactId version9.4.43.v20210629/version /dependency /dependencies /project这里需要排除tomcat依赖因为springboot中已经添加了tomcat的依赖。
不排除就会出来既有tomcat又有Jetty就会出现IllegalStateException异常。
到此运行user模块的UserApplication类就可以啦。