springboot启动类

2年前 (2022) 程序员胖胖胖虎阿
202 0 0

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、简单回顾
  • 二、run方法的入参
  • 三、ComponentScan的扫描范围
  • 总结

前言

提示:这里可以添加本文要记录的大概内容:

springboot启动流程有很多文章都介绍得很详细了,今天我们换种方式来讨论下启动类。


提示:以下是本篇文章正文内容,下面案例可供参考

一、简单回顾

1、首先快速创建一个springboot项目,编写一个测试接口。

@RestController
public class UserController {
    @RequestMapping("/test")
    public String getNameById(String id){
        System.out.println("测试接口入参,id="+id);
        return "lili";
    }
}

springboot启动类
接口正常,这是我们按默认方式创建项目。我们的启动类也很简单就是默认创建的。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

2、下面我们来改造下启动类,demo1:将启动类的注解去掉,run方法入参换一个配置类

import com.jy.demo.config.AopConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

//@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(AopConfig.class, args);
    }
}

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.*;

/**
 * @Author jy
 * @Date 2022/4/2 15:38
 * @Version 1.0
 */
@Configuration
@ComponentScan(basePackages = "com.jy.demo.controller")
//@EnableAspectJAutoProxy
@EnableAutoConfiguration
//@Import(JyAfterFilter.class)
public class AopConfig {
   
}

启动服务测试接口,发现服务正常启动,接口调用也是正常
springboot启动类
从目前来看这样操作是没有问题。那么我们再来试试demo2的方式修改,先新建两个类具体位置如图
springboot启动类
springboot启动类
从图片中可以看出,main所在的类在app包下,配置类在最外层,这时我用TestApplication启动,服务也是正常运行,测试接口也是正常调用成功
springboot启动类
3、我们再修改下代码,demo3:配置类也改一下

import com.jy.demo.config.AopConfig;
import org.springframework.boot.SpringApplication;

/**
 * @Author jy
 * @Date 2022/7/20 15:43
 * @Version 1.0
 */
public class TestApplication {
    public static void main(String[] args) {
        SpringApplication.run(AopConfig.class, args);
    }
}

服务启动依然正常,接口测试也正常
springboot启动类
我们几种修改都能正常启动,接口也正常调用,那么为什么可以呢?

二、run方法的入参

1、首先我们还是回顾下spring的注解模式

public class Demo9 {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
        applicationContext.publishEvent(new ApplicationEvent("监听事件推送") {
        });
        Apple bean = applicationContext.getBean(Apple.class);
        bean.setId(1);
        System.out.println(bean);
    }
}
public void register(Class<?>... componentClasses) {
		Assert.notEmpty(componentClasses, "At least one component class must be specified");
		StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register")
				.tag("classes", () -> Arrays.toString(componentClasses));
		this.reader.register(componentClasses);//注意这行代码!!!!
		registerComponentClass.end();
	}

传入的参数就是一个配置类,spring先将这个配置类注入容器,然后在BeanFactoryPostProccsor的实现中处理配置类带来的该处理的对象。具体的操作可以看看Configuration注解解析,而springboot的启动类中的run方法入参是什么呢?其实也是一个配置类,那么springboot是怎么用这个配置类的呢?
springboot启动类
首先是将这个配置类放在SpringApplication的成员变量里,然后在run方法中的上下文应用准备的时候对配置类进行了处理。
springboot启动类

// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		listeners.contextLoaded(context);
public Set<Object> getAllSources() {
		Set<Object> allSources = new LinkedHashSet<>();
		if (!CollectionUtils.isEmpty(this.primarySources)) {
			allSources.addAll(this.primarySources);
		}
		if (!CollectionUtils.isEmpty(this.sources)) {
			allSources.addAll(this.sources);
		}
		return Collections.unmodifiableSet(allSources);
	}

往下一步就是在load()方法中加载
springboot启动类
springboot启动类
这里因为我们看的配置类,所以进入第一个if中,再往里走就到了我们前面spring处理时的类似代码了
springboot启动类
圈上的这行代码和前面提示注意的那行代码就是一个意思了,到目前为止说清楚了,这个配置类被注册到容器的过程,那么和我们启动类的位置有什么关系呢?

三、ComponentScan的扫描范围

细心的朋友可能已经发现,我们几次修改的时候配置类的ComponentScan的value值是有所不同的,@SpringBootApplication的value值是默认空,而自己写的配置类上的ComponentScan注解是加了路径值的。两者的区别在于,默认空的话,扫描路径是按配置类所在的位置扫描同级及下级;有值的话就按填写的值进行扫描同级及下级。这个操作在ConfigurationClassPostProcessor类处理配置类信息时处理的。而配置类的信息在前面已经说了是怎么注册到容器中的,所以spring能处理到我们的启动配置类,也顺其自然的能处理到配置类上的扫描范围。所以虽然我们修改了main方法文件所在的位置,修改了配置类的位置服务依然能正常启用,接口也正常调用。

总结

唉,前面屁话了一堆,第三点就这么点东西说明了原因。最后再多一嘴,
既然启动类的位置可以不在最外层,那springboot为什么要放在最外层呢?因为springboot不知道我们会写些什么包路径,所以放最外层从最外层往下扫描,将对象是否注入容器交给开发者(需要注入就加注解)。以上就是我的一点个人理解,欢迎大家斧正。大家一起加油!!!

版权声明:程序员胖胖胖虎阿 发表于 2022年11月23日 上午12:48。
转载请注明:springboot启动类 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...