SpringBoot入门到精通-(七)SpringBoot启动流程

1.认识SpringApplication

SpringApplication 类提供了一种可通过运行 main() 方法来启动 Spring 应用的简单方式。多数情况下,您只需要委托给静态的 SpringApplication.run 方法:

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

如果 SpringApplication 的默认设置不符合您的想法,您可以创建本地实例进行定制化。例如,要关闭 banner,您可以这样:

public static void main(String[] args) {
    SpringApplication app = new SpringApplication(MySpringConfiguration.class);
    app.setBannerMode(Banner.Mode.OFF);
    app.run(args);
}

2.SpringApplication.run执行流程
SpringApplication可用于从 Java 主方法引导和启动 Spring 应用程序的类。默认情况下,类将执行以下步骤来启动应用:

  • 创建一个适当的[ApplicationContext]实例(取决于您的类路径
  • 注册 [CommandLinePropertySource]以将命令行参数公开为 Spring 属性
  • 刷新应用程序上下文,加载所有单例 bean
  • 触发任何[CommandLineRunner]bean

下面我们就来详细分析一下它的执行流程,见:org.springframework.boot.SpringApplication#run(java.lang.String…)

public ConfigurableApplicationContext run(String... args) {
		//创建秒表,用来计算启动事件
		StopWatch stopWatch = new StopWatch();
    	//启动秒表
		stopWatch.start();
         //Spring IOC 容器对象
		ConfigurableApplicationContext context = null;
         //收集Spring Boot 异常报告器的list
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    	//配置无头属性,java.awt.headless
		configureHeadlessProperty();
    	//SpringBoot的SpringApplication run方法的侦听器 监听器,
    	//SpringApplicationRunListeners维护了一个 SpringApplicationRunListener 集合
		SpringApplicationRunListeners listeners = getRunListeners(args);
    	//会触发所有 SpringApplicationRunListener#starting的执行
         //,会通过SimpleApplicationEventMulticaster广播一个ApplicationStartingEvent事件
		listeners.starting();
		try {
             //把应用参数封装到DefaultApplicationArguments,通过它可以访问应用参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
             //创建环境对象,Environment包括了property和profile
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            //配置忽略 Bean 信息 ,spring.beaninfo.ignore
			configureIgnoreBeanInfo(environment);
            //打印横幅
			Banner printedBanner = printBanner(environment);
            //创建IOC容器对象 AnnotationConfigApplicationContext
			context = createApplicationContext();
             //创建Spring Boot 异常报告器实例。会扫描spring.factories下的 FailureAnalyzers实例,
            //FailureAnalyzer是用于分析故障并提供可显示给用户的诊断信息
            //比如:NoSuchBeanDefinitionFailureAnalyzer ; DataSourceBeanCreationFailureAnalyzer
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
            //刷新容器准备工作
            //1.把environment绑定到context容器对象
            //2.context后置处理,比如绑定resourceLoader
            //3.触发 ApplicationContextInitializer#initialize初始化(用于在刷新之前初始化Context回调接口。)
            //4.触发 listener.contextPrepared ,抛出 ApplicationContextInitializedEvent 事件
            //5.把ApplicationArguments注册到容器中成为一个Bean
            //6.把 Banner注册到容器中成为一个Bean
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            //刷新容器,底层走spring的刷新容器流程
			refreshContext(context);
            //空方法,留给我们扩展
			afterRefresh(context, applicationArguments);
            //暂定秒表
			stopWatch.stop();
			if (this.logStartupInfo) {
                //打印秒表记录的时间
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
            //触发 SpringApplicationRunListener#started方法抛出 ApplicationStartedEvent 事件
			listeners.started(context);
            //调用 ApplicationRunner 和 CommandLineRunner
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
            //处理异常,会从exceptionReporters拿出异常进行打印
            //以及会触发 SpringApplicationRunListeners#failed,广播 ApplicationFailedEvent事件
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
                //执行listeners.running , 抛出 ApplicationReadyEvent 事件
			listeners.running(context);
		}
		catch (Throwable ex) {
               //处理异常,会从exceptionReporters拿出异常进行打印
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
    	//返回容器
		return context;
	}

画一个流程图总结一下:

3.StopWatch秒表

Spring体用的秒表,允许对多个任务进行计时,显示每个命名任务的总运行时间和运行时间。隐藏System.nanoTime()的使用,提高应用程序代码的可读性并减少计算错误的可能性。注意,此对象并非设计为线程安全的,也不使用同步。

public class StopWatch {
		/**
	 * Identifier of this {@code StopWatch}.
	 * <p>Handy when we have output from multiple stop watches and need to
	 * distinguish between them in log or console output.
	 */
    //任务的ID
	private final String id;

	private boolean keepTaskList = true;
    //任务列表
	private final List<TaskInfo> taskList = new LinkedList<>();

	/** Start time of the current task. */
    //开始时间
	private long startTimeNanos;

	/** Name of the current task. */
    //当前任务名
	@Nullable
	private String currentTaskName;

	@Nullable
	private TaskInfo lastTaskInfo;
    //任务数量
	private int taskCount;

	/** Total running time. */
    //总时间
	private long totalTimeNanos;
    
    //开始任务,穿了一个“”作为taskName
    public void start() throws IllegalStateException {
		start("");
	}

	/**
	 * Start a named task.
	 * <p>The results are undefined if {@link #stop()} or timing methods are
	 * called without invoking this method first.
	 * @param taskName the name of the task to start
	 * @see #start()
	 * @see #stop()
	 */
    //开始任务
	public void start(String taskName) throws IllegalStateException {
		if (this.currentTaskName != null) {
			throw new IllegalStateException("Can't start StopWatch: it's already running");
		}
        //任务名
		this.currentTaskName = taskName;
        //记录开始时间
		this.startTimeNanos = System.nanoTime();
	}
    
    //停止秒表
    public void stop() throws IllegalStateException {
		if (this.currentTaskName == null) {
			throw new IllegalStateException("Can't stop StopWatch: it's not running");
		}
        //时间差
		long lastTime = System.nanoTime() - this.startTimeNanos;
        //累计时间
		this.totalTimeNanos += lastTime;
        //创建一个TaskInfo任务信息
		this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
		if (this.keepTaskList) {
            //加入任务列表
			this.taskList.add(this.lastTaskInfo);
		}
        //增加任务数量
		++this.taskCount;
        //清空任务名
		this.currentTaskName = null;
	}
    //以优雅的格式打印秒表记录的时间日志
    public String prettyPrint() {
		StringBuilder sb = new StringBuilder(shortSummary());
		sb.append('\n');
		if (!this.keepTaskList) {
			sb.append("No task info kept");
		}
		else {
			sb.append("---------------------------------------------\n");
			sb.append("ns         %     Task name\n");
			sb.append("---------------------------------------------\n");
			NumberFormat nf = NumberFormat.getNumberInstance();
			nf.setMinimumIntegerDigits(9);
			nf.setGroupingUsed(false);
			NumberFormat pf = NumberFormat.getPercentInstance();
			pf.setMinimumIntegerDigits(3);
			pf.setGroupingUsed(false);
			for (TaskInfo task : getTaskInfo()) {
				sb.append(nf.format(task.getTimeNanos())).append("  ");
				sb.append(pf.format((double) task.getTimeNanos() / getTotalTimeNanos())).append("  ");
				sb.append(task.getTaskName()).append("\n");
			}
		}
		return sb.toString();
	}
}

StopWatch秒表可以用来对多个任务计时,,start的时候会使用System.nanoTime()来获时间记录到startTimeNanos ,stop结束方计算时间差,然后会把每次的时间和任务名封装成TaskInfo,加入taskList。最后会累计每次任务的时间总额。提供了prettyPrint方法以优雅的格式组织秒表记录的时间日志。

但是要注意:虽然它可以允许多个任务记时,但是它并不是线程安全的。

4.SpringBootExceptionReporter异常报告
4.1.核心类认识

SpringBootExceptionReporter是用于支持自定义上报SpringApplication启动错误的回调接口,它可以把启动的错误日志汇报给用户

@FunctionalInterface
public interface SpringBootExceptionReporter {

	/**
	 * Report a startup failure to the user.
	 * @param failure the source failure
	 * @return {@code true} if the failure was reported or {@code false} if default
	 * reporting should occur.
	 */
	boolean reportException(Throwable failure);

}

reportException方法的作用就是为用户报告错误。它的唯一实现类是 FailureAnalyzers ,它提供了

	
final class FailureAnalyzers implements SpringBootExceptionReporter {

	private static final Log logger = LogFactory.getLog(FailureAnalyzers.class);

	private final ClassLoader classLoader;
    //故障分析仪
	private final List<FailureAnalyzer> analyzers;
    
    //报告指定的异常
    @Override
	public boolean reportException(Throwable failure) {
        //把异常封装到FailureAnalysis
        //FailureAnalysis中维护了很多的FailureAnalyzer,它的作用是分析故障并提供可显示给用户的诊断信息
		FailureAnalysis analysis = analyze(failure, this.analyzers);
		return report(analysis, this.classLoader);
	}
    
    //分析异常
	private FailureAnalysis analyze(Throwable failure, List<FailureAnalyzer> analyzers) {
		for (FailureAnalyzer analyzer : analyzers) {
			try {
                //把Throwable异常信息封装成FailureAnalysis
				FailureAnalysis analysis = analyzer.analyze(failure);
				if (analysis != null) {
					return analysis;
				}
			}
			catch (Throwable ex) {
				logger.debug(LogMessage.format("FailureAnalyzer %s failed", analyzer), ex);
			}
		}
		return null;
	}
    
	private boolean report(FailureAnalysis analysis, ClassLoader classLoader) {
        //加载FailureAnalysisReporter,  FailureAnalysisReporter用来 向用户报告FailureAnalysis分析。
		List<FailureAnalysisReporter> reporters = SpringFactoriesLoader.loadFactories(FailureAnalysisReporter.class,
				classLoader);
		if (analysis == null || reporters.isEmpty()) {
			return false;
		}
		for (FailureAnalysisReporter reporter : reporters) {
            //报告异常
			reporter.report(analysis);
		}
		return true;
	}

reportException方法接收一个Throwable ,然后Throwable 会被封装到FailureAnalysis。然后通过SpringFactoriesLoader去加载FailureAnalysisReporter(向用户报告FailureAnalysis分析),通过FailureAnalysisReporter去报告异常。FailureAnalysis结构如下

public class FailureAnalysis {
	//异常描述
	private final String description;
	
	private final String action;
	//异常对象
	private final Throwable cause;

LoggingFailureAnalysisReporter结构如下见:org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter#report

public final class LoggingFailureAnalysisReporter implements FailureAnalysisReporter {

	private static final Log logger = LogFactory.getLog(LoggingFailureAnalysisReporter.class);

@Override
	public void report(FailureAnalysis failureAnalysis) {
        
		if (logger.isDebugEnabled()) {
			logger.debug("Application failed to start due to an exception", failureAnalysis.getCause());
		}
        //把错误日志打印到控制台
		if (logger.isErrorEnabled()) {
			logger.error(buildMessage(failureAnalysis));
		}
	}
	//构建错误日志内容
	private String buildMessage(FailureAnalysis failureAnalysis) {
		StringBuilder builder = new StringBuilder();
		builder.append(String.format("%n%n"));
		builder.append(String.format("***************************%n"));
		builder.append(String.format("APPLICATION FAILED TO START%n"));
		builder.append(String.format("***************************%n%n"));
		builder.append(String.format("Description:%n%n"));
		builder.append(String.format("%s%n", failureAnalysis.getDescription()));
		if (StringUtils.hasText(failureAnalysis.getAction())) {
			builder.append(String.format("%nAction:%n%n"));
			builder.append(String.format("%s%n", failureAnalysis.getAction()));
		}
		return builder.toString();
	}
4.2.报告异常

在SpringApplication#run方法中有try-catch操作,如果启动出现异常,会执行org.springframework.boot.SpringApplication#handleRunFailure来处理异常

private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
			Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
		try {
			try {
                //处理退出码,发布一个ExitCodeEvent事件
				handleExitCode(context, exception);
				if (listeners != null) {
                    //发布ApplicationFailedEvent事件
					listeners.failed(context, exception);
				}
			}
			finally {
                //报告异常,通过LoggingFailureAnalysisReporter 把异常打印到控制台
				reportFailure(exceptionReporters, exception);
				if (context != null) {
					context.close();
				}
			}
		}
		catch (Exception ex) {
			logger.warn("Unable to close ApplicationContext", ex);
		}
		ReflectionUtils.rethrowRuntimeException(exception);
	}

上面重要是发布ApplicationFailedEvent事件, 然后通过SpringBootExceptionReporter#reportException去把异常打印到控制台。

5.监听器机制
上面代码中有很多地方都出现了事件发布,比如: SpringApplicationRunListeners listeners = getRunListeners(args) 它的作用是广播ApplicationStartingEvent事件,这用到了Spring的监听器机制。我们可以认为以 Listenner 结尾的类都是监听器,监听器使用到了观察者设计模式,其作用是监听一些事件的发生从而进行一些操作。监听器的好处是可以实现代码解耦,对此你可能不是很能理解,我这里用一个js例子来代理理解事件机制

function dothing(){
    //回调函数
}
//监听button的click事件
$("#button").click(dothing);

上面代码相信你是写过的,就是一个JS监听按钮点击事件,这里需要明确三个角色

  • button : 事件源,这个事件发生在谁身上
  • click : 事件类型 ,按钮发生了什么事件
  • dothing : 回调函数,当button被点击,触发 dothing函数。

那么Java中的事件机制和上面案例很相似,我这里有个案例:当用户注册成功,给用户推送一条短信,使用事件机制来实现

这么理解这幅图

  1. 首先需要定义一个事件类型RegisterApplicationEvent 继承于ApplicationEvent , 代表的注册这个事件,好比是"click"
  2. 然后需要在注册逻辑中,使用事件发布器ApplicationEventPublisher 发布该事件 ,好比 button 被 click了
  3. 事件被发布,需要触发某段逻辑,所以要写一个监听器类实现ApplicationListernner,该监听器监听的是“注册事件”。
  4. 然后就调用短信发送逻辑发送短信即可。好比是上面的dothing回调函数。

相信大致的流程你是看懂了,但是有些陌生类让我们比较迷惑,下面我们就来系统的认识一下这些类。

5.1. 核心类认识

EventListener

EventListener是java提供的最顶层的监听器接口,不管是Servlet的监听器还是Spring的监听器都是该接口的子类(所有事件侦听器接口都必须实现于接口)。

/**
 * A tagging interface that all event listener interfaces must extend.
 * @since JDK1.1
 */
public interface EventListener {
}

ApplicationListener

Spring提供的基于 Observer 设计模式(观察者)设计的监听器接口,实现了EventListener

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	/**
	 * Handle an application event.
	 * @param event the event to respond to
	 */
	void onApplicationEvent(E event);

}

编写Spring的监听器都需要实现 ApplicationListener这个接口 , 比如这个类

public class ScheduledAnnotationBeanPostProcessor  implements ApplicationListener<ContextRefreshedEvent>...省略{
    @Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		if (event.getApplicationContext() == this.applicationContext) {
			// Running in an ApplicationContext -> register tasks this late...
			// giving other ContextRefreshedEvent listeners a chance to perform
			// their work at the same time (e.g. Spring Batch's job registration).
			finishRegistration();
		}
	}
}

ScheduledAnnotationBeanPostProcessor就是Schedule定时任务的处理器 ,它是实现ApplicationListener监听器接口,监听了ContextRefreshedEvent容器刷新完成事件,也就是说当Spring容器刷新完成,就会触发 onApplicationEvent方法,那么该监听器就可以对 Scheduled做一些处理了。

ApplicationEvent

Spring提供的事件对象,实现于EventObject,它代表了事件的类型。下面是EventObject的结构

public class EventObject implements java.io.Serializable {

    private static final long serialVersionUID = 5516075349620653480L;

    /**
     * The object on which the Event initially occurred.
     */
    protected transient Object  source;

    /**
     * Constructs a prototypical Event.
     *
     * @param    source    The object on which the Event initially occurred.
     * @exception  IllegalArgumentException  if source is null.
     */
    public EventObject(Object source) {
        if (source == null)
            throw new IllegalArgumentException("null source");

        this.source = source;
    }

    /**
     * The object on which the Event initially occurred.
     *
     * @return   The object on which the Event initially occurred.
     */
    public Object getSource() {
        return source;
    }

    /**
     * Returns a String representation of this EventObject.
     *
     * @return  A a String representation of this EventObject.
     */
    public String toString() {
        return getClass().getName() + "[source=" + source + "]";
    }
}

Object source; 可以用来传送数据 ,当我们要发布一个事件的时候,就需要创建一个事件对象,同时事件对象指定一个shource。在Spring中内置了很多事件对象,他们都是ApplicationEvent的子类比如:

  • ContextStartedEvent : ApplicationContext Spring容器启动时引发的事件。
  • ContextRefreshedEvent : 初始化或刷新ApplicationContext时引发的事件。
  • ContextStoppedEvent:当ApplicationContext停止时引发的事件。
  • ContextClosedEvent:ApplicationContext关闭时引发的事件。

下面这几个是SpringBoot提供的事件对象,也是ApplicationEvent的子类,在上面的SpringBoot执行流程中也有看到

  • ApplicationPreparedEvent : 当SpringApplication启动并且ApplicationContext已完全准备好但未刷新时发布的事件。 将加载 bean 定义,并且在此阶段Environment已准备好使用
  • ApplicationStartingEvent : 在SpringApplication启动后尽可能早地发布事件 - 在Environment或ApplicationContext可用之前,但在ApplicationListener已注册之后
  • ApplicationStartedEvent :刷新应用程序上下文但在调用任何application和command line运行程序之前发布的事件。
  • ApplicationReadyEvent :发布该事件表明应用程序已准备好为请求提供服务
  • ApplicationFailedEvent : SpringApplication启动失败时发布的事件

ApplicationEventPublisher

事件发布器,提供了发布事件的基础功能

@FunctionalInterface
public interface ApplicationEventPublisher {
    default void publishEvent(ApplicationEvent event) {
        this.publishEvent((Object)event);
    }

    void publishEvent(Object event);
}

大名鼎鼎的 ApplicationContext 容器对象就是实现该接口ApplicationEventPublisher ,拥有了事件发布的能力。

他们的继承体系如下

5.2.发布自定义事件
我们来把上面的案例实现以下,即:当注册成功,给用户发送一条短信,确定好三个角色

  • 事件是什么?“注册成功事件” -> 需要定义一个事件对象
  • 事件源是什么?“注册逻辑” -> 在注册成功后,发布"注册事件"
  • 做出的反应是什么?“推送短信” -> 监听“注册事件”,推送短信

第一步,创建一个事件对象,继承ApplicationEvent

//注册成功事件对象
public class RegisterSuccessEvent extends ApplicationEvent {

    //Object source :待会要传递的参数
    public RegisterSuccessEvent(Object source) {
        super(source);
    }
}

第二步,创建事件监听器,监听RegisterSuccessEvent事件

//注册成功的监听器,监听 RegisterSuccessEvent事件
@Slf4j
@Service
public class RegisterSuccessListenner implements ApplicationListener<RegisterSuccessEvent> {

    @Override
    public void onApplicationEvent(RegisterSuccessEvent event) {
        Map<String,String> source = (Map<String, String>) event.getSource();
        if(source == null){
            log.error("短信内容不能为空");
        }else{
            String phone = source.get("phone");
            String message = source.get("message");
            log.info("注册成功,给: {} 发送短信,内容: {}" , phone , message);
        }
    }
}

第三步,注册逻辑,抛出RegisterSuccessEvent事件,我这里就简单模拟一下


//注入事件发布器
@Autowired
private ApplicationEventPublisher applicationEventPublisher;

@RequestMapping("/register")
public JSONResult register(){
    Map<String,String> map = new HashMap<>();
    map.put("phone","18233333333");
    map.put("message","注册成功");
    applicationEventPublisher.publishEvent(new RegisterSuccessEvent(map));
    return JSONResult.builder().build();
}

第四步,启动测试,效果如下

2022-01-14 10:10:31.941 INFO 20448 — [nio-8081-exec-5] cn.whale.event.RegisterSuccessListenner : 注册成功,给: 18233333333 发送短信,内容: 注册成功

这里我再给一个小案例:项目启动完成,往数据库初始化一个管理员账号,这个应该怎么做呢?

我们可以编写一个类,实现 ApplicationListener<ContextRefreshedEvent> ,监听事件是Spring容器刷新完成,然后复写onApplicationEvent方法,在该方法中往数据库保存管理员账号即可。你可以自己试着写一下。

5.3.web三大监听器

这里给大家介绍web环境中,三个比较有用的监听器

HttpSessionListener : 它是针对于Session的监听器,提供了两个方法来监听session的创建和销毁,代码如下

public interface HttpSessionListener extends EventListener {
    //session创建
    public default void sessionCreated(HttpSessionEvent se) {
    }
    //session销毁
    public default void sessionDestroyed(HttpSessionEvent se) {
    }
}

如果我们实现该接口,就可以监听到session的什么周期方法了,你可能会问,这有什么用,那我给一个需求你思考一下怎么做:“记录用户在网站的登录到退出的时间”

ServletContextListener : 它是用来监听ServletContext上下文对象的生命周期,结构如下


public interface ServletContextListener extends EventListener {

    //上下文初始化,代表应用启动
    public default void contextInitialized(ServletContextEvent sce) {
    }

    //上下文销毁,代表应用结束
    public default void contextDestroyed(ServletContextEvent sce) {
    }
}

我这么说吧,Spirng整合web,就是通过实现ServletContextListenner监听器 ,复写 contextInitialized方法,然后在该方法中创建WebApplicationContext容器。所以该监听器可以监听整个程序的启动和销毁。

ServletRequestListener : 它是监听request对象的生命周期方法。

public interface ServletRequestListener extends EventListener {

   //销毁
    public default void requestDestroyed (ServletRequestEvent sre) {
    }

    //request初始化
    public default void requestInitialized (ServletRequestEvent sre) {
    }
}

该监听器是监听request对象的初始化和销毁 ,我不知道你有没有通过 RequestContextHolder.getRequestAttributes() 来获取Request对象,它的实现原理就是通过一个 RequestContextListener类去实现 ServletRequestListener 接口,然后在requestInitialized方法中拿到request对象,封装到RequestContextHolder中的一个 ThreadLocal<RequestAttributes> requestAttributesHolder 中存储起来,所以我们在业务代码中可以通过RequestContextHolder直接获取Request对象。

6.SpringBoot的事件

讲完Spirng事件我们来讲SpringBoot中的事件,在SpringBoot启动流程中出现 SpringApplicationRunListeners#starting 等代码,其实就是在发布SpringBoot的事件。我们来看

6.1.SpringApplicationRunListeners

SpringApplicationRunListeners : 下面是 SpringApplicationRunListeners的代码结构

class SpringApplicationRunListeners {

	private final Log log;
    //保存了很多的SpringApplicationRunListener
	private final List<SpringApplicationRunListener> listeners;

	SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<>(listeners);
	}
    //调用starting,本质是调用SpringApplicationRunListener的starting
	void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}
    //调用environmentPrepared环境准备完成,本质是调用SpringApplicationRunListener的environmentPrepared
	void environmentPrepared(ConfigurableEnvironment environment) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.environmentPrepared(environment);
		}
	}
     //调用contextPrepared 容器刷新完成,本质是调用SpringApplicationRunListener的contextPrepared
	void contextPrepared(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextPrepared(context);
		}
	}
    //容器加载完成
	void contextLoaded(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextLoaded(context);
		}
	}
    //启动
	void started(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.started(context);
		}
	}
   //运行中
	void running(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.running(context);
		}
	}
    //出现错误
	void failed(ConfigurableApplicationContext context, Throwable exception) {
		for (SpringApplicationRunListener listener : this.listeners) {
			callFailedListener(listener, context, exception);
		}
	}
    //出现错误
	private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context,
			Throwable exception) {
		try {
			listener.failed(context, exception);
		}
		catch (Throwable ex) {
			if (exception == null) {
				ReflectionUtils.rethrowRuntimeException(ex);
			}
			if (this.log.isDebugEnabled()) {
				this.log.error("Error handling failed", ex);
			}
			else {
				String message = ex.getMessage();
				message = (message != null) ? message : "no error message";
				this.log.warn("Error handling failed (" + message + ")");
			}
		}
	}

}

