背景
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