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会提示错误

在这里插入图片描述

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);
	}
}

以上代码及注释会在github


第一步:初始化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) {//默认为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);
		}
	}
  1. final变量的初始化:
    若为对象变量(非静态)可在定义时赋值,或者在构造函数中赋值(上述代码中就是构造函数中赋值;若为类变量(静态)则可在定义时或静态代码块中赋值)推荐博文 final变量的初始化方式
  2. 类的命名规则:
    本类为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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot项目中,可以使用初始化代码来实现项目启动时的一些初始化操作。下面以示例代码进行解释: 1. 创建一个类,命名为ApplicationRunnerImpl实现ApplicationRunner接口,该接口继承了CommandLineRunner接口,用于在Spring Boot项目启动之后执行特定的代码。 ```java import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; @Component public class ApplicationRunnerImpl implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { // 这里写入初始化代码,例如加载配置、数据库连接等操作 System.out.println("Spring Boot项目启动初始化代码执行!"); } } ``` 2. 在启动类中,使用@SpringBootApplication注解启动Spring Boot应用程序。 ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ``` 在上述示例代码中,当Spring Boot项目启动之后,ApplicationRunnerImpl类中的run方法会被执行,可以在此方法中编写一些初始化的代码。比如加载配置文件、初始化数据库连接等。上面的例子中,run方法内只打印了一条信息。实际应用中,可以根据需要编写具体的初始化逻辑。 当代码执行时,控制台会打印出"Spring Boot项目启动初始化代码执行!"的信息,表示初始化代码成功执行。 这种方式非常适用于需要在项目启动时执行一些初始化操作的场景,可以方便地集成到Spring Boot框架中,实现项目的自动初始化

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值