写在前面:在调试代码的时候,遇到小的细节都会记录,由于技术水平不高,请多留言指正。
- 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#starting
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 4.创建一个defaultApplicationArguments对象,他持有args参数(main函数中传入),调用prepareEnvironment方法
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 5.打印banner
Banner printedBanner = printBanner(environment);
// 6.创建springboot上下文
context = createApplicationContext();
// 7.初始化FailureAnalyzers
analyzers = new FailureAnalyzers(context);
// 8.调用prepareContext
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 9.调用AbstractApplicationContext#refresh
refreshContext(context);
// 10.在容器完成刷新后,依次调用注册的Runners
afterRefresh(context, applicationArguments);
// 11.调用SpringApplicationRunListeners#finished
listeners.finished(context, null);
// 12.停止计时
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
return context;
} catch (Throwable ex) {
// 13.初始化过程中出现异常时调用handelRunFailure进行处理,抛出IllegalStateException
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
- 1 github本地上传
- 2 github工具:左侧的目录树 Tree
第一步:初始化stopWatcch(计数器),调用其start方法开始计时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
/**
* 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) {//默认为true
this.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) {//默认为true
this.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关键字