Spring Boot簡化了Spring應用的開發,採用約定大於配置的思想,去繁從簡,很方便就能構建一個獨立的、產品級別的應用。html
開發笨重、配置繁多複雜、開發效率低下、部署流程複雜、第三方技術集成難度大。java
自動配置xxxAutoConfigurationreact
SpringBoot使用一個全局的配置文件。配置文件名是固定的。
-application.properties或者application.ymlweb
全局配置文件的做用是對一些默認配置進行修改spring
對比點 | @ConfigurationProperties | @Value |
---|---|---|
功能 | 批量注入配置文件中的屬性 | 一個個指定 |
鬆散綁定(鬆散語法) | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303數據校驗 | 支持 | 不支持 |
複雜類型封裝 | 支持 | 不支持 |
屬性名匹配規則
-person.firstName 使用標準方式
-person.first-name 使用-
-person.first_name 使用_
-PERSON_FIRST_NAME 推薦系統屬性使用這種寫法json
@PropertySource
加載指定的配置文件瀏覽器
ConfigurationProperties
-與@Bean結合爲屬性賦值
-與@PropertySource(只能用於properties文件)結合讀取指定文件。緩存
ConfigurationProperties Validation
-支持JSR303進行配置文件值校驗。tomcat
@Component @PropertySource(value={"classpath:person.properties"}) @ConfigurationProperties(prefix="person") @Validated public class Person{ @Email @Value("${person.email}") private String email; }
RandomValuePropertySource
配置文件中可使用隨機數
-${random.value}
-${random.int}
-${random.long}
-${random.int(10)}
-${random.int[1024,65536]}springboot
app.name=MyApp app.description=${app.name} is a SpringBoot Application
profile是Spring對不一樣環境提供不一樣配置功能的支持,能夠經過激活指定參數的方式快速切換環境。
#### 1.多profile文件形式
格式:application-{profile}.properties/yml
application-dev.properties、application-prod.properties
spring.profiles.active=prod #激活指定配置 spring.profiles=prod server.port=80 # default表示未指定時的默認配置 spring.profiles=default server.port=8080
SpringBoot啓動會掃描一下位置的application.properties或者application.yml文件做爲SpringBoot的默認配置文件。
- file:./config/
- file:./
- classpath:/config/
-classpath:/
-以上是按照優先級從高到低的順序,全部位置的文件都會被加載,高優先級配置內容會覆蓋低優先級配置內容。
-能夠經過配置spring.config.location來改變默認配置。
@Conditional擴展註解 | 做用(判斷是否知足當期指定條件) |
---|---|
@ConditionalOnJava | 系統的java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定Bean |
@ConditionalOnMissingBean | 容器中不存在指定Bean |
@ConditionalOnExpression | 知足SpEL表達式指定 |
@ConditionalOnClass | 系統中有指定的類 |
@ConditionalOnMissingClass | 容器中沒有指定類 |
@ConditionalOnSingleCandidate | 容器中只有一個指定的Bean,或者這個Bean是首選Bean |
@ConditionalOnProperty | 系統中指定的屬性是否有指定的值 |
@ConditionalOnResource | 類路徑下是否存在指定資源文件 |
@ConditionalOnWebApplication | 當前是web環境 |
@ConditionalOnNotWebApplication | 當前不是web環境 |
@ConditionalOnJndi | JNDI存在指定項 |
市場上存在很是多的日誌框架,JUL(java.util.logging)、JCL(Apache Commons Logging)、Log4J、Log4J二、Logback、SLF4j、jboss-logging等。
日誌門面 | 日誌實現 |
---|---|
JCL、SLF4J、jboss-logging | log4j、JUL、Log4j二、Logback |
日誌系統 | 配置文件 |
---|---|
Logback | logback-spring.xml、logback-spring.groovy、logback.xml或logback.groovy |
Log4j2 | log4j2-spring.xml、log4j2.xml |
JUL | logging.properties |
SpringBoot對SpringMVC的默認配置(WebMvcAutoConfiguration)以下:
- 支持靜態資源,包括支持Wenjars - 靜態首頁訪問 - 支持favicon.ico - 自動註冊了Converter、GenericConverter、Formatter。 - Converter:轉換器。 - Formatter:格式化器
編寫一個配置類(@Configuration),是WebMvcConfigurerAdapter類型,不能標註@EnableWebMvc註解
@Configuration public class MyMvcConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/desperado").setViewName("success"); } }
原理
若是想要使SpringMVC的自動配置失效,只須要在咱們自定義的配置類中添加@EnableWebMvc註解便可。
@EnableWebMvc @Configuration public class MyMvcConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/desperado").setViewName("success"); } }
原理
@Import(DelegatingWebMvcConfiguation.class) public @interface EnableWebMvc{}
@Configuration public class DelegatingWebMvcConfiguation extend WebMvcConfigurationSupport{}
@Configuration @ConditionalOnWebApplication @ConditionalOnClass({Servlet.class,DispatcherServlet.class, WebMvcConfigurerAdapter.class}) //容器中沒有這個組件,這個自動配置類纔會生效 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class}) public class WebMvcAutoConfiguration{}
使用自定義WebMvcConfigurationAdapter進行配置
//使用WebMvcConfigurationAdapter能夠擴展SpringMVC的功能 @Configuration public class MyMvcConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { //瀏覽器發送/desperado 請求來到success registry.addViewController("/desperado").setViewName("success"); } //全部的webMvcConfigurerAdapter組件都會一塊兒起做用 @Bean //將組件註冊到容器 public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){ WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() { @Override public void addViewControllers(ViewControllerRegistry registry) { //配置默認路徑的頁面 registry.addViewController("/").setViewName("login"); registry.addViewController("/index.html").setViewName("login"); } }; return adapter; } }
1.編寫國際化配置文件
編寫不一樣語言的配置文件,好比login.properties、login_en_US.properties、login_zh_CN.properties等。
@EnableConfigurationProperties public class MessageSourceAutoConfiguration { private static final Resource[] NO_RESOURCES = new Resource[0]; public MessageSourceAutoConfiguration() { } @Bean @ConfigurationProperties( prefix = "spring.messages" ) public MessageSourceProperties messageSourceProperties() { return new MessageSourceProperties(); } @Bean public MessageSource messageSource(MessageSourceProperties properties) { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); if (StringUtils.hasText(properties.getBasename())) { //設置國際化資源文件的基礎名(去掉語言國家代碼) messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename()))); } if (properties.getEncoding() != null) { messageSource.setDefaultEncoding(properties.getEncoding().name()); } messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale()); Duration cacheDuration = properties.getCacheDuration(); if (cacheDuration != null) { messageSource.setCacheMillis(cacheDuration.toMillis()); } messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat()); messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage()); return messageSource; }
原理
根據請求頭帶來的區域信息獲取Locale進行國際化。
原理
public class DefaultErrorAttributes implements ErrorAttributes { //獲取錯誤頁面的信息 public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) { Map<String, Object> errorAttributes = new LinkedHashMap(); errorAttributes.put("timestamp", new Date()); errorAttributes.put("path", request.path()); Throwable error = this.getError(request); HttpStatus errorStatus = this.determineHttpStatus(error); errorAttributes.put("status", errorStatus.value()); errorAttributes.put("error", errorStatus.getReasonPhrase()); errorAttributes.put("message", this.determineMessage(error)); this.handleException(errorAttributes, this.determineException(error), includeStackTrace); return errorAttributes; } }
@Controller @RequestMapping({"${server.error.path:${error.path:/error}}"}) public class BasicErrorController extends AbstractErrorController { //產生html類型的數據,瀏覽器發送的請求來打這個方法處理 @RequestMapping( produces = {"text/html"} ) public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { HttpStatus status = this.getStatus(request); Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML))); response.setStatus(status.value()); //去哪一個頁面做爲錯誤頁面。包含頁面地址和頁面內容 ModelAndView modelAndView = this.resolveErrorView(request, response, status, model); return modelAndView != null ? modelAndView : new ModelAndView("error", model); } //產生json數據,其餘客戶端來到這個方法處理 @RequestMapping public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL)); HttpStatus status = this.getStatus(request); return new ResponseEntity(body, status); } }
public class ErrorProperties { @Value("${error.path:/error}") private String path = "/error"; private boolean includeException; private ErrorProperties.IncludeStacktrace includeStacktrace; private final ErrorProperties.Whitelabel whitelabel; }
public class ErrorMvcAutoConfiguration { private static class StaticView implements View { private static final Log logger = LogFactory.getLog(ErrorMvcAutoConfiguration.StaticView.class); private StaticView() { } public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { if (response.isCommitted()) { String message = this.getMessage(model); logger.error(message); } else { StringBuilder builder = new StringBuilder(); Date timestamp = (Date)model.get("timestamp"); Object message = model.get("message"); Object trace = model.get("trace"); if (response.getContentType() == null) { response.setContentType(this.getContentType()); } builder.append("<html><body><h1>Whitelabel Error Page</h1>").append("<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>").append("<div id='created'>").append(timestamp).append("</div>").append("<div>There was an unexpected error (type=").append(this.htmlEscape(model.get("error"))).append(", status=").append(this.htmlEscape(model.get("status"))).append(").</div>"); if (message != null) { builder.append("<div>").append(this.htmlEscape(message)).append("</div>"); } if (trace != null) { builder.append("<div style='white-space:pre-wrap;'>").append(this.htmlEscape(trace)).append("</div>"); } builder.append("</body></html>"); response.getWriter().append(builder.toString()); } } private String htmlEscape(Object input) { return input != null ? HtmlUtils.htmlEscape(input.toString()) : null; } private String getMessage(Map<String, ?> model) { Object path = model.get("path"); String message = "Cannot render error page for request [" + path + "]"; if (model.get("message") != null) { message = message + " and exception [" + model.get("message") + "]"; } message = message + " as the response has already been committed."; message = message + " As a result, the response may have the wrong status code."; return message; } public String getContentType() { return "text/html"; } }
5.DefaultErrorViewResolver解析頁面
public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered { public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) { ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model); if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) { modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model); } return modelAndView; } private ModelAndView resolve(String viewName, Map<String, Object> model) { //默認去找一個頁, error/404 String errorViewName = "error/" + viewName; //模板引擎能夠解析這個頁面地址就要模板引擎解析 TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext); //模板引擎可用的狀況下返回到errorViewName指定的視圖地址 //模板引擎不能夠,就在靜態資源文件夾下找對應的頁面 return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model); }
@ControllerAdvice public class MyExceptionHandler { @ResponseBody @ExceptionHandler(CustomException.class) public Map<String,Object> handleException(Exception e){ HashMap<String, Object> map = new HashMap<>(); map.put("code","錯誤信息"); map.put("message",e.getMessage()); return map; } }
2.轉發到/error進行自適應響應效果處理。
@ControllerAdvice public class MyExceptionHandler { @ResponseBody @ExceptionHandler(CustomException.class) public String handleException(Exception e, HttpServletRequest request){ HashMap<String, Object> map = new HashMap<>(); //傳入咱們本身的錯誤狀態碼,不然就不會進入定製錯誤頁面的解析流程 request.setAttribute("java.servlet.error.status_code","500"); map.put("code","錯誤信息"); map.put("message",e.getMessage()); //轉發到/error return "forward:/error"; } }
出現錯誤以後,回來到/error請求,會被BasicErrorController處理,響應出去能夠獲取的數據是由getErrorAttributes獲得的。
//給容器中加入自定義的ErrorAttributes @Component public class MyErrorAttributes extends DefaultErrorAttributes { @Override public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { //獲取ErrorAttributes的map Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace); //加入本身屬性字段 map.put("name","desperado"); return map; } }
SpringBoot默認使用Tomcat做爲內嵌的Servlet容器
在配置文件application文件中修改和server有關的配置。
server.port=8081 server.context_path=/crud server.tomcat.uri-encoding=utf-8
編寫一個EmbeddedServletContainerCustomizer(2.x中使用WebServerFactoryCustomizer),來修改Servlet容器的配置。
@Bean public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){ return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>(){ @Override public void customize(ConfigurableWebServerFactory factory) { factory.setPort(8081); } }; }
因爲SpringBoot是默認以jar包的方式啓動內嵌的Servlet容器來啓動SpringBoot的web應用,沒有web.xml文件。因此註冊Servlet、Filter、Listener的方式也不一樣
@Bean public ServletRegistrationBean<MyServlet> myServlet(){ ServletRegistrationBean<MyServlet> registrationBean = new ServletRegistrationBean<>(new MyServlet(), "/myServlet"); return registrationBean; }
@Bean public FilterRegistrationBean<MyFilter> myFilter(){ FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new MyFilter()); registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet")); return registrationBean; }
@Bean public ServletListenerRegistrationBean<MyListener> myListener(){ ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener()); return registrationBean; }
替換爲其餘的Servlet很是簡單,只須要在pom中引入其依賴,而後排除tomcat的依賴便可.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>
@Configuration class ServletWebServerFactoryConfiguration { ServletWebServerFactoryConfiguration() { } @Configuration @ConditionalOnClass({Servlet.class, Undertow.class, SslClientAuthMode.class}) @ConditionalOnMissingBean( value = {ServletWebServerFactory.class}, search = SearchStrategy.CURRENT ) public static class EmbeddedUndertow { public EmbeddedUndertow() { } @Bean public UndertowServletWebServerFactory undertowServletWebServerFactory() { return new UndertowServletWebServerFactory(); } } @Configuration @ConditionalOnClass({Servlet.class, Server.class, Loader.class, WebAppContext.class}) @ConditionalOnMissingBean( value = {ServletWebServerFactory.class}, search = SearchStrategy.CURRENT ) public static class EmbeddedJetty { public EmbeddedJetty() { } @Bean public JettyServletWebServerFactory JettyServletWebServerFactory() { return new JettyServletWebServerFactory(); } } @Configuration //判斷當前是否引入了tomcat依賴 @ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class}) ///判斷當前容器沒有用戶本身定義ServletWebServerFactory: //嵌入式的Servlet容器工廠;做用:建立嵌入式的Servlet容器 @ConditionalOnMissingBean( value = {ServletWebServerFactory.class}, search = SearchStrategy.CURRENT ) public static class EmbeddedTomcat { public EmbeddedTomcat() { } @Bean public TomcatServletWebServerFactory tomcatServletWebServerFactory() { return new TomcatServletWebServerFactory(); } } }
4.以tomcat爲例
public WebServer getWebServer(ServletContextInitializer... initializers) { Tomcat tomcat = new Tomcat(); File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); this.customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); this.configureEngine(tomcat.getEngine()); Iterator var5 = this.additionalTomcatConnectors.iterator(); while(var5.hasNext()) { Connector additionalConnector = (Connector)var5.next(); tomcat.getService().addConnector(additionalConnector); } this.prepareContext(tomcat.getHost(), initializers); return thisb.getTomcatWebServer(tomcat); }
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { //初始化以前 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { //若是當前初始化的是一個WebServerFactory類型的組件 if (bean instanceof WebServerFactory) { this.postProcessBeforeInitialization((WebServerFactory)bean); } return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } private void postProcessBeforeInitialization(WebServerFactory webServerFactory) { // 獲取全部的定製器,調用每個定製器的customize方法來給servlet容器進行屬性賦值 ((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> { customizer.customize(webServerFactory); }); } private Collection<WebServerFactoryCustomizer<?>> getCustomizers() { if (this.customizers == null) { // 定製Servlet容器,給容器中能夠添加一個WebServerFactoryCustomizer類型的組件 this.customizers = new ArrayList(this.getWebServerFactoryCustomizerBeans()); this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE); this.customizers = Collections.unmodifiableList(this.customizers); } return this.customizers; } private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() { //從容器中獲取全部這個類型的組件:WebServerFactoryCustomizer return this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values(); } }
總結
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch(this.webApplicationType) { case SERVLET: contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"); break; case REACTIVE: contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"); break; default: contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext"); } } catch (ClassNotFoundException var3) { throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3); } } return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass); }
public void refresh() throws BeansException, IllegalStateException { Object var1 = this.startupShutdownMonitor; synchronized(this.startupShutdownMonitor) { //準備刷新的context this.prepareRefresh(); //調用子類去刷新內部的實例工廠 ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); //準備在這個context中要使用的實例工廠 this.prepareBeanFactory(beanFactory); try { // 容許在上下文子類中對bean工廠進行後置處理。 this.postProcessBeanFactory(beanFactory); //在context中調用註冊爲bean的工廠處理器。 this.invokeBeanFactoryPostProcessors(beanFactory); //註冊攔截bean建立的bean處理器。 this.registerBeanPostProcessors(beanFactory); //初始化此context的消息源 this.initMessageSource(); //初始化此上下文的事件多播器。 this.initApplicationEventMulticaster(); //在特定的context子類中初始化其餘特殊bean。 this.onRefresh(); // 檢查監聽器bean並註冊它們。 this.registerListeners(); // 實例化全部剩餘(非延遲初始化)單例。 this.finishBeanFactoryInitialization(beanFactory); //最後一步:發佈相應的事件。 this.finishRefresh(); } catch (BeansException var9) { if (this.logger.isWarnEnabled()) { this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9); } //摧毀已經建立的單例以免佔用資源。 this.destroyBeans(); //重置 ‘active’ 標誌 this.cancelRefresh(var9); //Propagate exception to caller. throw var9; } finally { //從咱們開始,重置Spring核心中的常見內省緩存 //可能再也不須要單例bean的元數據了... this.resetCommonCaches(); } } }
private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = this.getServletContext(); if (webServer == null && servletContext == null) { ServletWebServerFactory factory = this.getWebServerFactory(); this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()}); } else if (servletContext != null) { try { this.getSelfInitializer().onStartup(servletContext); } catch (ServletException var4) { throw new ApplicationContextException("Cannot initialize servlet context", var4); } } this.initPropertySources(); }
獲取嵌入式的Servlet容器工廠: ServletWebServerFactory factory = this.getWebServerFactory();從IOC容器中獲取ServletWebServerFactory組件;
使用容器工廠獲取嵌入式的Servlet容器:his.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
public WebServer getWebServer(ServletContextInitializer... initializers) { Tomcat tomcat = new Tomcat(); File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); this.customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); this.configureEngine(tomcat.getEngine()); Iterator var5 = this.additionalTomcatConnectors.iterator(); while(var5.hasNext()) { Connector additionalConnector = (Connector)var5.next(); tomcat.getService().addConnector(additionalConnector); } this.prepareContext(tomcat.getHost(), initializers); return this.getTomcatWebServer(tomcat); }
9.先啓動嵌入式的Servlet容器,再將IOC容器中剩餘的沒有建立出來的對象獲取出來、IOC容器啓動就會建立嵌入式的Servlet容器。
優勢:簡單、便捷。
缺點:默認不支持JSP,優化定製比較複雜。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐starter‐tomcat</artifactId> <scope>provided</scope> </dependency>
public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { //傳入SpringBoot應用的主程序 return application.sources(SpringBoot04WebJspApplication.class); } }
原理
啓動服務器,服務器啓動SpringBoot應用[SpringBootServletInitializer],啓動IOC容器。
規則
@HandlesTypes({WebApplicationInitializer.class}) public class SpringServletContainerInitializer implements ServletContainerInitializer { public SpringServletContainerInitializer() { } public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List<WebApplicationInitializer> initializers = new LinkedList(); Iterator var4; if (webAppInitializerClasses != null) { var4 = webAppInitializerClasses.iterator(); while(var4.hasNext()) { Class<?> waiClass = (Class)var4.next(); if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { initializers.add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance()); } catch (Throwable var7) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7); } } } } if (initializers.isEmpty()) { servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); } else { servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); AnnotationAwareOrderComparator.sort(initializers); var4 = initializers.iterator(); while(var4.hasNext()) { WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next(); initializer.onStartup(servletContext); } } } }
至關於SpringBootServletInitializer的類會被建立對象,並執行onStartup()方法。
SpringBootServletInitializer實例執行onStartup的時候會crateRootApplicationContext建立容器。
protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) { // 1.建立SpringApplicationBuilder SpringApplicationBuilder builder = this.createSpringApplicationBuilder(); builder.main(this.getClass()); ApplicationContext parent = this.getExistingRootWebApplicationContext(servletContext); if (parent != null) { this.logger.info("Root context already created (using as parent)."); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null); builder.initializers(new ApplicationContextInitializer[]{new ParentContextApplicationContextInitializer(parent)}); } builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)}); builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class); // 2.調用configure方法,子類從新了這個方法,將SpringBoot的主程序類傳入 builder = this.configure(builder); builder.listeners(new ApplicationListener[]{new SpringBootServletInitializer.WebEnvironmentPropertySourceInitializer(servletContext)}); // 3.使用builder建立一個Spring應用 SpringApplication application = builder.build(); if (application.getAllSources().isEmpty() && AnnotationUtils.findAnnotation(this.getClass(), Configuration.class) != null) { application.addPrimarySources(Collections.singleton(this.getClass())); } Assert.state(!application.getAllSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation"); //確保錯誤頁被註冊 if (this.registerErrorPageFilter) { application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class)); } // 4. q啓動Spring應用 return this.run(application); }
Spring的應用啓動而且建立IOC容器。
先啓動Servlet容器,再啓動SpringBoot應用。