在 SpringApplicationRunListeners这类中维护了一个 List<SpringApplicationRunListener> listeners;集合,SpringApplicationRunListeners#starting方法本质是调用的SpringApplicationRunListener#starting方法

6.2.SpringApplicationRunListener

SpringApplicationRunListener是一个接口,其中维护了很多的事件方法

public interface SpringApplicationRunListener {

	//开始
	default void starting() {
	}
    //环境准备完成
	default void environmentPrepared(ConfigurableEnvironment environment) {
	}
     //容器准备完成
	default void contextPrepared(ConfigurableApplicationContext context) {
	}
    //容器加载完成
	default void contextLoaded(ConfigurableApplicationContext context) {
	}
    //启动成功
	default void started(ConfigurableApplicationContext context) {
	}
    //运行中
	default void running(ConfigurableApplicationContext context) {
	}
    //启动失败
	default void failed(ConfigurableApplicationContext context, Throwable exception) {
	}

}

这里我找到了它的唯一一个默认实现类EventPublishingRunListener , 看名字是一个时间发布器

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    ...省略...
        
    //简单应用事件广播器
    private final SimpleApplicationEventMulticaster initialMulticaster;
    
    @Override
	public void starting() {
        //通过 SimpleApplicationEventMulticaster 发布ApplicationStartingEvent 应用开始启动事件
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}

	@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
        //通过 SimpleApplicationEventMulticaster 发布ApplicationEnvironmentPreparedEvent 应用环境准备完成事件
		this.initialMulticaster
				.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
	}

	@Override
	public void contextPrepared(ConfigurableApplicationContext context) {
        //通过 SimpleApplicationEventMulticaster 发布ApplicationContextInitializedEvent 应用初始化完成事件
		this.initialMulticaster
				.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
	}

	@Override
	public void contextLoaded(ConfigurableApplicationContext context) {
		for (ApplicationListener<?> listener : this.application.getListeners()) {
			if (listener instanceof ApplicationContextAware) {
				((ApplicationContextAware) listener).setApplicationContext(context);
			}
			context.addApplicationListener(listener);
		}
        //通过 SimpleApplicationEventMulticaster 发布ApplicationPreparedEvent 应用准备成功事件
		this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
	}

	@Override
	public void started(ConfigurableApplicationContext context) {
        //通过 ConfigurableApplicationContext 发布ApplicationStartedEvent 应用启动成功事件
		context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
	}

	@Override
	public void running(ConfigurableApplicationContext context) {
        //通过 ConfigurableApplicationContext 发布ApplicationReadyEvent 应用启动完毕,可以接受请求了
		context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
	}

	@Override
	public void failed(ConfigurableApplicationContext context, Throwable exception) {
        //应用启动失败的事件
		ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
		if (context != null && context.isActive()) {
			// Listeners have been registered to the application context so we should
			// use it at this point if we can
			context.publishEvent(event);
		}
		else {
			// An inactive context may not have a multicaster so we use our multicaster to
			// call all of the context's listeners instead
			if (context instanceof AbstractApplicationContext) {
				for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
						.getApplicationListeners()) {
					this.initialMulticaster.addApplicationListener(listener);
				}
			}
            //处理错误
			this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
			//通过 initialMulticaster 发布ApplicationFailedEvent 应用启动失败
			this.initialMulticaster.multicastEvent(event);
		}
	}

	private static class LoggingErrorHandler implements ErrorHandler {

		private static final Log logger = LogFactory.getLog(EventPublishingRunListener.class);

        //打印错误
		@Override
		public void handleError(Throwable throwable) {
			logger.warn("Error calling ApplicationEventListener", throwable);
		}

	}
}

