spring容器以及Tomcat啟動流程
- 在
CasSpringApplication
的run方法中啟動spring容器
public class CasSpringApplication {
public static void run(Class clazz) {
// 啟動sprinng容器
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(clazz);
context.refresh();
// 啟動服務(wù)
start(context);
}
}
- 定義服務(wù)
2.1 定義接口
public interface WebServer {
void start();
}
2.2 Tomcat實現(xiàn)類
public class TomcatWebServer implements WebServer {
@Override
public void start() {
System.out.println("tomcat 啟動了");
}
}
2.3 Jetty實現(xiàn)類
public class JettyWebServer implements WebServer {
@Override
public void start() {
System.out.println("jetty 啟動了");
}
}
2.3 獲取實現(xiàn)類
public static void start(AnnotationConfigWebApplicationContext applicationContext) {
Map<String, WebServer> webserver = applicationContext.getBeansOfType(WebServer.class);
if (webserver.size() > 1) {
throw new RuntimeException("獲取到多個服務(wù)");
}
webserver.values().stream().findFirst().get().start();
}
- 動態(tài)選擇服務(wù)
- 如果項目中有Tomcat就啟動Tomcat
- 如果項目中有Jetty則啟動Jetty
3.1 條件注解
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Conditional(CondationOnExitClass.class)
public @interface CondationOnExit {
String value() default "";
}
3.2 CondationOnExitClass
實現(xiàn)類
public class CondationOnExitClass implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> map = metadata.getAllAnnotationAttributes(CondationOnExit.class.getName());
String className = (String) map.getFirst("value");
try {
context.getClassLoader().loadClass(className);
return true;
} catch (ClassNotFoundException e) {
e.printStackTrace();
return false;
}
}
}
3.3 向spring
容器中注入bean
@Configuration
public class WebServerBean {
@Bean
@CondationOnExit("org.apache.catalina.startup.Tomcat")
public TomcatWebServer tomcatWebServer() {
return new TomcatWebServer();
}
@Bean
@CondationOnExit("org.eclipse.jetty.server.Server")
public JettyWebServer jettyWebServer() {
return new JettyWebServer();
}
}
springboot的包掃描
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration
@ComponentScan
public @interface CasSpringBoot {
}
springboot 自動裝配
- spring有一套自己實現(xiàn)的spi機制 在這里使用java自帶的spi 暫時不做太復(fù)雜
public class AutoImportSelect implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// java spi
ServiceLoader<AutoConfiguation> configuations = ServiceLoader.load(AutoConfiguation.class);
List<String> list = new ArrayList<>();
for (AutoConfiguation configuation : configuations) {
list.add(configuation.getClass().getName());
}
return list.toArray(new String[0]);
}
}
這個類無法被spring包掃描到 我們需要手動導(dǎo)入
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration
@ComponentScan
@Import({AutoImportSelect.class}) // 導(dǎo)入自動裝配類
public @interface CasSpringBoot {
}
測試
至此我們一個極簡版springboot就實現(xiàn)完畢了
@CasSpringBoot
public class CasApplication {
public static void main(String[] args) {
CasSpringApplication.run(CasApplication.class);
}
}