SpringBoot的reload加载器的方法
背景
springboot越来越多的被大家所使用springboot devtool实现热部署
出现了相同类castexception
分析
首先确定出现相同类的castexception比如是由于classloader不同造成的。
一个class是否相同取决于两个因素
- classloader相同
- 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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: RHEL6.5下JDK1.8安装教程