SpringBoot的reload加载器的方法

背景

springboot越来越多的被大家所使用SpringBoot DevTool实现热部署

出现了相同类castException

分析

首先确定出现相同类的castException比如是由于classloader不同造成的。

一个class是否相同取决于两个因素

  1. classloader相同
  2. class文件相同

即不同classloader解释出来的class是不同的class

我们在学习jdbc的时候常见的使用

/**
 * Returns the {@code Class} object associated with the class or
 * interface with the given string name. Invoking this method is
 * equivalent to:
 *
 * <blockquote>
 * {@code Class.forName(className, true, currentLoader)}
 * </blockquote>
 *
 * where {@code currentLoader} denotes the defining class loader of
 * the current class.
 *
 * <p> For example, the following code fragment returns the
 * runtime {@code Class} descriptor for the class named
 * {@code java.lang.Thread}:
 *
 * <blockquote>
 *  {@code Class t = Class.forName("java.lang.Thread")}
 * </blockquote>
 * <p>
 * A call to {@code forName("X")} causes the class named
 * {@code X} to be initialized.
 *
 * @param   className  the fully qualified name of the desired class.
 * @return   the {@code Class} object for the class with the
 *       specified name.
 * @exception LinkageError if the linkage fails
 * @exception ExceptionInInitializerError if the initialization provoked
 *      by this method fails
 * @exception ClassNotFoundException if the class cannot be located
 */
public static Class<?> forName(String className)
      throws ClassNotFoundException {
  return forName0(className, true, ClassLoader.getCallerClassLoader());
}

从上面我们可以了解不同的classloader解释的相同class也无法互相转换

这样我们把目标放在devtools上。

我们在springboot中引入了如下依赖

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

那么如何排除devtool的依赖呢?

在application.properties中增加

spring.devtools.restart.enabled=false

发现启动时仍然可以看出使用的restartedMain