在EventPublishingRunListener中维护了一个 SimpleApplicationEventMulticaster 事件广播器,其中 starting ;environmentPrepared ;contextPrepared;contextLoaded ;failed 使用的是SpringBoot提供的SimpleApplicationEventMulticaster去发布事件。而对于;started ;failed; running ;会用到 ConfigurableApplicationContext容器对象去发布事件,ConfigurableApplicationContextApplicationEventPublisher Spring的事件发布器的实现类。

下面这几个是SpringBoot提供的事件对象,也是ApplicationEvent的子类,在上面的SpringBoot执行流程中也有看到

  1. ApplicationPreparedEvent : 当SpringApplication启动并且ApplicationContext已完全准备好但未刷新时发布的事件。 将加载 bean 定义,并且在此阶段Environment已准备好使用
  2. ApplicationStartingEvent : 在SpringApplication启动后尽可能早地发布事件 - 在Environment或ApplicationContext可用之前,但在ApplicationListener已注册之后
  3. ApplicationStartedEvent :刷新应用程序上下文但在调用任何application和command line运行程序之前发布的事件。
  4. ApplicationReadyEvent :发布该事件表明应用程序已准备好为请求提供服务
  5. ApplicationFailedEvent : SpringApplication启动失败时发布的事件
  6. ApplicationEnvironmentPreparedEvent:当SpringApplication启动并且Environment首次可供检查和修改时发布的事件。
  7. ApplicationContextInitializedEvent :当SpringApplication启动并准备ApplicationContext并且 ApplicationContextInitializers 已被调用但在加载任何 bean 定义之前发布的事件

