欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringBoot的reload加载器的方法

程序员文章站 2022-06-05 08:36:44
背景 springboot越来越多的被大家所使用springboot devtool实现热部署 出现了相同类castexception 分析 首...

背景

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参数

SpringBoot的reload加载器的方法

-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

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