Spring Boot核心流程

发布时间 2023-05-28 00:37:35作者: RainbowMagic

项目组成

首先有两个mavne项目,lyra-spring-boot用于模拟spring boot的实现, say-hello项目则是我们的具体业务使用
image

注解

  • SpringBootApplication: 在启动类上,初始化要加载的容器的注解。
  • SpringApplication.run(): 初始化Spring容器。

我们要实现以下的逻辑:

  1. 初始化Spring容器。
  2. 启动tomcat。

创建Spring容器

run方法会解析class上的注解包含componentScan注解来将bean注入到容器中。
annotationConfigApplicationContext.register(c);这个语句就是做这件事的。

public class SpringBoot {
    public static void run(Class c) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext =
                new AnnotationConfigApplicationContext();
        annotationConfigApplicationContext.register(c);
        annotationConfigApplicationContext.refresh();
    }

}

启动tomcat

    public static void run(Class c) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext =
                new AnnotationConfigApplicationContext();
        annotationConfigApplicationContext.register(c);
        annotationConfigApplicationContext.refresh();

        // 创建web容器并启动
        WebServer webServer = new WebServer();
        webServer.start();


//        DeferredImportSelector bean = annotationConfigApplicationContext.getBean(DeferredImportSelector.class);
    }

实现tomcat与jetty的切换

上面那中方法只使用了tomcat,如果想要切换jetty的话,因为代码已经写死了。改起来会很麻烦。
我们可以创建一个接口,通过接口实现不同的类,根据类的不同启动不同的容器,比如有一个WebServer的接口

public interface WebServer {
    public void start();
}

tomcat:

public class TomcatServer implements WebServer {
    @Override
    public void start() {
        System.out.println("run tomcat");
    }
}

jetty

public class JettyServer implements WebServer {

    @Override
    public void start() {
        System.out.println("jetty run");
    }
}

在run方法中根据接口获取容器,然后启动即可

    public static void run(Class c) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext =
                new AnnotationConfigApplicationContext();
        annotationConfigApplicationContext.register(c);
        annotationConfigApplicationContext.refresh();

        // 创建web容器并启动
        WebServer webServer = getWebServer(annotationConfigApplicationContext);
        webServer.start();

//        DeferredImportSelector bean = annotationConfigApplicationContext.getBean(DeferredImportSelector.class);
    }
   private static WebServer getWebServer(AnnotationConfigApplicationContext annotationConfigApplicationContext) {
        return annotationConfigApplicationContext.getBean(WebServer.class);
    }

但是这样做还是不太行,我们需要根据导入的依赖来加载不同bean

模拟条件注解

在@Conditional(WebCondition.class)注解中依赖了一个类,在类中定义了条件注解的规则。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Conditional(WebCondition.class)
public @interface ConditionalOnClass {
    String value();
}

在类中调用条件定义的条件注解即可,条件注解会将value传入到@Conditional(WebCondition.class)的WebCondion注解中。

@Configuration
public class WenServerAutoConfiguration {
    @Bean
    @ConditionalOnClass(value = "org.apache.catalina.startup.Tomcat")
    public WebServer tomcatServer() {
        return new TomcatServer();
    }

    @Bean
    public WebServer jettyServer() {
        return new JettyServer();
    }
}

类实现了Condition接口。判断穿过来的value是否能被类加载器加载到,如果加载到则返回true,可以注入到spring容器中,否则返回false。

public class WebCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> conditionalOnClass = metadata.getAnnotationAttributes(ConditionalOnClass.class.getName());

        ClassLoader classLoader = context.getClassLoader();
        try {
            return classLoader.loadClass(conditionalOnClass.get("value").toString()) != null;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}

但是这样做还没完,我们需要将WenServerAutoConfiguration注入到Spring容器中,由于CompnentScan扫描不到我们定义的Spring boot的configuration,所以需要一个注解了完成这个操作:
@Import(value = WebServerAutoConfiguration.class)