背景
springboot越来越多的被大家所使用SpringBoot DevTool实现热部署
出现了相同类castException
分析
首先确定出现相同类的castException比如是由于classloader不同造成的。
一个class是否相同取决于两个因素
- classloader相同
- class文件相同
即不同classloader解释出来的class是不同的class
我们在学习jdbc的时候常见的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
/**
* 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中引入了如下依赖
1
2
3
4
5
|
< dependency >
< groupId >org.springframework.boot</ groupId >
< artifactId >spring-boot-devtools</ artifactId >
< optional >true</ optional >
</ dependency >
|
那么如何排除devtool的依赖呢?
在application.properties中增加
1
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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参数
1
|
-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的配置文件中配置的目的是啥呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
/**
* 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;
}
|
从代码中看到似乎是用来配置是否监听能否自动重启
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
/**
* 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
需要注意的是 当将这一段代码注释时 需要重新
1
|
mvn clean
|
否则有可能无法自动排除该jar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://my.oschina.net/qixiaobo025/blog/1648121