Apakah prinsip permulaan SpringBoot?
入口
版本: 2.1.8.RELEASE
启动代码:
@SpringBootApplcation public static void main(String[] args) { SpringApplication.run(BlogAdminApplication.class, args); System.out.println("======== admin start success... =========="); }
这里传入了两个参数,BlogAdminApplication当前类和args参数
我们点击进入run方法查看
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class[]{primarySource}, args); }
这里是将我们写的启动类传入到了Class[]数组中,这步就是个单纯的参数转换。
探讨primarySource参数
那么问题是primarySource能接受的类型是啥样的,是不是什么类都可以接受,带着这个疑问,我们做一个测试,把这个参数给换成一个别的类呢,ManagerController类是一个我写的接口类
@SpringBootApplication public class BlogProjectApplication { public static void main(String[] args) { SpringApplication.run(ManagerController.class, args); System.out.println("======== admin start success... =========="); } }
控制台打印
org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
提示不能启动服务,提示缺少了ServletWebServerFactory bean 点进这个类看下
@FunctionalInterface public interface ServletWebServerFactory { WebServer getWebServer(ServletContextInitializer... initializers); }
他被FunctionalInterface标注了,是一个函数式接口,只有一个getWebServer方法,用来获取webServer的 看下他的实现类,
这不就是提示我们缺少启动的服务容器么,说的直白点,我的理解就是他缺少可以运行的容器,我们知道,没有使用springboot项目之前,我们的项目都是跑在tomcat容器上的,当然也有使用Jetty容器的。再者,我们知道SpringBoot是对tomcat进行了内置。而SpringBoot不仅仅是只有内置了tomcat,而且还内置了好多的东西,比如我们经常使用的mq、redis等等一系列的东西,这个我们可以在spring.factories配置文件中看到,这个文件位于如下位置
大概内容有下,篇幅有限,就不一一列举了。
省略。。。 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ 省略。。。
那么回过头来,我们再看下这个问题,这些个类是如何被加载进来的,我们知道SpingBoot有个注解是开启自动注解的@EnableAutoConfiguration,他就干这个事情的。他能够激活SpringBoot内建和自定义组件的自动装配特性。
那么,知道了这些,我们把这个之前修改后的类给改造一下,加上注解@EnableAutoConfiguration,看下执行效果。
@RestController @RequestMapping("project/manager") @EnableAutoConfiguration public class ManagerController extends AbstractController {
运行如下
从打印信息就能知道,服务器有了,只不过下面报错,提示找不到bean,那这不就简单了么,他是不是就是没有扫描到我们的包么,这里就其实可以在配置扫描包的注解继续测试,我就懒的不测试了,直接去看@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 {
@SpringBootApplication这个注解其实就是一个组合注解,里面包含了
元注解:用来标注他是一个注解的,jdk自带的
@SpringBootConfiguration:集成自@Configuration,表示是一个配置类
@EnableAutoConfiguration:激活SpringBoot内建和自定义组件的自动装配特性
@ComponentScan:扫描注解,添加了排除参数,指定排除了一些类
通过这里的实验,我们可以得出结论:
primarySource参数能接收的类是一个配置类,同时要把符合扫描规则的类装配到spring容器中,并且对SpringBoot内置的一些类进行自动扫描到,而这里的@SpringBootApplication注解就是把这些特性都整合到了一起,作为了一个引导类而已。那么说白了,primarySource他接受的其实就是一个配置类。
关于注解详细知识的话,这里就聊这么多了,后面再详细聊。
args参数
args是Java命令行参数,我们在DOS中执行Java程序的时候使用“java 文件名 args参数”。args这个数组可以接收到这些参数。这个是个基础常识了。
以下我们将继续跟踪源码进行分析
我们继续追run()方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return (new SpringApplication(primarySources)).run(args); }
这个方法干了两个事情:
1、new SpringApplication()来创建对象
2、通过创建后的对象,调用对象里面的run()方法
以下我们将从这两个地方进行分析,本篇就先研究第一个
创建对象
我们先看下他是怎么创建对象的,创建了哪些对象,
public SpringApplication(Class<?>... primarySources) { this((ResourceLoader)null, primarySources); } public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { //资源加载器 this.resourceLoader = resourceLoader; //断言 Assert.notNull(primarySources, "PrimarySources must not be null"); //对primarySources进行存储到LinkedHashSet this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //1、推断web应用类别 this.webApplicationType = WebApplicationType.deduceFromClasspath(); //2、加载Spring应用上下文初始化 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //3、加载Spring应用事件监听器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //4、推断应用引导类 this.mainApplicationClass = deduceMainApplicationClass(); }
下面研究下主要流程部分
1、推断web应用类别
推断web应用类型属于SpringBoot应用web类型的初始化过程。而该类型也可在SpringApplication构造后,run方法执行之前,通过setWebApplicationType(WebApplicationType webApplicationType)方法进行调整。
在推断Web应用类型的过程中,由于当前Spring应用上下文尚未准备(可在代码执行顺序中看到),所以实现采用的是检查检查当前ClassLoader下基准Class的存在性判断。
上源码
this.webApplicationType = WebApplicationType.deduceFromClasspath(); 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"; private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext"; private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext"; static WebApplicationType deduceFromClasspath() { //当DispatcherHandler存在,且DispatcherServlet、ServletContainer两个不存在时;换言之,SpringBoot仅依赖WebFlux存在时,此时的应用类型为REACTIVE if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } //当Servlet和ConfigurableWebApplicationContext均不存在时,当前应用为非Web应用,即WebApplicationType.NONE,因为这些API均是Spring Web MVC必须的依赖 for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } //当WebFlux和Spring Web MVC同时存在时,Web应用类型同样是Servlet Web,即WebApplicationType.SERVLET return WebApplicationType.SERVLET; }
2、加载Spring应用上下文初始化
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
此过程包含两个动作,依次为getSpringFactoriesInstances(ApplicationContextInitializer.class)和setInitializers方法。 先看第一个过程
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) { //获取类加载器 ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates //加载了META-INF/spring.factories资源中配置的ApplicationContextInitializer实现类名单。 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); //初始化 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
此处使用了Spring工厂加载机制方法SpringFactoriesLoader.loadFactoryNames(type, classLoader)。加载了META-INF/spring.factories资源中配置的ApplicationContextInitializer实现类名单。
加载完成后使用createSpringFactoriesInstances方法对其进行初始化。
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); //判断instanceClass是不是type的子类 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; }
3、加载Spring应用事件监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
此过程与2、加载上下文初始化基本类似。 只不过初始化的对象类型变成了ApplicationListener.class,setListeners方法也只是赋值而已
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) { this.listeners = new ArrayList<>(); this.listeners.addAll(listeners); }
4、推断应用引导类
this.mainApplicationClass = deduceMainApplicationClass(); 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; }
这里使用到了new RuntimeException().getStackTrace()来获取堆栈信息,找到调用执行的main方法,从而确定他的类。
这里有个疑问:他不是传了primarySources数组,里面包含了类名么,怎么还用堆栈的方式去获取,此外,这里的堆栈获取也只能获取一个调用的主main方法,他为啥还要传一个Class数组呢?
具体咋获取的,可以追下源码,一直跟踪他的父类Throwable,找到如下代码
/** * Fills in the execution stack trace. This method records within this * {@code Throwable} object information about the current state of * the stack frames for the current thread. * * <p>If the stack trace of this {@code Throwable} {@linkplain * Throwable#Throwable(String, Throwable, boolean, boolean) is not * writable}, calling this method has no effect. * * @return a reference to this {@code Throwable} instance. * @see java.lang.Throwable#printStackTrace() */ public synchronized Throwable fillInStackTrace() { if (stackTrace != null || backtrace != null /* Out of protocol state */ ) { fillInStackTrace(0); stackTrace = UNASSIGNED_STACK; } return this; } private native Throwable fillInStackTrace(int dummy);
这里最后调用了native本地方法,去爬取线程堆栈信息,为运行时栈做一份快照。
通过这个图片,可以看到整个方法的调用链,从下往上看哦
Atas ialah kandungan terperinci Apakah prinsip permulaan SpringBoot?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas



