本文主要是介绍SpringApplication 初始化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
写在前面:在调试代码的时候,遇到小的细节都会记录,由于技术水平不高,请多留言指正。
- Step Into 指跳入,Step Over 指下一步
复杂情况一般指定行数:Step Into(11)跳入第11行,Step Over (33-35)从33行走到35行。- // 中为手动添加注释,纯英文的为自带注释
注:一般为此代码中的疑难点 重点解释
SpringApplication初始化
一、 @SpringBootApplication
- 开启debug模式
- Step into
* @param source【当前的启动类的class】 the source to load* @param args 【main方法对应的参数,此时为空数组】the application arguments (usually passed from a Java main method)public static ConfigurableApplicationContext run(Object source, String... args) {return run(new Object[]{source}, args);}
- Step Into
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {//【初始化SpringApplication,调用其run方法,这里是以构造函数的方式,初始化了其他的参数】return new SpringApplication(sources).run(args);}
注:对final的理解:new SpringApplication(source):通过构造函数的方式传了一个source参数给SpringApplilcation:
// 这里的source时final变量 定义如下private final Set<Object> sources = new LinkedHashSet<Object>();
-->
public SpringApplication(Object... sources) {initialize(sources);}private void initialize(Object[] sources) {if (sources != null && sources.length > 0) {this.sources.addAll(Arrays.asList(sources));}}final的值类型不可变,引用类型,引用的指向不可变,但被指向的对象可变这里final指向new LinkedHashSet,在【this.sources】拿到地址的时候,将对象赋值了。但不可以 进行:source==xxx(xxx为其他对象,这种操作)
ide会提示错误
- Step Into
SpringApplication.java
public ConfigurableApplicationContext run(String... args) {// 1.初始化stopWatcch(计数器),调用其start方法开始计时StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;FailureAnalyzers analyzers = null;// 2.设置系统属性java.awt.headless,这里设置为true,表示运行在服务器端,在没有显示器和鼠标键盘的模式下工作,模拟输入输出设备功能configureHeadlessProperty();// 3.调用springApplicationRunListeners#startingSpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {// 4.创建一个defaultApplicationArguments对象,他持有args参数(main函数中传入),调用prepareEnvironment方法ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);// 5.打印bannerBanner printedBanner = printBanner(environment);// 6.创建springboot上下文context = createApplicationContext();// 7.初始化FailureAnalyzersanalyzers = new FailureAnalyzers(context);// 8.调用prepareContextprepareContext(context, environment, listeners, applicationArguments, printedBanner);// 9.调用AbstractApplicationContext#refreshrefreshContext(context);// 10.在容器完成刷新后,依次调用注册的RunnersafterRefresh(context, applicationArguments);// 11.调用SpringApplicationRunListeners#finishedlisteners.finished(context, null);// 12.停止计时stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}return context;} catch (Throwable ex) {// 13.初始化过程中出现异常时调用handelRunFailure进行处理,抛出IllegalStateExceptionhandleRunFailure(context, listeners, analyzers, ex);throw new IllegalStateException(ex);}
}
注:以上代码及注释会在github
- 1 github本地上传
- 2 github工具:左侧的目录树 Tree
第一步:初始化stopWatcch(计数器),调用其start方法开始计时
StopWatch stopWatch = new StopWatch();stopWatch.start();
StopWatch.class
/*** Construct a new stop watch with the given id.* Does not start any task.* @param id identifier for this stop watch.* Handy when we have output from multiple stop watches* and need to distinguish between them.*/public StopWatch(String id) {this.id = id;}/*** Return the id of this stop watch, as specified on construction.* @return the id (empty String by default)* @since 4.2.2* @see #StopWatch(String)*/public String getId() {return this.id;}/*** Determine whether the TaskInfo array is built over time. Set this to* "false" when using a StopWatch for millions of intervals, or the task* info structure will consume excessive memory. Default is "true".*/public void setKeepTaskList(boolean keepTaskList) {this.keepTaskList = keepTaskList;}/*** Start an unnamed task. The results are undefined if {@link #stop()}* or timing methods are called without invoking this method.* @see #stop()*/public void start() throws IllegalStateException {start("");}/*** Start a named task. The results are undefined if {@link #stop()}* or timing methods are called without invoking this method.* @param taskName the name of the task to start* @see #stop()*/public void start(String taskName) throws IllegalStateException {if (this.running) {throw new IllegalStateException("Can't start StopWatch: it's already running");}this.running = true;this.currentTaskName = taskName;this.startTimeMillis = System.currentTimeMillis();}/*** Stop the current task. The results are undefined if timing* methods are called without invoking at least one pair* {@code start()} / {@code stop()} methods.* @see #start()*/public void stop() throws IllegalStateException {if (!this.running) {throw new IllegalStateException("Can't stop StopWatch: it's not running");}long lastTime = System.currentTimeMillis() - this.startTimeMillis;this.totalTimeMillis += lastTime;this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);if (this.keepTaskList) {//默认为truethis.taskList.add(lastTaskInfo);}++this.taskCount;this.running = false;this.currentTaskName = null;}
注:用哪种方式表示当前时间:
方式一:this.startTimeMillis = System.currentTimeMillis();
方式二:this.startTimeMills = new Date().getTime()
推荐博文:System.currentTimeMillis()和new Date().getTime()区别
// 下面是第二种方式的实现源码:public Date() {this(System.currentTimeMillis());}public Date(long date) {fastTime = date;}public long getTime() {return getTimeImpl();}private final long getTimeImpl() {if (cdate != null && !cdate.isNormalized()) {normalize();}return fastTime;}
可见:第二种方式内部也是采用System.currentTimeMills()方式。建议使用第一种方式(同源码)
第十三步:此方法的stop()
此行代码:
this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);if (this.keepTaskList) {//默认为truethis.taskList.add(lastTaskInfo);}
注:TaskInfo:
public static final class TaskInfo {private final String taskName;private final long timeMillis;TaskInfo(String taskName, long timeMillis) {this.taskName = taskName;this.timeMillis = timeMillis;}/*** Return the name of this task.*/public String getTaskName() {return this.taskName;}/*** Return the time in milliseconds this task took.*/public long getTimeMillis() {return this.timeMillis;}/*** Return the time in seconds this task took.*/public double getTimeSeconds() {return (this.timeMillis / 1000.0);}}
- final变量的初始化:
若为对象变量(非静态)可在定义时赋值,或者在构造函数中赋值(上述代码中就是构造函数中赋值;若为类变量(静态)则可在定义时或静态代码块中赋值)推荐博文 final变量的初始化方式 - 类的命名规则:
本类为StopWatch.class的静态内部类
所有内部类会在编译的时候产生相对应的class文件,非匿名内部类类名规则为 OutClass$InnerClass (外部类类名与内部类类名中间用 $ 连接) 匿名内部类类名则为OutClass$数字(OutClass$1,OutClass$2,OutClass$3)—摘录自匿名内部类类名规则($1,$2)
如图:ide中的类名
- 单例模式下的静态内部类
public class StaticInnerClassMode {private StaticInnerClassMode(){};public static StaticInnerClassMode getInstance(){return InnerClass.staticInnerClassMode;}private static class InnerClass {private static StaticInnerClassMode staticInnerClassMode =new StaticInnerClassMode();}public static void main(String[] args) {System.err.println(StaticInnerClassMode.getInstance());// System.err.println(StaticInnerClassMode.getInstance());}
}
推荐博文:java静态内部类以及单例应用
第二步:设置系统属性java.awt.headless
这里设置为true,表示运行在服务器端,在没有显示器和鼠标键盘的模式下工作,模拟输入输出设备功能 configureHeadlessProperty();
-
step into
这里设置了系统参数:
private void configureHeadlessProperty() {System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));}
- step into #setProperty
public static String setProperty(String key, String value) {checkKey(key);SecurityManager sm = getSecurityManager();if (sm != null) {sm.checkPermission(new PropertyPermission(key,SecurityConstants.PROPERTY_WRITE_ACTION));}return (String) props.setProperty(key, value);}
这里是 setProperty方法的实现,我们来看下
SecurityManager sm = getSecurityManager();这句如何返回一个Security对象
- step into #getSecurityManager();
private static volatile SecurityManager security = null;public static SecurityManager getSecurityManager() {return security;}
注:volatile关键字在这里出现,单例模式中有个双重校验if的写法,但是并不能完全保证线程安全,加上volatile 关键字可以有效避免(指令重排) 推荐博文:单例模式为什么要用Volatile关键字
第三步:设置系统属性java.awt.headless
这篇关于SpringApplication 初始化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!