【SpringBoot1】Spring Boot是如何推断你的工程类型的

2024-02-04 22:44

本文主要是介绍【SpringBoot1】Spring Boot是如何推断你的工程类型的,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

我们的工程一般都是基于java的Servlet的,但是除此之外还有其他类型的,

在WebApplicationType里定义了常见的类型:

public enum WebApplicationType {/*** The application should not run as a web application and should not start an* embedded web server.*/NONE,/*** The application should run as a servlet-based web application and should start an* embedded servlet web server.*/SERVLET,/*** The application should run as a reactive web application and should start an* embedded reactive web server.*/REACTIVE;

从定义上也能看到,第一种是None类型,表示不是网络服务的类型,如果我们的服务不是web服务可以这么设置,但是这个应用场景极少。

第二种就是我们常用的SERVLRT方式,一般我们工程都要采用这种方式,即使不需要对外部用户提供服务,也需要与监控系统等交互,因此一般我们设置为Servet就行了。

第三种是REACTIVE,这个应该是支持响应式编程的,这个目前接触不多,暂且略过。

那么SpringBoot是如何判断我们的服务是哪种类型的呢?

一般我们SpringBoot的main入口都是这么写的:

@SpringBootApplication
@MapperScan("com.online_education.storage.mapper;")
public class TeacherServiceApp {public static void main(String[] args) {SpringApplication.run(TeacherServiceApp.class,args);}
}

其中@SpringBootApplication注解里其实是集成了三个:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(...)

这三个注解的功能我们后面分析,这里我们先回到main方法里的run()继续看:

SpringApplication.run(TeacherServiceApp.class,args);

此时内容是这样的:

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

这里的run没做什么,我们继续看run里的代码:

return new SpringApplication(primarySources).run(args);

这里主要是用反射创建我们的Main方法所在的对象,我们首先进入SpringApplication类里看一下,

	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();setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}

这里有一行很重要的代码:

this.webApplicationType = WebApplicationType.deduceFromClasspath();

这一行就是获取类型的,那具体如何获取的呢?我们继续看:

	static WebApplicationType deduceFromClasspath() {if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {return WebApplicationType.REACTIVE;}for (String className : SERVLET_INDICATOR_CLASSES) {if (!ClassUtils.isPresent(className, null)) {return WebApplicationType.NONE;}}return WebApplicationType.SERVLET;}

这里的isPresent()做什么的我们后面再细看,这里通过名字也能推测出来,大致是要判断是否存在的意思,第一个if部分就是判断是否为响应式REACTIVE类型,后面的for循环就是判断是否存在

SERVLET_INDICATOR_CLASSES里标记的类型,里面两个参数是web服务必须的,只要有一个不存在就会返回类型是空。

private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext" };

可以看到,这里其实是两个写死的类的地址,因此这里其实主要到某个地方查是否加载了这两个类。

如果都存在,那就返回Servlet类型。

那具体到哪里查的呢?我们继续看ClassUtils.isPresent(className, null)的实现:

  public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {try {forName(className, classLoader);return true;} catch (IllegalAccessError var3) {throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" + className + "]: " + var3.getMessage(), var3);} catch (Throwable var4) {return false;}}

到这里,根据定义和forName这种写法,有经验的同学就知道了,这里是到JVM里查装载器是否载入了。

public static Class<?> forName(String name, @Nullable ClassLoader classLoader){
... 
Class elementClass;String elementName;if (name.endsWith("[]")) {elementName = name.substring(0, name.length() - "[]".length());elementClass = forName(elementName, classLoader);return Array.newInstance(elementClass, 0).getClass();} else if (name.startsWith("[L") && name.endsWith(";")) {elementName = name.substring("[L".length(), name.length() - 1);elementClass = forName(elementName, classLoader);return Array.newInstance(elementClass, 0).getClass();} else if (name.startsWith("[")) {elementName = name.substring("[".length());elementClass = forName(elementName, classLoader);return Array.newInstance(elementClass, 0).getClass();} else {ClassLoader clToUse = classLoader;if (classLoader == null) {clToUse = getDefaultClassLoader();}try {return Class.forName(name, false, clToUse);
... 
}

这里的写法就比较负责了,上面是一个递归,本质上是在读字节码文件,例如name.startsWith("[L") 这些都是读字节码用的。

 那字节码是保存在哪里的呢?学过JVM虚拟机原理的同学应该知道,字节码是保存在元空间的,所以这里SpringBoot在启动的时候是去元空间里查,看看我们有没有将Servlet的类信息加载进来。

到此我们就知道了,如果我们要启动web服务,需要先将Servlet加载进来,SpringBoot还会以此类型来判断是否启用web服务。

而且通过这个代码,我们也可以知道Servlet和响应式只能装载一个,如果两个都有,仍然会当做Servelt服务。

if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {return WebApplicationType.REACTIVE;}

很明显,如果我们启动SpringBoot的时候还没有将Servlet相关类加载进来,那自然启动就失败了。

这篇关于【SpringBoot1】Spring Boot是如何推断你的工程类型的的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/679015

相关文章

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听