Pengenalan kepada Jasypt Jasypt ialah perpustakaan java yang membenarkan pembangun menambah fungsi penyulitan asas pada projeknya dengan usaha yang minimum dan tidak memerlukan pemahaman yang mendalam tentang cara penyulitan berfungsi dengan tinggi untuk penyulitan sehala dan dua hala. teknologi penyulitan berasaskan piawai. Sulitkan kata laluan, teks, nombor, perduaan... Sesuai untuk penyepaduan ke dalam aplikasi berasaskan Spring, API terbuka, untuk digunakan dengan mana-mana pembekal JCE... Tambahkan kebergantungan berikut: com.github.ulisesbocchiojasypt-spring-boot-starter2 Faedah Jasypt melindungi keselamatan sistem kami Walaupun kod itu bocor, sumber data boleh dijamin.

Senario penggunaan 1. Tempahan berjaya dibuat tetapi pembayaran tidak dibuat dalam masa 30 minit. Pembayaran tamat masa dan pesanan dibatalkan secara automatik 2. Pesanan telah ditandatangani dan tiada penilaian dilakukan selama 7 hari selepas ditandatangani. Jika pesanan tamat dan tidak dinilai, sistem lalai kepada penilaian positif 3. Pesanan dibuat dengan jayanya jika peniaga tidak menerima pesanan selama 5 minit, pesanan itu dibatalkan peringatan mesej teks dihantar... Untuk senario dengan kelewatan yang lama dan prestasi masa nyata yang rendah, kami boleh Gunakan penjadualan tugas untuk melaksanakan pemprosesan undian biasa. Contohnya: xxl-job Hari ini kita akan memilih

