上一章介绍了通过外部web容器启动springboot项目,本章介绍通过Springboot的Main函数启动。
一、springboot Main 方式启动
@EnableSwagger2
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
二、Springboot 的启动入口
很多人都自然想到Main函数时程序启动入口,解开springboot的jar包,可以看到里面的文件目录
可以看到多了loader的目录结构,主要原因是springboot项目以来的方式是jar in jar,与普通的在同目录下的lib不一样,当加载lib包时,springboot需要自定义加载方式,将依赖加载到classpath下。
1、函数的入口
Mainclass是JarLauncher,查看具体的内容有
package org.springframework.boot.loader;
import org.springframework.boot.loader.archive.Archive;
import org.springframework.boot.loader.archive.Archive.Entry;
public class JarLauncher extends ExecutableArchiveLauncher
{
static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";
static final String BOOT_INF_LIB = "BOOT-INF/lib/";
public JarLauncher() {}
protected JarLauncher(Archive archive){
super(archive);
}
protected boolean isNestedArchive(Archive.Entry entry){
if (entry.isDirectory()) {
return entry.getName().equals("BOOT-INF/classes/");
}
return entry.getName().startsWith("BOOT-INF/lib/");
}
public static void main(String[] args) throws Exception {
new JarLauncher().launch(args);
}
}
接着看Launcher的实现
public abstract class Launcher{
// 加载函数
protected void launch(String[] args) throws Exception{
JarFile.registerUrlProtocolHandler();
ClassLoader classLoader = createClassLoader(getClassPathArchives());
launch(args, getMainClass(), classLoader);
}
protected ClassLoader createClassLoader(List<Archive> archives)throws Exception {
List<URL> urls = new ArrayList(archives.size());
for (Archive archive : archives) {
urls.add(archive.getUrl());
}
return createClassLoader((URL[])urls.toArray(new URL[0]));
}
// 自定义类加载器
protected ClassLoader createClassLoader(URL[] urls) throws Exception{
return new LaunchedURLClassLoader(urls, getClass().getClassLoader());
}
// 加载函数
protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception{
Thread.currentThread().setContextClassLoader(classLoader);
// 真正的开始执行方法
createMainMethodRunner(mainClass, args, classLoader).run();
}
protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader){
return new MainMethodRunner(mainClass, args);
}
protected abstract String getMainClass()throws Exception;
protected abstract List<Archive> getClassPathArchives() throws Exception;
protected final Archive createArchive()throws Exception{
ProtectionDomain protectionDomain = getClass().getProtectionDomain();
CodeSource codeSource = protectionDomain.getCodeSource();
URI location = codeSource != null ? codeSource.getLocation().toURI() : null;
String path = location != null ? location.getSchemeSpecificPart() : null;
if (path == null) {
throw new IllegalStateException("Unable to determine code source archive");
}
File root = new File(path);
if (!root.exists()) {
throw new IllegalStateException("Unable to determine code source archive from " + root);
}
return root.isDirectory() ? new ExplodedArchive(root) : new JarFileArchive(root);
}
}
package org.springframework.boot.loader;
import java.lang.reflect.Method;
public class MainMethodRunner{
private final String mainClassName;
private final String[] args;
public MainMethodRunner(String mainClass, String[] args){
this.mainClassName = mainClass;
this.args = (args != null ? (String[])args.clone() : null);
}
// 通过反射获取main方法,然后调用main方法执行
public void run() throws Exception{
Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName);
Method mainMethod = mainClass.getDeclaredMethod("main", new Class[] { String[].class });
mainMethod.invoke(null, new Object[] { this.args });
}
}
所以,springboot的启动函数入口是Main函数,但不是程序中定义的Main函数,而是JarLauncher中的launch,然后反射出app 的Main函数执行。
三、springboot的自动加载
1、@SpringBootApplication注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}
包含了ComponentScan,EnableAutoConfiguration和SpringBootConfiguration,@ComponentScan是包扫描,对特定注解的类提交给IOC管理。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
// 本质上是Configuration,本身是Bean,也提供对其他Bean的创建
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
重点关注AutoConfigurationImportSelector类,里面实现了selectImports方法
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取候选Bean的名字
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
// 去除重复的Bean名字
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
// 发送自动配置依赖的listener
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
// 这个方法会扫描classpath下META-INF/spring.factories中的类,改文件很多spring包中都有
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
// loadFactoryNames 方法会扫描META-INF/spring.factories,找到实现类,以URL方式读取配置文件
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
}