依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.60</version>
</dependency>
</dependencies>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>springboot</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.4.43.v20210629</version>
</dependency>
</dependencies>
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("test")
public String test(){
return userService.test();
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration
@ComponentScan
public @interface ZhouyuSpringBootApplication {
}
public class ZhouyuSpringApplication {
public static void run(Class clazz){
}
}
@ZhouyuSpringBootApplication
public class MyApplication {
public static void main(String[] args) {
ZhouyuSpringApplication.run(MyApplication.class);
}
}
public class ZhouyuSpringApplication {
public static void run(Class clazz){
AnnotationConfigWebApplicationContext applicationContext = new
AnnotationConfigWebApplicationContext();
applicationContext.register(clazz);
applicationContext.refresh();
}
}
public class TomcatWebServer implements WebServer{
@Override
public void start() {
System.out.println("启动Tomcat");
}
public static void startTomcat(WebApplicationContext applicationContext){
Tomcat tomcat = new Tomcat();
Server server = tomcat.getServer();
Service service = server.findService("Tomcat");
Connector connector = new Connector();
connector.setPort(8081);
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();
}
}
}
public static void run(Class clazz){
AnnotationConfigWebApplicationContext applicationContext = new
AnnotationConfigWebApplicationContext();
applicationContext.register(clazz);
applicationContext.refresh();
startTomcat(applicationContext);
}
public interface WebServer {
public void start();
}
public class TomcatWebServer implements WebServer{
@Override
public void start() {
System.out.println("启动Jetty");
}
}
public class JettyWebServer implements WebServer{
@Override
public void start() {
System.out.println("启动Tomcat");
}
}
而在ZhouyuSpringApplication中的run方法中,我们就要去获取对应的WebServer,然后启动对应的webServer,代码为:
public static void run(Class clazz){
AnnotationConfigWebApplicationContext applicationContext = new
AnnotationConfigWebApplicationContext();
applicationContext.register(clazz);
applicationContext.refresh();
WebServer webServer = getWebServer(applicationContext);
webServer.start();
}
public static WebServer getWebServer(ApplicationContext applicationContext){
return null;
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Conditional(ZhouyuOnClassCondition.class)
public @interface ZhouyuConditionalOnClass {
String value() default "";
}
注意核心为@Conditional(ZhouyuOnClassCondition.class)中的ZhouyuOnClassCondition,因为
它才是真正得条件逻辑:
public class ZhouyuOnClassCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> annotationAttributes =
metadata.getAnnotationAttributes(ZhouyuConditionalOnClass.class.getName());
String className = (String) annotationAttributes.get("value");
try {
context.getClassLoader().loadClass(className);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}
????????具体逻辑为,拿到@ZhouyuConditionalOnClass中的value属性,然后用类加载器进行加载,如果加载到了所指定的这个类,那就表示符合条件,如果加载不到,则表示不符合条件。
模拟实现自动配置类
有了条件注解,我们就可以来使用它了,那如何实现呢?
这里就要用到自动配置类的概念,我们先看代码:
@Configuration
public class WebServiceAutoConfiguration {
@Bean
@ZhouyuConditionalOnClass("org.apache.catalina.startup.Tomcat")
public TomcatWebServer tomcatWebServer(){
return new TomcatWebServer();
}
@Bean
@ZhouyuConditionalOnClass("org.eclipse.jetty.server.Server")
public JettyWebServer jettyWebServer(){
return new JettyWebServer();
}
}
只有存在"org.apache.catalina.startup.Tomcat"类,那么才有TomcatWebServer这个Bean
只有存在"org.eclipse.jetty.server.Server"类,那么才有TomcatWebServer这个Bean
public static WebServer getWebServer(ApplicationContext applicationContext){
// key为beanName, value为Bean对象
Map<String, 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();
}
public interface AutoConfiguration {
}
@Configuration
public class WebServerAutoConfiguration implements AutoConfiguration {
@Bean
@ConditionalOnClass("org.apache.catalina.startup.Tomcat")
public TomcatWebServer tomcatWebServer(){
return new TomcatWebServer();
}
@Bean
@ConditionalOnClass("org.eclipse.jetty.server.Server")
public JettyWebServer jettyWebServer(){
return new JettyWebServer();
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration
@ComponentScan
@Import(ZhouyuImportSeclet.class)
public @interface ZhouyuSpringBootApplication {
}
public class ZhouyuImportSeclet implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 自动配置
ServiceLoader<AutoConfiguration> loader = ServiceLoader.load(AutoConfiguration.class);
List<String> list = new ArrayList<>();
for (AutoConfiguration configuration : loader) {
list.add(configuration.getClass().getName());
}
return list.toArray(new String[0]);
}
}
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>springboot</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.4.43.v20210629</version> 1
</dependency>
</dependencies>
只有先排除到Tomcat的依赖,再添加Jetty的依赖才能启动Jetty:
注意:由于没有了Tomcat的依赖,记得把最开始写的startTomcat方法给注释掉,并删除掉相关依