1. Redis melaksanakan prinsip kunci teragih dan mengapa kunci teragih diperlukan Sebelum bercakap tentang kunci teragih, adalah perlu untuk menjelaskan mengapa kunci teragih diperlukan. Lawan daripada kunci yang diedarkan ialah kunci yang berdiri sendiri Apabila kami menulis program berbilang benang, kami mengelakkan masalah data yang disebabkan oleh mengendalikan pembolehubah yang dikongsi pada masa yang sama Kami biasanya menggunakan kunci untuk mengecualikan pembolehubah yang dikongsi bersama untuk memastikan ketepatannya pembolehubah yang dikongsi skop penggunaannya adalah dalam proses yang sama. Jika terdapat berbilang proses yang perlu mengendalikan sumber yang dikongsi pada masa yang sama, bagaimanakah ia boleh saling eksklusif? Aplikasi perniagaan hari ini biasanya merupakan seni bina perkhidmatan mikro, yang juga bermakna bahawa satu aplikasi akan menggunakan berbilang proses Jika berbilang proses perlu mengubah suai baris rekod yang sama dalam MySQL, untuk mengelakkan data kotor yang disebabkan oleh operasi yang tidak teratur, keperluan pengedaran. untuk diperkenalkan pada masa ini. Gaya dikunci. Ingin mencapai mata

Springboot membaca fail, tetapi tidak boleh mengakses perkembangan terkini selepas membungkusnya ke dalam pakej balang Terdapat situasi di mana springboot tidak boleh membaca fail selepas membungkusnya ke dalam pakej balang adalah tidak sah dan hanya boleh diakses melalui strim. Fail berada di bawah resources publicvoidtest(){Listnames=newArrayList();InputStreamReaderread=null;try{ClassPathResourceresource=newClassPathResource("name.txt");Input

Apabila Springboot+Mybatis-plus tidak menggunakan pernyataan SQL untuk melaksanakan operasi penambahan berbilang jadual, masalah yang saya hadapi akan terurai dengan mensimulasikan pemikiran dalam persekitaran ujian: Cipta objek BrandDTO dengan parameter untuk mensimulasikan parameter yang dihantar ke latar belakang bahawa adalah amat sukar untuk melaksanakan operasi berbilang jadual dalam Mybatis-plus Jika anda tidak menggunakan alatan seperti Mybatis-plus-join, anda hanya boleh mengkonfigurasi fail Mapper.xml yang sepadan dan mengkonfigurasi ResultMap yang berbau dan kemudian. tulis pernyataan sql yang sepadan Walaupun kaedah ini kelihatan menyusahkan, ia sangat fleksibel dan membolehkan kita

SpringBoot dan SpringMVC adalah kedua-dua rangka kerja yang biasa digunakan dalam pembangunan Java, tetapi terdapat beberapa perbezaan yang jelas antara mereka. Artikel ini akan meneroka ciri dan penggunaan kedua-dua rangka kerja ini dan membandingkan perbezaannya. Mula-mula, mari belajar tentang SpringBoot. SpringBoot telah dibangunkan oleh pasukan Pivotal untuk memudahkan penciptaan dan penggunaan aplikasi berdasarkan rangka kerja Spring. Ia menyediakan cara yang pantas dan ringan untuk membina bersendirian, boleh dilaksanakan

1. Sesuaikan RedisTemplate1.1, mekanisme siri lalai RedisAPI Pelaksanaan cache Redis berasaskan API menggunakan templat RedisTemplate untuk operasi cache data Di sini, buka kelas RedisTemplate dan lihat maklumat kod sumber kelas tersebut. Isytihar kunci, Pelbagai kaedah pesirilan nilai, nilai awal kosong @NullableprivateRedisSe

Dalam projek, beberapa maklumat konfigurasi sering diperlukan Maklumat ini mungkin mempunyai konfigurasi yang berbeza dalam persekitaran ujian dan persekitaran pengeluaran, dan mungkin perlu diubah suai kemudian berdasarkan keadaan perniagaan sebenar. Kami tidak boleh mengekodkan konfigurasi ini dalam kod. Adalah lebih baik untuk menulisnya dalam fail konfigurasi Sebagai contoh, anda boleh menulis maklumat ini dalam fail application.yml. Jadi, bagaimana untuk mendapatkan atau menggunakan alamat ini dalam kod? Terdapat 2 kaedah. Kaedah 1: Kita boleh mendapatkan nilai yang sepadan dengan kunci dalam fail konfigurasi (application.yml) melalui ${key} beranotasi dengan @Value Kaedah ini sesuai untuk situasi di mana terdapat sedikit perkhidmatan mikro projek, Apabila perniagaan adalah rumit, logik