我们来看一下 initialMulticaster 是怎么广播事件的,如: this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args)); ,见:org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
   ...省略...
	@Override
	public void multicastEvent(ApplicationEvent event) {
		multicastEvent(event, resolveDefaultEventType(event));
	}

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
        //这里是拿到监听了该event事件的Listenner,循环去调用监听器
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
                //调用监听器
				invokeListener(listener, event);
			}
		}
	}
    
    protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
        //错误处理器
		ErrorHandler errorHandler = getErrorHandler();
		if (errorHandler != null) {
			try {
                //调用监听器
				doInvokeListener(listener, event);
			}
			catch (Throwable err) {
                //错误处理
				errorHandler.handleError(err);
			}
		}
		else {
			doInvokeListener(listener, event);
		}
	}
    
    
    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
            //触发了 ApplicationListener#onApplicationEvent
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			...省略...
		}
	}


}

你应该看明白了 ,SimpleApplicationEventMulticaster 中先是根据某个事件类型比如 ApplicationStartingEvent ,获取到监听了该事件的ApplicationListener 监听类,循环去调用监听类即:调用ApplicationListener#onApplicationEvent方法去触发事件。

那么如果你的Listenner监听了ApplicationStartingEvent 该事件,你的onApplicationEvent方法就会被触发执行。

