栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

springboot热部署原理

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

springboot热部署原理

热部署核心:重建类加载器

直接调appclassLoader加载,仅仅修改类的话,已被加载的类不会重新加载所以需要自定义类加载器,要想重新加载需要重构类加载器。

1.加载过程

启动类SpringApplication

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new linkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //获取所有的监听器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}
    //加载SpringApplicationRunListeners
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    //获取监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    //触发监听  
    listeners.starting();
    ....
}

EventPublishingRunListener监听器发布事件

public void starting() {
    this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
    
//this.defaultRetriever.applicationListeners变量
//EventPublishingRunListener  构造器
public EventPublishingRunListener(SpringApplication application, String[] args) {
    this.application = application;
    this.args = args;
    this.initialMulticaster = new SimpleApplicationEventMulticaster();
    //从SpringApplication的全局变量listeners中获取监听器
    for (ApplicationListener listener : application.getListeners()) {
        this.initialMulticaster.addApplicationListener(listener);
    }
}
//监听器存入this.defaultRetriever.applicationListeners
public void addApplicationListener(ApplicationListener listener) {
    synchronized (this.retrievalMutex) {
        // Explicitly remove target for a proxy, if registered already,
        // in order to avoid double invocations of the same listener.
        Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
        if (singletonTarget instanceof ApplicationListener) {
            this.defaultRetriever.applicationListeners.remove(singletonTarget);
        }
        this.defaultRetriever.applicationListeners.add(listener);
        this.retrieverCache.clear();
    }
}

SimpleApplicationEventMulticaster多播器

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);
        }
    }
}
//监听调用
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);
    }
}

调用监听器RestartApplicationListener

private void onApplicationStartingEvent(ApplicationStartingEvent event) {
    String enabled = System.getProperty(ENABLED_PROPERTY);//spring.devtools.restart.enabled
    if (enabled == null || Boolean.parseBoolean(enabled)) {//通过该变量控制是否热部署
        String[] args = event.getArgs();
        DefaultRestartInitializer initializer = new DefaultRestartInitializer();
        boolean restartonInitialize = !AgentReloader.isActive();
        if (!restartOnInitialize) {
            logger.info("Restart disabled due to an agent-based reloader being active");
        }
        Restarter.initialize(args, false, initializer, restartOnInitialize);
    }
    else {
        logger.info(LogMessage.format("Restart disabled due to System property '%s' being set to false",
                                      ENABLED_PROPERTY));
        Restarter.disable();
    }
}

Restarter重启类

public static void initialize(String[] args, boolean forceReferenceCleanup, RestartInitializer initializer,
            boolean restartOnInitialize) {
    Restarter localInstance = null;
    synchronized (INSTANCE_MONITOR) {
        if (instance == null) {
            localInstance = new Restarter(Thread.currentThread(), args, forceReferenceCleanup, initializer);
            instance = localInstance;
        }
    }
    if (localInstance != null) {
        localInstance.initialize(restartOnInitialize);
    }
}
protected void initialize(boolean restartOnInitialize) {
    preInitializeLeakyClasses();
    if (this.initialUrls != null) {
        this.urls.addAll(Arrays.asList(this.initialUrls));
        if (restartOnInitialize) {
            this.logger.debug("Immediately restarting application");
            immediateRestart();
        }
    }
}
private void immediateRestart() {
    try {
        getLeakSafeThread().callAndWait(() -> {
            start(FailureHandler.NONE);//异步线程
            cleanupCaches();
            return null;
        });
    }
    catch (Exception ex) {
        this.logger.warn("Unable to initialize restarter", ex);
    }
    SilentExitExceptionHandler.exitCurrentThread();//主线程抛异常退出
}
protected void start(FailureHandler failureHandler) throws Exception {
    do {
        Throwable error = doStart();
        if (error == null) {
            return;
        }
        if (failureHandler.handle(error) == Outcome.ABORT) {
            return;
        }
    }
    while (true);
}
private Throwable doStart() throws Exception {
    Assert.notNull(this.mainClassName, "Unable to find the main class to restart");
    URL[] urls = this.urls.toArray(new URL[0]);
    ClassLoaderFiles updatedFiles = new ClassLoaderFiles(this.classLoaderFiles);
    ClassLoader classLoader = new RestartClassLoader(this.applicationClassLoader, urls, updatedFiles, this.logger);//新建类加载器new RestartClassLoader
    if (this.logger.isDebugEnabled()) {
        this.logger.debug("Starting application " + this.mainClassName + " with URLs " + Arrays.asList(urls));
    }
    return relaunch(classLoader);//重新运行程序relaunch,传入RestartClassLoader
}
protected Throwable relaunch(ClassLoader classLoader) throws Exception {
    RestartLauncher launcher = new RestartLauncher(classLoader, this.mainClassName, this.args,
                                                   this.exceptionHandler);
    launcher.start();
    launcher.join();
    return launcher.getError();
}

