首页IT科技spring 初始化方法注解(【java】(一)SpringBoot 源码解析——SpringApplication 初始化)

spring 初始化方法注解(【java】(一)SpringBoot 源码解析——SpringApplication 初始化)

时间2025-07-30 20:23:25分类IT科技浏览4479
导读:1.前言...

1.前言

深入学习springboot笔记系列            ,可能会有错误还请指正                  ,互相勉励       ,互相学习            。

SpringBoot 项目启动只需启动 主类的 main 函数即可启动java服务         ,相比于以往的部署java服务简化方便了很多                  ,接下我们从主函数入手一步一步剖析源码是如何通过main函数启动服务的                   。

2.SpringBoot 项目程序入口

主函数通过一个静态 run 方法完成整个服务的构建      。

接下来看看静态的 run 方法的内部实现         。

2.1.run 方法构造

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args);} public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args);}

以上通过 new SpringApplication(primarySources) 执行了初始化的一些相关操作                   。

3.SpringApplication 初始化

public SpringApplication(Class<?>... primarySources) {   this(null, primarySources);}

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {   this.resourceLoader = resourceLoader;   Assert.notNull(primarySources, "PrimarySources must not be null");   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));   // 推断服务类型   this.webApplicationType = WebApplicationType.deduceFromClasspath();   // 初始化 META-INF/spring.factories 中 所有的 BootstrapRegistryInitializer 类   this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));   // 初始化 META-INF/spring.factories 中 所有的 ApplicationContextInitializer 类   setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));   // 初始化 META-INF/spring.factories 中 所有的 ApplicationListener 类   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));   // 推断主类   this.mainApplicationClass = deduceMainApplicationClass(); }

SpringApplication 初始化主要初始化了resourceLoader            、primarySources                   、webApplicationType       、bootstrapRegistryInitializers         、initializers                   、listeners         、mainApplicationClass 这几个对象         。

其中resourceLoader 默认为null          ,primarySources 则为主类的有序去重集合      。

3.1.webApplicationType 推断当前项目启动类型

private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet"; private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler"; private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer"; static WebApplicationType deduceFromClasspath() { //webflux 响应式 if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } // 是否web 项目 for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; }

此处使用ClassUtils.isPresent(String className, @Nullable ClassLoader classLoader) 方法为内部调用类加载加载响应的类      ,如果未找到则抛出ClassNotFoundException异常                   。类加载WEBMVC_INDICATOR_CLASS      、WEBFLUX_INDICATOR_CLASS                    、JERSEY_INDICATOR_CLASS 几种不同的 Servlet                  ,如果未加载到则为false             ,从而推断项目启动类型            。此处我们是web项目   ,因此最后的返回值是WebApplicationType.SERVLET   。

3.2.bootstrapRegistryInitializers 初始化

private<T> Collection<T> getSpringFactoriesInstances(Class<T>type) {

return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { //获取默认类加载器AppClassLoader ClassLoader classLoader = getClassLoader(); //使用默认类加载器加载META-INFO/spring.factories 中配置的类 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); //使用反射将上一步 类加载完成的 names中的类实例化 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 将实例化的类排序 排序规则:继承了 PriorityOrdered 接口的 优先级最高                  , 其次实现Ordered 接口 或者添加@Order 注解 通过比较order值的大小来排序                ,值越小优先级越高                   。 AnnotationAwareOrderComparator.sort(instances); return instances; } @SuppressWarnings("unchecked") private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> instances = new ArrayList<>(names.size()); for (String name : names) { try { Class<?> instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes); T instance = (T) BeanUtils.instantiateClass(constructor, args); instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex); } } return instances; } public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { // 缓存cache 中查找 Map<String, List<String>> result = cache.get(classLoader); if (result != null) { return result; } result = new HashMap<>(); try { // 此处使用类加载器读取所有依赖的包中 MEAT-INFO/spring.factories 中配置的类,并添加在缓存cache中 Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue()); for (String factoryImplementationName : factoryImplementationNames) { result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()) .add(factoryImplementationName.trim()); } } } // Replace all lists with unmodifiable lists containing unique elements result.replaceAll((factoryType, implementations) -> implementations.stream().distinct() .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))); cache.put(classLoader, result); } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } return result; }

以上方法通过默认的类加载AppClassLoader 加载依赖包中 META-INFO/spring.factories 路径中所有配置的类并缓存在cache中               ,然后由class 类型找出缓存cache 中需要相应加载的类并通过反射将类实例化并排序返回               。

3.3.initializers             、listeners初始化

同 bootstrapRegistryInitializers 加载流程一致                   ,通过cache 缓存提高加载速度。

3.4.mainApplicationClass 初始化

private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }

通过构造一个运行异常获取堆栈信息    ,从main方法的堆栈信息中获取主类的信息                。因为SpringApplication的构造器primarySources 是数组类型            ,因此无法直接通过primarySource 来判断main方法属于哪个class                   。

至此 SpringApplication类初始化加载完成   。

总结

1.通过类加载器加载依赖的servlet判断项目类型;

2.通过类加载器加载指定路径文件 MEAT-INFO/spring.factories 读取指定配置文件并使用cache 缓存提高加载速度;

3.通过java反射的方式将指定的BootstrapRegistryInitializer.class   、ApplicationContextInitializer.class                   、ApplicationListener.class 类的实现类实例化;

4.通过运行异常的堆栈信息推断main方法所在的类为主类            。

后续

下一章 将继续 讲解 SpringBoot 后续启动流程                  ,谢谢观看                   。

声明:本站所有文章       ,如无特殊说明或标注         ,均为本站原创发布      。任何个人或组织                  ,在未征得本站同意时          ,禁止复制               、盗用、采集                、发布本站内容到任何网站                   、书籍等各类媒体平台         。如若本站内容侵犯了原著者的合法权益      ,可联系我们进行处理                   。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
python怎么搭建环境(python pipenv创建环境) 深拷贝和浅拷贝的区别及实现方式(死磕Java面试系列:深拷贝与浅拷贝的实现原理)