那我们再看一下ConfigurableApplicationContext#publishEvent是怎么发布事件的,如:context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));,见:org.springframework.context.support.AbstractApplicationContext#publishEvent

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
		Assert.notNull(event, "Event must not be null");

		// Decorate event as an ApplicationEvent if necessary
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		}
		else {
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
			}
		}

		// Multicast right now if possible - or lazily once the multicaster is initialized
		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		}
		else {
            //这里还是通过 Multicaster 去广播事件
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// Publish event via parent context as well...
		if (this.parent != null) {
			if (this.parent instanceof AbstractApplicationContext) {
				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
			}
			else {
				this.parent.publishEvent(event);
			}
		}
	}

我们看到 ConfigurableApplicationContext 其实掉用了AbstractApplicationContext#publishEvent方法去广播事件,底层还是使用了 org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent去广播事件

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    Executor executor = getTaskExecutor();
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            invokeListener(listener, event);
        }
    }
}

SpringBoot的事件就讲到这,那么我们学会了什么呢?至少你应该知道如果我要在SpirngBoot启动,或者启动失败做一些自己的业务,那你应该怎么做了。

7.printBanner打印横幅

横幅是SpringBoot启动时候在控制台打印的SpringBoot图案

我们可以在resources中创建banner.txt来自定义横幅内容 , 也可以通过 spring.main.banner-mode=off关闭打印

