- 初始化SpringApplication:确定webApplicatuonType类型,扫描所有jar包下META-INF/spring.factories文件,生成ApplicationContextInitializer和ApplicationListener接口的实现类的对象。
- 确定webApplicationType类型:
this.webApplicationType = WebApplicationType.deduceFromClasspath();
源码解析:获取类加载器,去加载路径下的类,如果找不到就抛异常返回false,最后通过判
断返回类型为SERVLET
参考:https://blog.csdn.net/z69183787/article/details/105834513/
- 确定applicationContext的实现类:
run方法中有这一段:
ConfigurableApplicationContext context = null;
context = this.createApplicationContext();
打开createApplicationContext方法:
switch(this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
* 最终得知applicationcontext的实现类为;AnnotationConfigServletWebServerApplicationContext
然后通过return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass) 方法实例化AnnotationConfigServletWebServerApplicationContext类,生成对象
* 所以,通过添加不同的jar包,就能得到不同的webApplicationType类型,通过不同的webApplicationType类型就能加载不同的applicationContext的实现类。
- 假如我们想在springboot项目启动完成之后,做点什么,我们应该怎么办呢?
实现ApplicationRunner,重写run方法。
this.callRunners(context, applicationArguments); 这段是执行程序,大致流程如下:
找到ApplicationRunner的实现类,反射生成对象,调用run()方法。前提是实现了ApplicationRunner的类标注@Component注解交给spring管理。
思维发散:要用到spring某个接口的功能时,先要创建一个类实现接口并加上@Component注解交给Spring管理,然后实现这个接口的某个固定方法,
spring会在执行过程中去找到接口的实现类,然后反射创建对象,调用这个固定方法执行其中的程序。
参考:https://zhuanlan.zhihu.com/p/343357078
- Runner,它是在项目加载完成之后执行的,有后就有前,有没有在项目加载之前执行的呢,ApplicationContextInitializer就是在spring的bean加载之前执行的
ApplicationContextInitializer用于在spring容器刷新之前初始化Spring ConfigurableApplicationContext的回调接口。
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); - 就是这个方法中的this.applyInitializers(context)方法找到调用initialize()方法
this.refreshContext(context);
剪短说就是在容器刷新之前调用该类的 initialize 方法。并将 ConfigurableApplicationContext 类的实例传递给该方法
通常用于需要对应用程序上下文进行编程初始化的web应用程序中。例如,根据上下文环境注册属性源或激活配置文件等。
* 过程:在spring的bean加载之前,通过扫描所有jar包META-INF文件夹下的spring.factories文件,找到所有实现了ConfigurableApplicationContext接口的类,
进行实例化,然后调用每个实例化对象的initialize()方法进行初始化等工作。这就是ApplicationContextInitializer接口的作用,请记住,他是用来初始化的。
* 自己实现:创建一个类实现ApplicationContextInitializer接口,重写initialize()方法,在resource目录下新建META-INF文件夹,
新建spring.factories文件,按照格式接口=配置类,即可以实现spring的bean加载前调用的初始化程序
参考: https://zhuanlan.zhihu.com/p/343704665
- ApplicationContextAware
如果我们需要在工具类中获取bean怎么办,又不能用@Autowired引入
此时,我们可以创建一个类交给Spring管理,然后实现ApplicationContextAware接口,重写其中的set方法。然后在这个类中写个方法去get全局变量ApplicationContext,获取写个方法获取某个bean就可以用了
源码逻辑:启动过程中会去找到ApplicationContextAware接口的实现类,然后通过回调其中的set方法,把全局使用的ApplicationContext给set进去,所以我们就可以使用了。
- springboot项目有很多自动配置注解,比如@EnableScheduling定时任务,@EnableAsync异步编程。
其实实现很简单,点开这个注解,你会发现里面有一个@Import({SchedulingConfiguration.class})注解。
进入导入的注解类,返回一个return new ScheduledAnnotationBeanPostProcessor();
进入这个创建的类,有一个核心方法:
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (!(bean instanceof AopInfrastructureBean) && !(bean instanceof TaskScheduler) && !(bean instanceof ScheduledExecutorService)) {
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
if (!this.nonAnnotatedClasses.contains(targetClass)) {
Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass, (method) -> {
Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, Scheduled.class, Schedules.class);
return !scheduledMethods.isEmpty() ? scheduledMethods : null;
});
if (annotatedMethods.isEmpty()) {
this.nonAnnotatedClasses.add(targetClass);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No @Scheduled annotations found on bean class: " + targetClass);
}
} else {
// 重点就是这里,因为我们是定时任务自动配置,所以这里面会找到加上@Scheduled注解的所有方法,然后去执行他们
annotatedMethods.forEach((method, scheduledMethods) -> {
scheduledMethods.forEach((scheduled) -> {
this.processScheduled(scheduled, method, bean);
});
});
if (this.logger.isTraceEnabled()) {
this.logger.trace(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName + "': " + annotatedMethods);
}
}
}
return bean;
} else {
return bean;
}
}
所以其实很简单,我们在启动类上配置@EnableXXX注解,就会自动导入对应的实现类,实现类中有某个方法去执行对应的逻辑,
比如找到添加了定时器@Scheduled的注解方法或者添加了异步的注解方法,去执行其中的逻辑。
Spring如何把bean对象放入applicationContext的实现类AnnotationConfigServletWebServerApplicationContext中的源码在哪里研究下
listeners研究下
重要方法:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
// 获取类加载器
ClassLoader classLoader = this.getClassLoader();
// 获取所有实现类的名称集合
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 生成实现类的对象集合
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
- 通过@EnableAutoConfiguration注解中的@Import({AutoConfigurationImportSelector.class})注解,同样扫描所有jar包下META-INF/spring.factories文件,生成EnableAutoConfiguration接口的实现类的对象,比如mybatis-spring-boot-starter就是这种方式加载的。
- 通过@ComponentScan注解,扫描启动类所在包及其子包中所有包含@Component的类并实例化,主要包括我们自定义的@Controller,@Service等注解。
网友评论