RestartLauncher

RestartLauncher(ClassLoader classLoader, String mainClassName, String[] args,
            UncaughtExceptionHandler exceptionHandler) {
    this.mainClassName = mainClassName;
    this.args = args;
    setName("restartedMain");
    setUncaughtExceptionHandler(exceptionHandler);
    setDaemon(false);
    setContextClassLoader(classLoader);//设置类加载器
}
//重新运行主程序   SpringApplication.run
@Override
public void run() {
    try {
        Class mainClass = Class.forName(this.mainClassName, false, getContextClassLoader());
        Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
        mainMethod.invoke(null, new Object[] { this.args });
    }
    catch (Throwable ex) {
        this.error = ex;
        getUncaughtExceptionHandler().uncaughtException(this, ex);
    }
}

疑问:再次调用SpringApplication.run后,又得调用RestartApplicationListener的onApplicationStartingEvent,不是造成死循环了吗?

答:其实第二次调用会跳出onApplicationStartingEvent的后续方法,执行到RestartApplicationListener的onApplicationStartingEvent时候

不会再走到Restarter.initialize方法去,故不会造成循环,具体跟源码看。

文件监听实现热部署

文件监听自动配置类LocalDevToolsAutoConfiguration

spring.factories文件中
//重启触发策略ClassPathRestartStrategy
@Bean
@ConditionalOnMissingBean
ClassPathFileSystemWatcher classPathFileSystemWatcher(FileSystemWatcherFactory fileSystemWatcherFactory,
                                                      ClassPathRestartStrategy classPathRestartStrategy) {
    URL[] urls = Restarter.getInstance().getInitialUrls();//监听的路径url
    //真正监听的实体类FileSystemWatcher
    ClassPathFileSystemWatcher watcher = new ClassPathFileSystemWatcher(fileSystemWatcherFactory,
                                                                        classPathRestartStrategy, urls);
    watcher.setStopWatcheronRestart(true);
    return watcher;
}
//监听ClassPathChangedEvent事件,触发Restarter重启应用
@Bean
ApplicationListener restartingClassPathChangedEventListener(
    FileSystemWatcherFactory fileSystemWatcherFactory) {
    return (event) -> {
        if (event.isRestartRequired()) {
            //调用Restarter
            Restarter.getInstance().restart(new FileWatchingFailureHandler(fileSystemWatcherFactory));
        }
    };
}

ClassPathFileSystemWatcher

//实现了InitializingBean
@Override
public void afterPropertiesSet() throws Exception {
    if (this.restartStrategy != null) {
        FileSystemWatcher watcherToStop = null;
        if (this.stopWatcherOnRestart) {
            watcherToStop = this.fileSystemWatcher;
        }
        //加入ClassPathFileChangeListener对象
        this.fileSystemWatcher.addListener(
            new ClassPathFileChangeListener(this.applicationContext, this.restartStrategy, watcherToStop));
    }
    this.fileSystemWatcher.start();
}

FileSystemWatcher