7.1.相关类介绍

在SpringBoot中提供了一个Banner接口专门用于以编程方式编写横幅的接口类。

@FunctionalInterface
public interface Banner {

	//打印横幅
	void printBanner(Environment environment, Class<?> sourceClass, PrintStream out);

	//模式
	enum Mode {
		OFF,
		CONSOLE,
		LOG

	}

}

我们来介绍一下这几个实现类

SpringBootBanner : SpringBoot默认的横幅打印实现类

class SpringBootBanner implements Banner {
	//默认要打印的横幅
	private static final String[] BANNER = { "", "  .   ____          _            __ _ _",
			" /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\", "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\",
			" \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )", "  '  |____| .__|_| |_|_| |_\\__, | / / / /",
			" =========|_|==============|___/=/_/_/_/" };

	private static final String SPRING_BOOT = " :: Spring Boot :: ";

	private static final int STRAP_LINE_SIZE = 42;

	@Override
	public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
        //打印横幅
		for (String line : BANNER) {
			printStream.println(line);
		}
        //拿到SpringBoot的版本号
		String version = SpringBootVersion.getVersion();
		version = (version != null) ? " (v" + version + ")" : "";
		StringBuilder padding = new StringBuilder();
		while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) {
			padding.append(" ");
		}
		//打印版本号
		printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(),
				AnsiStyle.FAINT, version));
        
		printStream.println();
	}

}

 ImageBanner : 用来打印图片横幅,我们只需要在resources下创建一个 banner.gif ,就可以被打印,也可以使用spring.banner.image.location 来指定横幅的位置。

spring:
  main:
    banner-mode: console #横幅模式,在控制台打印 off是关闭
  banner:
    image:
      location: classpath:banner.gif #图片横幅,默认也是banner.gif
      #height: 
      #width:

ImageBanner结构如下:

public class ImageBanner implements Banner {
    	private static final String PROPERTY_PREFIX = "spring.banner.image.";

	private static final Log logger = LogFactory.getLog(ImageBanner.class);

	private static final double[] RGB_WEIGHT = { 0.2126d, 0.7152d, 0.0722d };

	private final Resource image;
    
    //打印横幅
    private void printBanner(Environment environment, PrintStream out) throws IOException {
        //处理图片属性
		int width = getProperty(environment, "width", Integer.class, 76);
		int height = getProperty(environment, "height", Integer.class, 0);
		int margin = getProperty(environment, "margin", Integer.class, 2);
		boolean invert = getProperty(environment, "invert", Boolean.class, false);
		BitDepth bitDepth = getBitDepthProperty(environment);
		PixelMode pixelMode = getPixelModeProperty(environment);
		Frame[] frames = readFrames(width, height);
		for (int i = 0; i < frames.length; i++) {
			if (i > 0) {
				resetCursor(frames[i - 1].getImage(), out);
			}
            //打印
			printBanner(frames[i].getImage(), margin, invert, bitDepth, pixelMode, out);
			sleep(frames[i].getDelayTime());
		}
	}
}
7.2.横幅打印流程

横幅的打印是在SpringBoot启动流程中,调用 org.springframework.boot.SpringApplication#printBanner方法实现


private Banner printBanner(ConfigurableEnvironment environment) {
    //判断横幅功能是否被关闭,对应yaml中的 spring.main.banner-mode=off
    if (this.bannerMode == Banner.Mode.OFF) {
        return null;
    }
    //资源加载器,比如:用来加载banner.txt
    ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
        : new DefaultResourceLoader(getClassLoader());
    //创建 Banner打印器
    SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
    if (this.bannerMode == Mode.LOG) {
        //如果是log模式走这,对应:spring.main.banner-mode=log
        return bannerPrinter.print(environment, this.mainApplicationClass, logger);
    }
    //默认是console模式,走这
    return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

代码中会判断 this.bannerMode == Banner.Mode.OFF 时候开启日志打印(对应yaml中的配置),默认是Console,然后会创建一个ResourceLoader,主要是用来加载banner.txt或banner.gif 资源文件的。然后会判断如果this.bannerMode == Mode.LOG就使用logger打印,否则使用System.out打印,默认是System.out。打印横幅调用的是SpringApplicationBannerPrinter#print方法完成的。代码来到org.springframework.boot.SpringApplicationBannerPrinter#print

class SpringApplicationBannerPrinter {
	//banner.txt的地址
	static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
	//banner.gif的地址
	static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";
    //banner文件的名字
	static final String DEFAULT_BANNER_LOCATION = "banner.txt";
    //图片bannre的支持的后缀
	static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
    //默认的banner,使用SpringBootBanner
	private static final Banner DEFAULT_BANNER = new SpringBootBanner();

	private final ResourceLoader resourceLoader;

    
    Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
        //获取Banner
        Banner banner = getBanner(environment);
        //打印Banner
        banner.printBanner(environment, sourceClass, out);
        return new PrintedBanner(banner, sourceClass);
    }

    private Banner getBanner(Environment environment) {
        Banners banners = new Banners();
        //尝试获取image Banner,默认banner.gif,加入Banners
        banners.addIfNotNull(getImageBanner(environment));
        //尝试获取banner.txt ,加入Banners
        banners.addIfNotNull(getTextBanner(environment));
        if (banners.hasAtLeastOneBanner()) {
            //返回多个banners
            return banners;
        }
        if (this.fallbackBanner != null) {
            return this.fallbackBanner;
        }
        //如果没有指定banner.txt或者banner.gif,就会使用默认的SpringBootBanner
        return DEFAULT_BANNER;
    }

    private Banner getTextBanner(Environment environment) {
        //以:获取banner的location:spring.banner.location 
        String location = environment.getProperty(BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION);
        //加载 banner.txt
        Resource resource = this.resourceLoader.getResource(location);
        if (resource.exists()) {
            //返回banner
            return new ResourceBanner(resource);
        }
        //如果不存在banner.txt,那么就会使用默认的SpringBootBanner去打印
        return null;
    }

    private Banner getImageBanner(Environment environment) {
        //获取图片banner的地址:spring.banner.image.location
        String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY);
        if (StringUtils.hasLength(location)) {
            //加载图片,banner.gif
            Resource resource = this.resourceLoader.getResource(location);
            return resource.exists() ? new ImageBanner(resource) : null;
        }
        for (String ext : IMAGE_EXTENSION) {
            Resource resource = this.resourceLoader.getResource("banner." + ext);
            if (resource.exists()) {
                return new ImageBanner(resource);
            }
        }
        return null;
    }

