(1). 添加依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-devtools</artifactId>
	<optional>true</optional>
	<scope>runtime</scope>
</dependency>

(2). 查看Spring的入口(spring.factories)

spring-boot-devtools-2.1.0.RELEASE.jar/META-INF/spring.factories

# Application Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.devtools.restart.RestartScopeInitializer

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.devtools.restart.RestartApplicationListener,\
org.springframework.boot.devtools.logger.DevToolsLogFactory.Listener

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.devtools.autoconfigure.DevToolsDataSourceAutoConfiguration,\
org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration,\
org.springframework.boot.devtools.autoconfigure.RemoteDevToolsAutoConfiguration

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.devtools.env.DevToolsHomePropertiesPostProcessor,\
org.springframework.boot.devtools.env.DevToolsPropertyDefaultsPostProcessor

(3). LocalDevToolsAutoConfiguration$RestartConfiguration

@Configuration
@ConditionalOnInitializedRestarter
@EnableConfigurationProperties(DevToolsProperties.class)
public class LocalDevToolsAutoConfiguration {
    
	// ****************************************************************
	// 热部署配置(RestartConfiguration)
	// ****************************************************************
	@Configuration
  	@ConditionalOnProperty(prefix = "spring.devtools.restart", name = "enabled", matchIfMissing = true)
  	static class RestartConfiguration
  			implements ApplicationListener<ClassPathChangedEvent> {
  
  		private final DevToolsProperties properties;
  
        // 1. 注入配置文件
  		RestartConfiguration(DevToolsProperties properties) {
  			this.properties = properties;
  		}
  
  		@Override
  		public void onApplicationEvent(ClassPathChangedEvent event) {
  			if (event.isRestartRequired()) {
  				Restarter.getInstance().restart(
  						new FileWatchingFailureHandler(fileSystemWatcherFactory()));
  			}
  		}
  
         // 2. 配置文件监听
  		@Bean
  		@ConditionalOnMissingBean
  		public ClassPathFileSystemWatcher classPathFileSystemWatcher() {
			// urls = [file:/Users/lixin/Workspace/spring-web-demo/target/classes/]
  			URL[] urls = Restarter.getInstance().getInitialUrls();
			
			// *****************************************************************
			// 从现在情况来看:ClassPathFileSystemWatcher应该是重点了,因为:它聚合着监听和重启策略
			// *****************************************************************
  			ClassPathFileSystemWatcher watcher = new ClassPathFileSystemWatcher(
			        // 3. 创建文件系统监听工厂
  					fileSystemWatcherFactory(), 
					// 4. 创建重启策略
					classPathRestartStrategy(), 
					urls);
  			watcher.setStopWatcherOnRestart(true);
  			return watcher;
  		}
  
        // 4.1 创建重启策略
  		@Bean
  		@ConditionalOnMissingBean
  		public ClassPathRestartStrategy classPathRestartStrategy() {
			// 设置哪些文件排除不在重启的范围内
  			return new PatternClassPathRestartStrategy(this.properties.getRestart().getAllExclude());
  		}
  
  		@Bean
  		public HateoasObjenesisCacheDisabler hateoasObjenesisCacheDisabler() {
  			return new HateoasObjenesisCacheDisabler();
  		}
  
        // 3.1 创建文件系统监听工厂
  		@Bean
  		public FileSystemWatcherFactory fileSystemWatcherFactory() {
  			return this::newFileSystemWatcher;
  		}
  
  		@Bean
  		@ConditionalOnProperty(prefix = "spring.devtools.restart", name = "log-condition-evaluation-delta", matchIfMissing = true)
  		public ConditionEvaluationDeltaLoggingListener conditionEvaluationDeltaLoggingListener() {
  			return new ConditionEvaluationDeltaLoggingListener();
  		}
  
        // 3.2 创建文件系统监听工厂
  		private FileSystemWatcher newFileSystemWatcher() {
			// 获得重启部份的属性配置
  			Restart restartProperties = this.properties.getRestart();
			
			// 文件系统监听(间隔/频率)
  			FileSystemWatcher watcher = new FileSystemWatcher(true,
  					restartProperties.getPollInterval(),
  					restartProperties.getQuietPeriod());
  			
			// 触发文件
			String triggerFile = restartProperties.getTriggerFile();
  			if (StringUtils.hasLength(triggerFile)) {
  				watcher.setTriggerFilter(new TriggerFileFilter(triggerFile));
  			}
			
			// 监听的目录
  			List<File> additionalPaths = restartProperties.getAdditionalPaths();
  			for (File path : additionalPaths) {
  				watcher.addSourceFolder(path.getAbsoluteFile());
  			}
  			return watcher;
  		}
  
  	}// end 
}

(4). ClassPathFileSystemWatcher

public class ClassPathFileSystemWatcher
		implements InitializingBean, DisposableBean, ApplicationContextAware {

	private final FileSystemWatcher fileSystemWatcher;

	private ClassPathRestartStrategy restartStrategy;

	private ApplicationContext applicationContext;

	private boolean stopWatcherOnRestart;

	// 1. 监听工厂/重启策略/监听URL
	public ClassPathFileSystemWatcher(FileSystemWatcherFactory fileSystemWatcherFactory,
			ClassPathRestartStrategy restartStrategy, URL[] urls) {
		Assert.notNull(fileSystemWatcherFactory,
				"FileSystemWatcherFactory must not be null");
		Assert.notNull(urls, "Urls must not be null");
		this.fileSystemWatcher = fileSystemWatcherFactory.getFileSystemWatcher();
		this.restartStrategy = restartStrategy;
		this.fileSystemWatcher.addSourceFolders(new ClassPathFolders(urls));
	}

	/**
	 * Set if the {@link FileSystemWatcher} should be stopped when a full restart occurs.
	 * @param stopWatcherOnRestart if the watcher should be stopped when a restart occurs
	 */
	public void setStopWatcherOnRestart(boolean stopWatcherOnRestart) {
		this.stopWatcherOnRestart = stopWatcherOnRestart;
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.applicationContext = applicationContext;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		if (this.restartStrategy != null) {
			FileSystemWatcher watcherToStop = null;
			if (this.stopWatcherOnRestart) {
				watcherToStop = this.fileSystemWatcher;
			}
			
			// ****************************************************************
			// 为FileSystemWatcher类,配置监听器
			// ****************************************************************
			this.fileSystemWatcher.addListener(new ClassPathFileChangeListener(
					this.applicationContext, this.restartStrategy, watcherToStop));
		}
		
		// *******************************************************************
		// 为FileSystemWatcher类,启动监听
		// *******************************************************************
		this.fileSystemWatcher.start();
	}

	// 停止监听
	@Override
	public void destroy() throws Exception {
		this.fileSystemWatcher.stop();
	}
}

(5). 总结

通过FileSystemWatcher类监听目录变化(监听的目录,仅为当前开发的工程目录,所以,class文件相对比较少),当目录发生变化时,触发ClassLoader的重新加载.