spring框架必知点整理(总结概括篇)

1. springboot的启动流程

springboot的启动流程分两阶段:

  1. 构造函数,实例化SpringApplication阶段
  2. 实例化后,调用SpringApplication的run方法阶段

 启动流程

@SpringBootApplication
public class MySpringBootWebApplication {
        public static void main(String[] args) {
        // 启动springboot项目
        SpringApplication.run(AvfmsWebApplication.class, args);
    } 
}

1.1 实例化SpringApplication 

源码如下:

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
        // step1:安全验证:启动类必须不为空
		Assert.notNull(primarySources, "PrimarySources must not be null");
        // step2:设置启动类
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // step3:推断当前项目类型
        // 有3种:none、servlet、reactive
        // 一般我们的项目都是servlet
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // step4:获取所有在META-INF/spring.factories文件中指定了的BootstrapRegistryInitializer实现类
		this.bootstrapRegistryInitializers = new ArrayList<>(
				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
        // step5:获取所有在META-INF/spring.factories文件中指定了的ApplicationContextInitializer实现类
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // step6:获取所有在META-INF/spring.factories文件中指定了的ApplicationListener实现类
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // step7:推断出当前main函数所在的类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

比较重要的有:

  1. 推断了当前项目类型:none、servlet、reactive;这个项目类型,觉得了run方法里要创建什么样的ApplicationContext,也就是IOC容器。一般我们的项目为servlet类型
  2. 获取了所有指定的ApplicationContextInitializer实现类,run方法在IOC容器创建完,准备refresh阶段前,会调用这些实现类
  3. 获取了所有指定的ApplicationListener实现类,这些类会监听IOC容器创建bean的过程

1.2 调用SpringApplication实例的run方法

创建完SpringApplication实例后,调用run方法,每一个阶段,深入去看都有很多值得说的,但是我这篇文章点到为止(主要是自己水平还不够哈哈哈),就只做概括了,源码如下:

	public ConfigurableApplicationContext run(String... args) {
        // step1:开始计时
		long startTime = System.nanoTime();
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;
		configureHeadlessProperty();
        // step2:获取SpringApplicationListener,用于发布springboot项目启动的各个阶段事件
		SpringApplicationRunListeners listeners = getRunListeners(args);
        // step3:发布当前项目为staring,启动中状态
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // step4:根据项目类型,获取IOC容器的创建环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			configureIgnoreBeanInfo(environment);
            // step5:控制台打印banner信息
			Banner printedBanner = printBanner(environment);
            // step6:根据项目类型,创建IOC实例
			context = createApplicationContext();
			context.setApplicationStartup(this.applicationStartup);
            // step7:调用IOC容器的刷新方法前,做的准备工作
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            // step8:最核心的方法,IOC容器刷新
			refreshContext(context);
            // step9:IOC容器刷新后,里面其实是一个空方法,什么都没做
			afterRefresh(context, applicationArguments);
            // step10:结束计时,计算耗时
			Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
			}
            // step11:发布当前项目为started,已启动状态
			listeners.started(context, timeTakenToStartup);
            // step12:调用ApplicationRunner接口的实现run方法
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}
		try {
			Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
			listeners.ready(context, timeTakenToReady);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

最核心的方法就是创建IOC容器和刷新IOC容器了,如图:

1.2.1 根据项目类型创建IOC容器

就是根据实例化SpringApplication的时候,推断的项目类型,来创建对应的IOC容器:

如果是servlet项目类型,创建的IOC容器为:AnnotationConfigServletWebServerApplicationContext,具体怎么执行到这一步的,我就不放代码了,跳来跳去有点复杂。

 1.2.2 刷新IOC容器

最重要的刷新IOC容器方法,其实就是执行spring里的bean实例创建等各种核心方法过程。如果是servlet项目类型,最终执行到的方法是AbstractApplicationContext的refresh方法:

为什么会执行到这个方法,因为servlet项目IOC实例是AnnotationConfigServletWebServerApplicationContext,这个类间接继承了AbstractApplicationContext抽象类,类图:

 2. springboot自动装配原理

通过注解EnableAutoConfiguration来实现的,这个注解在springboot的启动注解SpringBootApplication中集成了。 

这个注解开启,那么就会解析springboot内置的两个文件:

  1. META-INF/spring.factories

  2. META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

这两个文件里定义了要加载的AutoConfiguration,有很多如RedisAutoConfiguration、DataSourceAutoConfiguration等,都是springboot帮我们定义好的,当然我们也可以自定义

举个例子,redis的RedisAutoConfiguration,通过ConditionOnClass注解来判断是否引入了redis的依赖,如果引入redis 

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

那么就初始化RedisTemplate实例到spring的IOC容器中,这样就做到了引入依赖,就可以在项目直接使用了。 

3. spring里bean的生命周期

或者也叫bean的创建流程,在spring中可以通过注解或者原始的XML定义的方式,来让spring帮我们创建bean并放入IOC容器中管理,创建的流程如下:

  1. 根据BeanDefinition获取bean的定义信息,准备实例化

  2. 实例化前,调用InstantiationAwareBeanPostProcessor前置处理器

  3. 构造方法实例化bean

  4. 实例化后,调用InstantiationAwareBeanPostProcessor后置处理器

  5. 执行依赖注入,如处理Autowired、Value等注解

  6. 执行Aware接口的方法

  7. 调用BeanPostProcesser前置处理器

  8. 执行初始化方法,如实现了InitializingBean接口或自定义的init-method

  9. 调用BeanPostProcesser后置处理器,可以对Bean做一些增强,比如使用动态代理

  10. 销毁阶段,调用销毁回调函数(如果实现了的话)

4. bean的循环依赖问题

循环依赖的问题是在bean的依赖注入阶段产生的,例如实例A在依赖注入的时候需要注入B,这个时候会走创建B实例的流程,而在实例B的依赖注入阶段,又需要注入A,就产生的循环依赖。

 spring通过三级缓存的方案解决循环依赖的问题,三级缓存其实就是3个map,一级缓存存放的是完整的spring bean信息,二级缓存存放的是未走完spring初始化周期的不完整bean信息,三级缓存存放的是bean的BeanFactory

为什么要用三级缓存,而不是二级缓存,因为存在AOP动态代理生成实例的场景,举例,beanA是需要动态代理生成的实例,beanB是普通的单例,beanA与beanB循环依赖,他们的解决流程如下: 

三级缓存解决循环依赖的问题:

  1. 实例化beanA,并把A的对象工厂BeanFactory放入三级缓存

  2. 依赖注入beanB

  3. 实例化beanB,并把B的对象工厂BeanFactory放入三级缓存

  4. 依赖注入beanA,会从三级缓存中获取A的BeanFactory,并调用getObject方法获取实例A,此时:

    1. 如果A是需要生成AOP动态代理对象的实例,那么此时直接生成代理实例beanA#proxy,放入二级缓存,返回这个代理实例beanA#proxy

    2. 如果beanA是一个普通的单例,那么获取到第1步实例化的beanA,返回这个普通单例beanA

  5. B根据BeanFactory返回的A注入

  6. 初始化beanB完成,beanB把自己放到一级缓存里,同时删除三级缓存中自己的BeanFactory

  7. 初始化beanA,这里会来到BeanPostProcessor的后置处理器,AOP动态代理对象是在这里创建的,但是:

    1. 如果能在二级缓存中,找到beanA#proxy,那么就不会重新生成A的代理对象,直接用二级缓存的

  8. beanA完成,将A或者动态代理对象A放入一级缓存,同时删除二级缓存、三级缓存中的自己

 有一种循环依赖spring是没有办法解决的,就是在构造函数里,A跟B的相互依赖:

-- 在A的构造函数里依赖B,但是B的构造函数里又依赖A
public class A{
    B b;
    public A(B b){
        this.b = b;
    }
}

public class B{

    A a;
    public B(A a){
        this.a = a;
    }
}

-- 解决方案为,给其中一个bean添加懒加载,即用到的时候再加载
-- 比如给B添加懒加载
public class A{
    B b;
    public A(@Lazy B b){
        this.b = b;
    }
}

5. springMVC的执行流程

springMVC有4大组件,来解决一次请求的全过程,他们的流程如图,来着黑马于洪波老师的图:

  1. 前端请求首先都来到dispatcherServlet,相当于一个调度中心,响应并调度所有的请求

  2. dispatcherServlet请求HandlerMapping,HandlerMapping中记录了请求路径和对应处理类#方法的映射

  3. HandlerMapping返回处理本次请求的类#方法

  4. dispatcherServlet不直接调用处理方法,而且请求HandlerAdaptor来处理

  5. HandlerAdaptor封装调用类#方法的参数,然后调用处理方法

  6. 返回ModelAndView给dispatcherServlet(注意在现在很多项目中不返回ModelAndView,而且直接返回数据,那么dispatcherServlet也就直接把数据返回给前端就结束了)

  7. dispatcherServlet调用ViewResolver

  8. ViewResolver解析视图并返回

  9. dispatcherServlet最后渲染视图然后返回前端

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值