public void run() {
   int remainingScans = this.remainingScans.get();
   while (remainingScans > 0 || remainingScans == -1) {
      try {
         if (remainingScans > 0) {
            this.remainingScans.decrementAndGet();
         }
         scan();//扫描对比文件快照和文件当前信息,查看是否有变化,有变化则触发监听
      }
      catch (InterruptedException ex) {
         Thread.currentThread().interrupt();
      }
      remainingScans = this.remainingScans.get();
   }
}
private void scan() throws InterruptedException {
    Thread.sleep(this.pollInterval - this.quietPeriod);
    Map previous;
    Map current = this.directories;
    do {
        previous = current;
        current = getCurrentSnapshots();//获取文件当前信息
        Thread.sleep(this.quietPeriod);//休止期,默认400毫秒
    }
    while (isDifferent(previous, current));//对比文件,有差异则退出循环
    if (isDifferent(this.directories, current)) {//休止期内,如果文件再改回去,相当于文件无变化
        updateSnapshots(current.values());
    }
}
private Map getCurrentSnapshots() {
    Map snapshots = new linkedHashMap<>();
    for (File directory : this.directories.keySet()) {
        //获取文件当前信息封装到DirectorySnapshot
        snapshots.put(directory, new DirectorySnapshot(directory));
    }
    return snapshots;
}
private void updateSnapshots(Collection snapshots) {
    Map updated = new linkedHashMap<>();
    Set changeSet = new linkedHashSet<>();
    for (DirectorySnapshot snapshot : snapshots) {
        DirectorySnapshot previous = this.directories.get(snapshot.getDirectory());
        updated.put(snapshot.getDirectory(), snapshot);
        ChangedFiles changedFiles = previous.getChangedFiles(snapshot, this.triggerFilter);
        if (!changedFiles.getFiles().isEmpty()) {
            changeSet.add(changedFiles);
        }
    }
    if (!changeSet.isEmpty()) {
        fireListeners(Collections.unmodifiableSet(changeSet));//发布监听事件
    }
    this.directories = updated;
}
//发布监听事件   ClassPathFileChangeListener
private void fireListeners(Set changeSet) {
    for (FileChangeListener listener : this.listeners) {
        listener.onChange(changeSet);
    }
}

DirectorySnapshot判断文件是否发生变化的封装类

DirectorySnapshot(File directory) {
    Assert.notNull(directory, "Directory must not be null");
    Assert.isTrue(!directory.isFile(), () -> "Directory '" + directory + "' must not be a file");
    this.directory = directory;
    this.time = new Date();
    Set files = new linkedHashSet<>();
    collectFiles(directory, files);//收集文件快照
    this.files = Collections.unmodifiableSet(files);//存储file文件,该快照集合不可修改
}
private void collectFiles(File source, Set result) {
    File[] children = source.listFiles();
    if (children != null) {
        for (File child : children) {
            if (child.isDirectory() && !DOTS.contains(child.getName())) {
                collectFiles(child, result);//递归处理
            }
            else if (child.isFile()) {
                result.add(new FileSnapshot(child));//存储文件快照的对象
            }
        }
    }
}
//存储文件长度、最后修改时间    该类已重写equals方法
FileSnapshot(File file) {
    Assert.notNull(file, "File must not be null");
    Assert.isTrue(file.isFile() || !file.exists(), "File must not be a directory");
    this.file = file;
    this.exists = file.exists();
    this.length = file.length();
    this.lastModified = file.lastModified();
}

ClassPathFileChangeListener,实现热部署

@Override
public void onChange(Set changeSet) {
    boolean restart = isRestartRequired(changeSet);
    publishEvent(new ClassPathChangedEvent(this, changeSet, restart));
}
private void publishEvent(ClassPathChangedEvent event) {
    this.eventPublisher.publishEvent(event);//发布事件   ClassPathChangedEvent
    if (event.isRestartRequired() && this.fileSystemWatcherToStop != null) {
        this.fileSystemWatcherToStop.stop();
    }
}
热部署原理图

https://www.processon.com/view/link/621cdfb8f346fb5010961fc2

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/750274.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号