SpringApplicationBannerPrinter会尝试获取spring.banner.image.location , 默认指向的resources/banner.gif 以及 spring.banner.location 指向的resources/banner.txt ,然后送resourceLoader加载资源文件,封装成 ImageBanner 或者 ResourceBanner。并交给Banners,Banners维护了Banner的列表。也就是说可以同时打印多个横幅,我们可以通过修改这2个路径来指定banner资源的位置。

如果没有banner.txt 和 banner.gif ,就会使用SpringBootBanner打印默认的banner.

紧接着就会调用Banners#printBanner方法循环执行每个Banner#printBanner方法。具体的Banner是如何打印出来的上面ImageBanner和SpringBootBanner已有介绍。

8.容器创建
8.1.容器介绍
SpringBoot底层是Spring,启动的过程中肯定要创建Spring容器,下面简单介绍一下IOC的容器工厂类

  • BeanFactory: Spring的容器顶层工厂,提供了获取Bean的基础方法,所有的IOC容器都是其子类
  • XmlWebApplicationContext : 整合web的容器对象,默认从’/WEB-INF/applicationContext.xml’加载配置
  • ClassPathXmlApplicationContext : 使用ResourcePatternResolver,默认从classpath加载xml配置的容器对象
  • AnnotationConfigApplicationContext :针对于注解的容器对象

8.2.容器的创建
创建容器是在SpringBoot启动流程中的 org.springframework.boot.SpringApplication#createApplicationContext方法

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    //默认走这,使用的是AnnotationConfigApplicationContext
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
            }
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
        }
    }
    //反射创建AnnotationConfigApplicationContext的实例
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

9.容器刷新
9.1.容器刷新

在SpringBoot启动流程中 有一句代码 SpringApplication#prepareContext, 这句代码在准备Spring容器,代码如下org.springframework.boot.SpringApplication#prepareContext

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    //把environment绑定到容器
    context.setEnvironment(environment);
    //容器的后置处理,里面也没干啥
    postProcessApplicationContext(context);
    //触发ApplicationContextInitializer#initialize的调用
    applyInitializers(context);
    //触发监听器,发布ApplicationContextInitializedEvent事件
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    //把applicationArguments注册Bean我们可以通过注入 ApplicationArguments 来获取程序启动参数
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        //把Bnaner注册为Bean
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
        //如果出现相同的Bean,是否允许覆盖,默认为false,
        //可用在yaml通过spring.main.allow-bean-definition-overriding: true来指定
        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    if (this.lazyInitialization) {
        //延迟初始化
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    // Load the sources
    //配置类资源
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    //load启动类,把启动类注册为Bean
    load(context, sources.toArray(new Object[0]));
    //抛出ApplicationPreparedEvent事件
    listeners.contextLoaded(context);
}
9.2.容器刷新

容器刷新在 org.springframework.boot.SpringApplication#refreshContext 方法中

private void refreshContext(ConfigurableApplicationContext context) {
    //刷新容器
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            //注册一个钩子,当容器被关闭,
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

上面代码做了两个事情,一个是调用refresh方法刷新容器,二个是向 JVM Runtime 注册一个名为SpringContextShutdownHook关闭钩子,在 JVM 关闭时关闭此上下文,委托容器的 doClose()执行实际的关闭过程。下面是context.registerShutdownHook();的内部代码,见:org.springframework.context.support.AbstractApplicationContext#registerShutdownHook

	@Override
	public void registerShutdownHook() {
		if (this.shutdownHook == null) {
			// No shutdown hook registered yet.
			this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
				@Override
				public void run() {
					synchronized (startupShutdownMonitor) {
						doClose();
					}
				}
			};
            //Runtime: 每个 Java 应用程序都有一个Runtime类的实例,它允许应用程序与运行应用程序的环境进行交互 
            //给Runtime 注册SpringContextShutdownHook 钩子
			Runtime.getRuntime().addShutdownHook(this.shutdownHook);
		}
	}



	protected void doClose() {
		// Check whether an actual close attempt is necessary...
		if (this.active.get() && this.closed.compareAndSet(false, true)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Closing " + this);
			}

			LiveBeansView.unregisterApplicationContext(this);

			try {
                //发布 ContextClosedEvent 容器关闭事件
				// Publish shutdown event.
				publishEvent(new ContextClosedEvent(this));
			}
			catch (Throwable ex) {
				logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
			}

			// Stop all Lifecycle beans, to avoid delays during individual destruction.
			if (this.lifecycleProcessor != null) {
				try {
                    //生命周期处理器,close触发
					this.lifecycleProcessor.onClose();
				}
				catch (Throwable ex) {
					logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
				}
			}

			// Destroy all cached singletons in the context's BeanFactory.
            //销毁所有的单利Bean
			destroyBeans();
			//close工厂
			// Close the state of this context itself.
			closeBeanFactory();
			//空实现,让子类做一些最后的清理...
			// Let subclasses do some final clean-up if they wish...
			onClose();

			// Reset local application listeners to pre-refresh state.
			if (this.earlyApplicationListeners != null) {
                //监听器清理
				this.applicationListeners.clear();
				this.applicationListeners.addAll(this.earlyApplicationListeners);
			}

			// Switch to inactive.
             //下文当前是否处于活动状态的标志修改为false
			this.active.set(false);
		}
	}