2018-03-19 22:04:37.641  INFO 53428 --- [restartedMain] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@7443f7a3: startup date [Mon Mar 19 22:03:34 CST 2018]; root of context hierarchy
2018-03-19 22:04:37.654  INFO 53428 --- [restartedMain] s.w.s.m.m.a.RequestMappingHandlerAdapter : Detected ResponseBodyAdvice bean in org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration$ActuatorEndpointLinksAdvice
2018-03-19 22:04:37.956  INFO 53428 --- [restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/swagger-ui.html] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-03-19 22:04:37.956  INFO 53428 --- [restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping

这边线程名为restartedMain 为啥设置spring.devtools.restart.enabled 无效呢?

代码

在对应devtools的包中使用了ApplicationListener

private void onApplicationStartingEvent(ApplicationStartingEvent event) {
  // It's too early to use the Spring environment but we should still allow
  // users to disable restart using a System property.
  String enabled = System.getProperty(ENABLED_PROPERTY);
  if (enabled == null || Boolean.parseBoolean(enabled)) {
   String[] args = event.getArgs();
   DefaultRestartInitializer initializer = new DefaultRestartInitializer();
   boolean restartOnInitialize = !AgentReloader.isActive();
   Restarter.initialize(args, false, initializer, restartOnInitialize);
  }
  else {
   Restarter.disable();
  }
}

很明显其实restarter的开启是从系统变量中读取 而并非从spring的环境中读取 正如注释所说 其实此刻使用spring的property还太早

因此可以使用系统变量

因此我们可以使用jvm参数

-Dspring.devtools.restart.enabled=false

果然此时一切就OK了

2018-03-19 22:18:12.928  INFO 66260 --- [main] com.f6car.base.Application               : The following profiles are active: dev
2018-03-19 22:18:13.131  INFO 66260 --- [main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@2a4354cb: startup date [Mon Mar 19 22:18:13 CST 2018]; root of context hierarchy

那在Spring的配置文件中配置的目的是啥呢?

/**
 * Restart properties.
 */
public static class Restart {

  private static final String DEFAULT_RESTART_EXCLUDES = "META-INF/maven/**,"
     + "META-INF/resources/**,resources/**,static/**,public/**,templates/**,"
     + "**/*Test.class,**/*Tests.class,git.properties,META-INF/build-info.properties";

  private static final long DEFAULT_RESTART_POLL_INTERVAL = 1000;

  private static final long DEFAULT_RESTART_QUIET_PERIOD = 400;

  /**
  * Enable automatic restart.
  */
  private boolean enabled = true;

  /**
  * Patterns that should be excluded from triggering a full restart.
  */
  private String exclude = DEFAULT_RESTART_EXCLUDES;

  /**
  * Additional patterns that should be excluded from triggering a full restart.
  */
  private String additionalExclude;

  /**
  * Amount of time (in milliseconds) to wait between polling for classpath changes.
  */
  private long pollInterval = DEFAULT_RESTART_POLL_INTERVAL;

  /**
  * Amount of quiet time (in milliseconds) required without any classpath changes
  * before a restart is triggered.
  */
  private long quietPeriod = DEFAULT_RESTART_QUIET_PERIOD;

  /**
  * Name of a specific file that when changed will trigger the restart check. If
  * not specified any classpath file change will trigger the restart.
  */
  private String triggerFile;

  /**
  * Additional paths to watch for changes.
  */
  private List<File> additionalPaths = new ArrayList<File>();

  public boolean isEnabled() {
   return this.enabled;
  }

  public void setEnabled(boolean enabled) {
   this.enabled = enabled;
  }

从代码中看到似乎是用来配置是否监听能否自动重启

/**
  * Local Restart Configuration.
  */
  @ConditionalOnProperty(prefix = "spring.devtools.restart", name = "enabled", matchIfMissing = true)
  static class RestartConfiguration {

   @Autowired
   private DevToolsProperties properties;

   @EventListener
   public void onClassPathChanged(ClassPathChangedEvent event) {
     if (event.isRestartRequired()) {
      Restarter.getInstance().restart(
         new FileWatchingFailureHandler(fileSystemWatcherFactory()));
     }
   }

   @Bean
   @ConditionalOnMissingBean
   public ClassPathFileSystemWatcher classPathFileSystemWatcher() {
     URL[] urls = Restarter.getInstance().getInitialUrls();
     ClassPathFileSystemWatcher watcher = new ClassPathFileSystemWatcher(
        fileSystemWatcherFactory(), classPathRestartStrategy(), urls);
     watcher.setStopWatcherOnRestart(true);
     return watcher;
   }

   @Bean
   @ConditionalOnMissingBean
   public ClassPathRestartStrategy classPathRestartStrategy() {
     return new PatternClassPathRestartStrategy(
        this.properties.getRestart().getAllExclude());
   }

   @Bean
   public HateoasObjenesisCacheDisabler hateoasObjenesisCacheDisabler() {
     return new HateoasObjenesisCacheDisabler();
   }

   @Bean
   public FileSystemWatcherFactory fileSystemWatcherFactory() {
     return new FileSystemWatcherFactory() {

      @Override
      public FileSystemWatcher getFileSystemWatcher() {
        return newFileSystemWatcher();
      }

     };
   }

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

  }

}

整个根据该配置来返回是否注册对应的watchService

当然我们也可以移除该jar

需要注意的是 当将这一段代码注释时 需要重新

mvn clean

否则有可能无法自动排除该jar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

您可能感兴趣的文章:

  • spring boot启动时加载外部配置文件的方法
  • 详解spring boot容器加载完后执行特定操作
  • Spring Boot容器加载时执行特定操作(推荐)
  • spring boot加载第三方jar包的配置文件的方法
  • spring boot启动加载数据原理分析
  • spring boot中的静态资源加载处理方式
  • Spring Boot 启动加载数据 CommandLineRunner的使用
  • 详解Spring Boot加载properties和yml配置文件
  • SpringBoot加载静态资源的方式
(0)

相关推荐

  • 详解spring boot容器加载完后执行特定操作

    有时候我们需要在spring boot容器启动并加载完后,开一些线程或者一些程序来干某些事情.这时候我们需要配置ContextRefreshedEvent事件来实现我们要做的事情 1.ApplicationStartup类 public class ApplicationStartup implements ApplicationListener<ContextRefreshedEvent>{ public void onApplicationEvent(ContextRefreshedEve

  • 详解Spring Boot加载properties和yml配置文件

    一.系统启动后注入配置 package com.example.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframewo

  • SpringBoot加载静态资源的方式

    在SpringBoot中加载静态资源和在普通的web应用中不太一样.默认情况下,spring Boot从classpath下一个叫/static(/public,/resources或/META-INF/resources)的文件夹或从ServletContext根目录提供静态内容.下面我们来写个例子看一下就会一目了然了:首先看一下项目的目录结构: 我们在resources下面的templates目录下建一个home.html的文件,完整目录为:src/main/resources/templa

  • spring boot启动加载数据原理分析

    实际应用中,我们会有在项目服务启动的时候就去加载一些数据或做一些事情这样的需求. 为了解决这样的问题,spring Boot 为我们提供了一个方法,通过实现接口 CommandLineRunner 来实现. 创建实现接口 CommandLineRunner 的类,通过@Component注解,就可以实现启动时加载数据项.使用@Order 注解来定义执行顺序. IndexStartupRunner.Java类: import org.springframework.boot.CommandLine

  • Spring Boot 启动加载数据 CommandLineRunner的使用

    实际应用中,我们会有在项目服务启动的时候就去加载一些数据或做一些事情这样的需求. 为了解决这样的问题,spring Boot 为我们提供了一个方法,通过实现接口 CommandLineRunner 来实现. 很简单,只需要一个类就可以,无需其他配置. 创建实现接口 CommandLineRunner 的类 package org.springboot.sample.runner; import org.springframework.boot.CommandLineRunner; import

  • spring boot中的静态资源加载处理方式

    1.spring boot默认资源处理 Spring Boot 默认为我们提供了静态资源处理,使用 WebMvcAutoConfiguration 中的配置各种属性. spring boot默认加载文件的路径是: /META-INF/resources/ /resources/ /static/ /public/ 这些目录下面, 当然我们也可以从spring boot源码也可以看到Java代码: private static final String[] CLASSPATH_RESOURCE_L

  • spring boot加载第三方jar包的配置文件的方法

    前言 今天收到一封邮件,大概内容如下:spring boot鼓励去配置化,那么怎么将第三方jar包中的xml去配置化了? 其实,这个问题,在前面的文章中也有提到,http://www.jb51.net/article/125700.htm 下面,我们就以Quartz定时任务为例,单独对这个问题来进行说明,如何实现去配置化. 如果不使用spring boot,我们配置一个简单的定时任务时,需要引入以下配置文件: <!-- 配置需要定时执行的任务类以及方法 --> <bean id=&quo

  • spring boot启动时加载外部配置文件的方法

    前言 相信很多人选择Spring Boot主要是考虑到它既能兼顾Spring的强大功能,还能实现快速开发的便捷.本文主要给大家介绍了关于spring boot启动时加载外部配置文件的相关内容,下面话不多说了,来随着小编一起学习学习吧. 业务需求: 加载外部配置文件,部署时更改比较方便. 先上代码: @SpringBootApplication public class Application { public static void main(String[] args) throws Exce

  • Spring Boot容器加载时执行特定操作(推荐)

    某些情况下我们需要在 Spring Boot 容器启动加载完后执行一些操作,此时可以通过实现 ApplicationListener<E extends ApplicationEvent> 接口,并指定相应事件来执行操作,例如启动某些自定义守护线程 ApplicationContextEvent 是由 ApplicationContext 引发的事件基类,它有几个实现类: ContextRefreshedEvent :ApplicationContext 容器初始化或者刷新时触发该事件,执行一

  • SpringBoot的reload加载器的方法

    背景 springboot越来越多的被大家所使用SpringBoot DevTool实现热部署 出现了相同类castException 分析 首先确定出现相同类的castException比如是由于classloader不同造成的. 一个class是否相同取决于两个因素 classloader相同 class文件相同 即不同classloader解释出来的class是不同的class 我们在学习jdbc的时候常见的使用 /** * Returns the {@code Class} object

  • php类自动加载器实现方法

    本文实例讲述了php类自动加载器实现方法.分享给大家供大家参考.具体如下: 这里autoload 可兼容以下格式: Cache_File_Json class_xxx.php xxx.class.php   xxx.php php代码如下: function __autoload($className){ $dirs=explode('_',$className); $fileName=array_pop($dirs); //print_r($dirs); $filePath=$fileName

  • vue-content-loader内容加载器的使用方法

    当我们开发网站或者APP时,遇到内容过多加载速度慢时,会导致用户打开页面有大量空白页,vue-content-loader正是解决这个问题的一个组件,使加载内容之前生成一个dom模板,提高用户体验. 第一步:安装 在控制台的项目路径下执行:npm install vue-content-loader --save 第二步:引入使用 <template> <!--<content-loader></content-loader>--> <facebook

  • 解析Java虚拟机中类的初始化及加载器的父委托机制

    类的初始化 在初始化阶段,Java虚拟机执行类的初始化语句,为类的静态变量赋予初始值. 在程序中,静态变量的初始化有两种途径: 1.在静态变量的声明处进行初始化: 2.在静态代码块中进行初始化. 没有经过显式初始化的静态变量将原有的值. 一个比较奇怪的例子: package com.mengdd.classloader; class Singleton { // private static Singleton mInstance = new Singleton();// 位置1 // 位置1输

  • js实现延时加载Flash的方法

    本文实例讲述了js实现延时加载Flash的方法.分享给大家供大家参考,具体如下: 当页面中包含自动播放视频的flash播放器时,flash的流媒体播放会一直不停的下载视频(哪怕你点击了播放暂停/停止也一样在不停下载),这样会影响到页面其它元素的加载. 最简单的优化办法就是让Flash一开始不要播放:当然,你可以修改fla源文件,让视频播放一开始就处于暂停,直到用户点击了播放按钮.但问题是,如果您嵌入的是没有源文件的flash动画,这种路子就走不通了. 思路: 可以先在放置flash的地方,放一张

  • 在Python的Django框架中加载模版的方法

    为了减少模板加载调用过程及模板本身的冗余代码,Django 提供了一种使用方便且功能强大的 API ,用于从磁盘中加载模板, 要使用此模板加载API,首先你必须将模板的保存位置告诉框架. 设置的保存文件就是settings.py. 如果你是一步步跟随我们学习过来的,马上打开你的settings.py配置文件,找到TEMPLATE_DIRS这项设置吧. 它的默认设置是一个空元组(tuple),加上一些自动生成的注释. TEMPLATE_DIRS = ( # Put strings here, li

  • python动态加载包的方法小结

    本文实例总结了python动态加载包的方法.分享给大家供大家参考,具体如下: 动态加载模块有三种方法 1. 使用系统函数__import_() stringmodule = __import__('string') 2. 使用imp 模块 import imp stringmodule = imp.load_module('string',*imp.find_module('string')) imp.load_source("TYACMgrHandler_"+app.upper(),

  • 概述如何实现一个简单的浏览器端js模块加载器

    在es6之前,js不像其他语言自带成熟的模块化功能,页面只能靠插入一个个script标签来引入自己的或第三方的脚本,并且容易带来命名冲突的问题.js社区做了很多努力,在当时的运行环境中,实现"模块"的效果. 通用的js模块化标准有CommonJS与AMD,前者运用于node环境,后者在浏览器环境中由Require.js等实现.此外还有国内的开源项目Sea.js,遵循CMD规范.(目前随着es6的普及已经停止维护,不论是AMD还是CMD,都将是一段历史了) 浏览器端js加载器 实现一个简

  • 举例讲解Java的内部类与类的加载器

    内部类 class A { //Inner1 要在 A 初始化后 才能使用,即要被A的对象所调用 class Inner1 { int k = 0; // static int j = 0; //A加载后,Inner1没有加载,所以这个 静态变量j 无法立即使用,报错 final int z = 0; /*static void say1() { }*/ void say2() { } } //Inner2 在A加载好后就可以使用了 static class Inner2 { int k = 0

  • 深入理解requireJS-实现一个简单的模块加载器

    在前文中我们不止一次强调过模块化编程的重要性,以及其可以解决的问题: ① 解决单文件变量命名冲突问题 ② 解决前端多人协作问题 ③ 解决文件依赖问题 ④ 按需加载(这个说法其实很假了) ⑤ ...... 为了深入了解加载器,中间阅读过一点requireJS的源码,但对于很多同学来说,对加载器的实现依旧不太清楚 事实上不通过代码实现,单单凭阅读想理解一个库或者框架只能达到一知半解的地步,所以今天便来实现一个简单的加载器 加载器原理分析 分与合 事实上,一个程序运行需要完整的模块,以下代码为例: /

随机推荐