总结一下上面代码:就是给JVM的Runtime注册一个SpringContextShutdownHook钩子,当程序关闭,会触发AbstractApplicationContext#doClose方法,该方法中最了一些销毁和扫尾工作。

接下来就是刷新容器方法 refresh 分析了,代码最终来到 AbstractApplicationContext#refresh 方法中。

@Override
   public void refresh() throws BeansException, IllegalStateException {
   	synchronized (this.startupShutdownMonitor) {
   		// Prepare this context for refreshing.
   		//1.准备刷新工作 ,记录开始时间,初始化属性,校验配置文件,准备事件的存储Set
   		prepareRefresh();

   		// Tell the subclass to refresh the internal bean factory.
   		//告诉子类,刷新Bean工厂,销毁旧beanFactory,创建新beanFactory,默认DefaultListableBeanFactory
   		//从子容器的refreshBeanFactory方法中载入Bean的资源文件
        //2.加载配置,加载Bean,注册Bean就在这儿
   		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

   		// Prepare the bean factory for use in this context.
   		//3.准备工厂,配置工厂的下文特性, 例如上下文的 ClassLoader 和后处理器。Bean表达式解析器,
   		//BeanPostProcessor和 Aware类的自动装配等
   		prepareBeanFactory(beanFactory);

   		try {
   			// Allows post-processing of the bean factory in context subclasses.
   			//4.BeanFactory初始化完成的后置工作,
            //这是一个空方法,留给三方框架或者自己配置,作用是允许对beanFoctory进行扩展处理
   			postProcessBeanFactory(beanFactory);

   			// Invoke factory processors registered as beans in the context.
   			//5.调用BeanFactory的后置处理器BeanFactoryPostProcessor,在 bean定义注册之后bean实例化之前调用
   			invokeBeanFactoryPostProcessors(beanFactory);

   			// Register bean processors that intercept bean creation.
   			//6.注册Bean的后置处理器BeanPostProcessor,在Bean初始化前,后执行
   			registerBeanPostProcessors(beanFactory);

   			// Initialize message source for this context.
   			//7.初始化信息源,国际化相关
   			initMessageSource();

   			// Initialize event multicaster for this context.
   			//8.初始化容器事件传播器,比如:SimpleApplicationEventMulticaster
   			initApplicationEventMulticaster();

   			// Initialize other special beans in specific context subclasses.
   			//9.空方法,该方法子类实现,在容器刷新的时候可以自定义逻辑;如创建Tomcat,Jetty等WEB服务器
   			onRefresh();

   			// Check for listener beans and register them.
   			//10.注册事件监听器,注册实现了ApplicationListener接口的监听器bean,
   			//这些监听器是注册到ApplicationEventMulticaster中的
   			registerListeners();

   			// Instantiate all remaining (non-lazy-init) singletons.
   			//11.实例化所有剩余的(非延迟初始化)单例的Bean
             //也就是创建Bean的真正实例就在该方法中
   			finishBeanFactoryInitialization(beanFactory);

   			// Last step: publish corresponding event.
   			//12.完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,并且发布事件(ContextRefreshedEvent)
   			finishRefresh();
   		}

   		catch (BeansException ex) {
   			if (logger.isWarnEnabled()) {
   				logger.warn("Exception encountered during context initialization - " +
   						"cancelling refresh attempt: " + ex);
   			}
   			// Destroy already created singletons to avoid dangling resources.
   			//销毁已经创建的单例Bean。
   			destroyBeans();
   			// Reset 'active' flag.
   			//取消容器刷新
   			cancelRefresh(ex);
   			// Propagate exception to caller.
   			throw ex;
   		}
   		finally {
   			// Reset common introspection caches in Spring's core, since we
   			// might not ever need metadata for singleton beans anymore...
   			//重置缓存
   			resetCommonCaches();
   		}
   	}
   }

这里其实已经在调用Spirng的容器刷新流程了,这里大概说明一下,具体的详细流程见:

  1. 在obtainFreshBeanFactory方法中回去解析配置文件,然后把Bean封装成BeanDefinition,注册到IOC容器中(一个CurrentHashMap)
  2. 在invokeBeanFactoryPostProcessors方法中会触发BeanFactory的后置处理器的调用,这个很有用,可以在BeanFactory初始化过程中做扩展
  3. registerBeanPostProcessors 方法中注册了BeanPostProcessors后置处理器,这个很有用,可以在Bean实例化前后,做一些扩展业务,比如:@Transactionl ,@Autowired的实现原理就是基于Bean的后置处理器
  4. finishBeanFactoryInitialization :这个方法中会对单利的Bean做正在的实例化,然后缓存到Spring的IOC容器中,以后获取Bean的时候会从缓存中取。这保证了Bean的单例。

10.callRunners
10.1.认识ApplicationRunner 和 CommandLineRunner

SpringBoot启动完成之后的最后一步就是org.springframework.boot.SpringApplication#callRunners,该方法中回去执行所有的ApplicationRunner , 和 CommandLineRunner 。

private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<>();
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		AnnotationAwareOrderComparator.sort(runners);
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

ApplicationRunner CommandLineRunner 可以看做是SpringBoot的扩展,两个接口的功能是一样的,通过实现该接口可以在SpringBoot启动成功之后做一些初始化工作。

这两个Runner的不同之处在于CommandLineRunner#run方法接收的是main方法传递过来的直接参数args,而ApplicationRunner#run方法接收的是封装了args的ApplicationArguments对象。我们来演示其中一个

10.2.使用ApplicationRunner

第一步:定义一个Runner类

@Component
@Slf4j
public class ApplicationRunnerBean implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("ApplicationRunnerBean.run SpringBoot启动完成,做一些初始化...");
    }
}

第二步:启动测试

结束啦,如果文章对你有所帮助,请一定给个好评哦~ 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值