SpringBoot Web开发

SpringBoot对静态资源的映射规则

SpringBoot中的SpringMVC自动配置在WebMvcAutoConfiguration类中

public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
    } else {
        Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
        CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
        if (!registry.hasMappingForPattern("/webjars/**")) {
            this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
        }

        String staticPathPattern = this.mvcProperties.getStaticPathPattern();
        if (!registry.hasMappingForPattern(staticPathPattern)) {
            this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
        }

    }
}

1、所有/webjars/**请求,都会去classpath:/META-INF/resources/webjars/找资源
webjars:会以jar包的形式引入静态资源,资源库地址https://www.webjars.org/
以jQuery为例,在POM文件中导入其依赖,在访问的时候只需要写webjars下面资源的名称即可

<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.3.1</version>
</dependency>

2、对/**请求,访问当前项目的任何资源,默认会去以下路径下查找,访问时地址不用加上静态资源文件夹

"classpath:/META-INF/resources/",
 "classpath:/resources/", 
 "classpath:/static/", 
 "classpath:/public/"

3、欢迎页,默认是寻找静态资源文件夹下的所有index.html页面,被/**映射
如localhost:8080/ 会去静态文件夹下找index页面

@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(
      ResourceProperties resourceProperties) {
   return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(),
         this.mvcProperties.getStaticPathPattern());
}

4、设置图标**/favicon.ico,浏览器图标默认是从静态资源文件夹下寻找favicon.ico

    @Configuration
   @ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
   public static class FaviconConfiguration {

      private final ResourceProperties resourceProperties;

      public FaviconConfiguration(ResourceProperties resourceProperties) {
         this.resourceProperties = resourceProperties;
      }

      @Bean
      public SimpleUrlHandlerMapping faviconHandlerMapping() {
         SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
         mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
         mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
               faviconRequestHandler()));
         return mapping;
      }

      @Bean
      public ResourceHttpRequestHandler faviconRequestHandler() {
         ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
         requestHandler
               .setLocations(this.resourceProperties.getFaviconLocations());
         return requestHandler;
      }

   }

}

模板引擎
在这里插入图片描述
SpringBoot推荐使用Thymeleaf,导入thymeleaf的starter依赖,查看如何配置,只需要在自动配置类中查看相应的properties即可
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对于返回视图,thymeleaf会自动转发到类路径下的templates目录下的对应的html,thymeleaf的使用可具体查看官网教程。
导入thymeleaf的名称空间,以便有提示

<html lang="en" xmlns:th="http://www.thymeleaf.org">

开发期间模板引擎页面修改以后,要实时生效
1、禁用模板引擎的缓存,spring.thymeleaf.cache=false
2、页面修改完成以后,需要在IDEA中ctrl+F9,使其重新编译

SpringBoot扩展SpringMVC
如果想要保留SpringBoot SpringMVC的一些特性,但是又想添加新的配置,如拦截器,视图控制器,则可以创建@Configuration标注的WebMvcConfigurerAdapter类型的类,,且不能标注@EnableWebMvc注解

@Configuration
public class MyConfigureAdapter extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {

        registry.addViewController("/cmzy").setViewName("success");
    }
}

SpringBoot会自动将自定义的WebMvcConfigurerAdapter加载,和容器中所有的WebMvcConfigurer一起起作用,如果配置了@EnableWebMvc注解,那么SpringBoot对SpringMVC的自动配置都不需要了,而是所有的配置都是自定义的。

SpringBoot国际化
SpringBoot国际化是使用的SpringMVC的,已经默认自动配置了。
1、配置国际化资源文件
2、SpringBoot自动配置国际化类,MessageSourceAutoConfiguration,并自动注入了ResourceBundleMessageSource ,默认情况下是获取类路径下的message名称的国际化资源文件,所以在全局配置文件中,可以使用spring.messages.basename设置资源文件的基础名,可以在前面设置包名,指定具体的位置

@Configuration
@ConditionalOnMissingBean(value = MessageSource.class, search = SearchStrategy.CURRENT)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(ResourceBundleCondition.class)
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "spring.messages")
public class MessageSourceAutoConfiguration {

    private String basename = "messages";

    @Bean
    public MessageSource messageSource() {
       ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
       if (StringUtils.hasText(this.basename)) {
           //此处会设置资源文件的基础名(也就是不包含国家和地区代码的名字)
          messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
                StringUtils.trimAllWhitespace(this.basename)));
       }
       if (this.encoding != null) {
          messageSource.setDefaultEncoding(this.encoding.name());
       }
       messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale);
       messageSource.setCacheSeconds(this.cacheSeconds);
       messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat);
       return messageSource;
    }

3、在页面使用thymeleaf获取资源文件
上述都是使用浏览器默认的语言设置来判断国际化的,当界面上定义了相关中英文按钮,手动切换的话,SpringMVC根据Locale信息来判断,而具体生成的类是在LocaleResolver中生成的Locale,从请求头中根据语言信息生成Locale,在SpringBoot自动配置SpringMVC的配置类中自动注入了LocaleResolver ,其中默认是AcceptHeaderLocaleResolver,可以自己定义LocaleResolver 改变Locale的生成方式

@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
   if (this.mvcProperties
         .getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
      return new FixedLocaleResolver(this.mvcProperties.getLocale());
   }
   AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
   localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
   return localeResolver;
}

AcceptHeaderLocaleResolver中获取Locale是从请求头中获取,自定义可以设置一个参数,带上语言信息,然后自己定义LocaleResolver ,并加入到容器中

@Override
public Locale resolveLocale(HttpServletRequest request) {
   Locale defaultLocale = getDefaultLocale();
   if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
      return defaultLocale;
   }
   Locale requestLocale = request.getLocale();
   if (isSupportedLocale(requestLocale)) {
      return requestLocale;
   }
   Locale supportedLocale = findSupportedLocale(request);
   if (supportedLocale != null) {
      return supportedLocale;
   }
   return (defaultLocale != null ? defaultLocale : requestLocale);
}

SpringBoot 错误处理
其自动配置类ErrorMvcAutoConfiguration,其给容器中自动添加了一些组件:
DefaultErrorAttributes:用于添加一些发生错误时的信息,以便客户端可以调用

@Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
      boolean includeStackTrace) {
   Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();
   errorAttributes.put("timestamp", new Date());
   addStatus(errorAttributes, requestAttributes);
   addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
   addPath(errorAttributes, requestAttributes);
   return errorAttributes;
}

private void addStatus(Map<String, Object> errorAttributes,
      RequestAttributes requestAttributes) {
   Integer status = getAttribute(requestAttributes,
         "javax.servlet.error.status_code");
   if (status == null) {
      errorAttributes.put("status", 999);
      errorAttributes.put("error", "None");
      return;
   }
   errorAttributes.put("status", status);
   try {
      errorAttributes.put("error", HttpStatus.valueOf(status).getReasonPhrase());
   }
   catch (Exception ex) {
      // Unable to obtain a reason
      errorAttributes.put("error", "Http Status " + status);
   }
}

private void addErrorDetails(Map<String, Object> errorAttributes,
      RequestAttributes requestAttributes, boolean includeStackTrace) {
   Throwable error = getError(requestAttributes);
   if (error != null) {
      while (error instanceof ServletException && error.getCause() != null) {
         error = ((ServletException) error).getCause();
      }
      errorAttributes.put("exception", error.getClass().getName());
      addErrorMessage(errorAttributes, error);
      if (includeStackTrace) {
         addStackTrace(errorAttributes, error);
      }
   }
   Object message = getAttribute(requestAttributes, "javax.servlet.error.message");
   if ((!StringUtils.isEmpty(message) || errorAttributes.get("message") == null)
         && !(error instanceof BindingResult)) {
      errorAttributes.put("message",
            StringUtils.isEmpty(message) ? "No message available" : message);
   }
}

private void addErrorMessage(Map<String, Object> errorAttributes, Throwable error) {
   BindingResult result = extractBindingResult(error);
   if (result == null) {
      errorAttributes.put("message", error.getMessage());
      return;
   }
   if (result.getErrorCount() > 0) {
      errorAttributes.put("errors", result.getAllErrors());
      errorAttributes.put("message",
            "Validation failed for object='" + result.getObjectName()
                  + "'. Error count: " + result.getErrorCount());
   }
   else {
      errorAttributes.put("message", "No errors");
   }
}

BasicErrorController:用于处理默认的/error请求,里面实现了两种处理方法,用于返回不同的形式。一个是html形式的,一个是json形式的,通过请求的请求头来判断的,如果是浏览器,则accept为text/html,如果是其他客户端则是*/*

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
    @RequestMapping(produces = "text/html")
    public ModelAndView errorHtml(HttpServletRequest request,
          HttpServletResponse response) {
       HttpStatus status = getStatus(request);
       Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
             request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
       response.setStatus(status.value());
       ModelAndView modelAndView = resolveErrorView(request, response, status, model);
       return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
    }
    
    @RequestMapping
    @ResponseBody
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
       Map<String, Object> body = getErrorAttributes(request,
             isIncludeStackTrace(request, MediaType.ALL));
       HttpStatus status = getStatus(request);
       return new ResponseEntity<Map<String, Object>>(body, status);
    }

ErrorPageCustomizer:系统出现错误之后会来到error请求

    @Value("${error.path:/error}")
    private String path = "/error";

DefaultErrorViewResolver:用于BasicErrorController处理完后生成ModelAndView

public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {

   private static final Map<Series, String> SERIES_VIEWS;

   static {
      Map<Series, String> views = new HashMap<Series, String>();
      views.put(Series.CLIENT_ERROR, "4xx");
      views.put(Series.SERVER_ERROR, "5xx");
      SERIES_VIEWS = Collections.unmodifiableMap(views);
   }

    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
          Map<String, Object> model) {
       ModelAndView modelAndView = resolve(String.valueOf(status), model);
       if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
          modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
       }
       return modelAndView;
    }
    
    private ModelAndView resolve(String viewName, Map<String, Object> model) {
        //SpringBoot默认去哪个错误页面 error/4xx、5xx
        String errorViewName = "error/" + viewName;
        //模板引擎可以解析这个页面地址就用模板引擎解析
        TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
             .getProvider(errorViewName, this.applicationContext);
       if (provider != null) {
           //如果模板引擎可以解析就返回模板引擎解析的页面
          return new ModelAndView(errorViewName, model);
       }
       //如果没有模板引擎,则去静态资源文件夹下去寻找相应的页面
       return resolveResource(errorViewName, model);
    }
    
    private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
       for (String location : this.resourceProperties.getStaticLocations()) {
          try {
             Resource resource = this.applicationContext.getResource(location);
             resource = resource.createRelative(viewName + ".html");
             if (resource.exists()) {
                return new ModelAndView(new HtmlResourceView(resource), model);
             }
          }
          catch (Exception ex) {
          }
       }
       return null;
    }

步骤:
一旦系统出现4xx或5xx之类的错误,ErrorPageCustomizer就会生效(定制错误的响应规则),就会来到/error请求,接着该请求就会被BasicErrorController处理,处理完返回的视图是由DefaultErrorViewResolver解析得到的。

如何定制错误响应
定制错误页面:
有模板引擎的情况下;error/状态码(将错误页面命名为错误状态码.html放在模板引擎文件夹里面的error文件夹下,发生此状态码的错误就会找到对应的页面)。可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的额所有错误,精确优先(先匹配精确地错误码,如果没有匹配则使用该页面)
没有模板引擎(或模板引擎中找不到错误页面),就会在静态资源文件夹下找。
以上都没有找到错误页面,就使用SpringBoot默认的错误提示页面。

配置嵌入式Servlet容器
1、ConfigurableEmbeddedServletContainer
2、EmbeddedServletContainerCustomizer
3、注册Servlet、Filter、Listener
ServletRegistrationBean
FilterRegistrationBean
ServletListenerRegistrationBean
4、使用其他的Servlet容器,Jetty、Undertow.

SpringBoot默认使用Tomcat作为嵌入式的Servlet容器
修改和Server相关的配置(SpringBoot默认自动配置类对应的属性类ServerProperties)

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties
      implements EmbeddedServletContainerCustomizer, EnvironmentAware, Ordered {

   /**
    * Server HTTP port.
    */
   private Integer port;

   /**
    * Network address to which the server should bind to.
    */
   private InetAddress address;

   /**
    * Context path of the application.
    */
   private String contextPath;

   /**
    * Display name of the application.
    */
   private String displayName = "application";

通用设置

server.port=8080
servper.contextPath=/project

tomcat相关的设置
server.tomcat.xxx

也可以通过编写EmbeddedServletContainerCustomizer的实现类,来定制嵌入式的Servlet容器的配置。并将其加入容器。

@Bean
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
    EmbeddedServletContainerCustomizer containerCustomizer = new EmbeddedServletContainerCustomizer(){

        //container表示传入的是默认的嵌入式的tomcat容器
        @Override
        public void customize(ConfigurableEmbeddedServletContainer container) {
            container.setPort(8083);
            container.setContextPath("/test");
        }
    };
    return containerCustomizer;
}

在配置文件中配置其原理也是一样的,ServerProperties也是实现类EmbeddedServletContainerCustomizer,并在customize方法中设置属性

@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
   if (getPort() != null) {
      container.setPort(getPort());
   }
   if (getAddress() != null) {
      container.setAddress(getAddress());
   }
   if (getContextPath() != null) {
      container.setContextPath(getContextPath());
   }
   if (getDisplayName() != null) {
      container.setDisplayName(getDisplayName());
   }
   if (getSession().getTimeout() != null) {
      container.setSessionTimeout(getSession().getTimeout());
   }
   container.setPersistSession(getSession().isPersistent());
   container.setSessionStoreDir(getSession().getStoreDir());
   if (getSsl() != null) {
      container.setSsl(getSsl());
   }
   if (getJspServlet() != null) {
      container.setJspServlet(getJspServlet());
   }
   if (getCompression() != null) {
      container.setCompression(getCompression());
   }
   container.setServerHeader(getServerHeader());
   if (container instanceof TomcatEmbeddedServletContainerFactory) {
      getTomcat().customizeTomcat(this,
            (TomcatEmbeddedServletContainerFactory) container);
   }
   if (container instanceof JettyEmbeddedServletContainerFactory) {
      getJetty().customizeJetty(this,
            (JettyEmbeddedServletContainerFactory) container);
   }

   if (container instanceof UndertowEmbeddedServletContainerFactory) {
      getUndertow().customizeUndertow(this,
            (UndertowEmbeddedServletContainerFactory) container);
   }
   container.addInitializers(new SessionConfiguringInitializer(this.session));
   container.addInitializers(new InitParameterConfiguringServletContextInitializer(
         getContextParameters()));
}

注册Servlet、Filter、Listener
ServletRegistrationBean
FilterRegistrationBean
ServletListenerRegistrationBean

由于SpringBoot默认是以jar包的方式启动嵌入式的Servlet容器来启动SpringBoot的Web应用,没有web.xml文件,所以要注册三大组件Servlet、Filter、Listener,需要使用上面的对应的包装类。以Filter为例

@Bean
public FilterRegistrationBean filterRegistrationBean(){
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
    //绑定相关的Filter
    filterRegistrationBean.setFilter(new MyFilter());
    //设置Filter过滤的url
    filterRegistrationBean.setUrlPatterns(Arrays.asList("/hello","/test"));
    return filterRegistrationBean;

}

SpringBoot自动配置SpringMVC的时候,自动注册了SpringMVC的前端控制器,DispatcherServlet,在DispatcherServletAutoConfiguration中

@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public ServletRegistrationBean dispatcherServletRegistration(
      DispatcherServlet dispatcherServlet) {
 
     //默认拦截:/,表示所有请求,包括静态资源,但是不拦截jsp请求,/*会拦截jsp请求
       //可以通过server.servletPath来修改SpringMVC的前端过滤访问
   ServletRegistrationBean registration = new ServletRegistrationBean(
         dispatcherServlet, this.serverProperties.getServletMapping());
   registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
   registration.setLoadOnStartup(
         this.webMvcProperties.getServlet().getLoadOnStartup());
   if (this.multipartConfig != null) {
      registration.setMultipartConfig(this.multipartConfig);
   }
   return registration;
}

“/”和“/”含义是不一样的,“/”属于路径匹配,并且可以匹配所有请求,由于路径匹配的优先级仅次于精确匹配,所以“/*”会覆盖所有的扩展名匹配,故不推荐使用。“/”是servlet中特殊的匹配模式,优先
级最低(第(4)级),不会覆盖其他任何匹配模式,只是会替换servlet容器的内建default servlet ,该模式同样会匹配所有请求。

使用其他的Servlet容器
Jetty(适合做长连接的业务,如聊天)
Undertow(不支持Jsp,但是并发性能好)
SpringBoot默认支持的是tomcat,Jetty,undertow,在配置默认Servlet属性时ConfigurableEmbeddedServletContainer,其实现类
在这里插入图片描述
SpringBoot默认是使用tomcat,主要在于添加初始化依赖时默认使用的tomcat依赖
在这里插入图片描述

所以在切换不同的Servlet容器时,只需要将tomcat的依赖移除,并添加相应容器的依赖。

嵌入式Servlet容器自动配置原理
在自动配置类EmbeddedServletContainerAutoConfiguration中实现。

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {

   /**
    * Nested configuration if Tomcat is being used.
    */
   @Configuration
   @ConditionalOnClass({ Servlet.class, Tomcat.class })
   @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
   public static class EmbeddedTomcat {

      @Bean
      public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
         return new TomcatEmbeddedServletContainerFactory();
      }

   }

通过对应的容器工厂,在容器工厂中生成对应的嵌入式容器

@Override
public EmbeddedServletContainer getEmbeddedServletContainer(
      ServletContextInitializer... initializers) {
   Tomcat tomcat = new Tomcat();
   File baseDir = (this.baseDirectory != null ? this.baseDirectory
         : createTempDir("tomcat"));
   tomcat.setBaseDir(baseDir.getAbsolutePath());
   Connector connector = new Connector(this.protocol);
   tomcat.getService().addConnector(connector);
   customizeConnector(connector);
   tomcat.setConnector(connector);
   tomcat.getHost().setAutoDeploy(false);
   configureEngine(tomcat.getEngine());
   for (Connector additionalConnector : this.additionalTomcatConnectors) {
      tomcat.getService().addConnector(additionalConnector);
   }
   prepareContext(tomcat.getHost(), initializers);
   return getTomcatEmbeddedServletContainer(tomcat);
}
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
      Tomcat tomcat) {
   return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0);
}

并且在生成嵌入式容器时会启动容器

public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {
   Assert.notNull(tomcat, "Tomcat Server must not be null");
   this.tomcat = tomcat;
   this.autoStart = autoStart;
   initialize();
}

private void initialize() throws EmbeddedServletContainerException {
   TomcatEmbeddedServletContainer.logger
         .info("Tomcat initialized with port(s): " + getPortsDescription(false));
   synchronized (this.monitor) {
      try {
         addInstanceIdToEngineName();
         try {
            // Remove service connectors to that protocol binding doesn't happen
            // yet
            removeServiceConnectors();

            // Start the server to trigger initialization listeners
            this.tomcat.start();

            // We can re-throw failure exception directly in the main thread
            rethrowDeferredStartupExceptions();

            Context context = findContext();
            try {
               ContextBindings.bindClassLoader(context, getNamingToken(context),
                     getClass().getClassLoader());
            }
            catch (NamingException ex) {
               // Naming is not enabled. Continue
            }

            // Unlike Jetty, all Tomcat threads are daemon threads. We create a
            // blocking non-daemon to stop immediate shutdown
            startDaemonAwaitThread();
         }
         catch (Exception ex) {
            containerCounter.decrementAndGet();
            throw ex;
         }
      }
      catch (Exception ex) {
         throw new EmbeddedServletContainerException(
               "Unable to start embedded Tomcat", ex);
      }
   }
}

在修改嵌入式容器属性的时候通过@Import(BeanPostProcessorsRegistrar.class)导入一个后置处理器,该处理器中会将所有的EmbeddedServletContainerCustomizer拿到然后执行其customize方法,并把当期嵌入式容器作为参数传入到customize方法中。

总结:
1、SpringBoot根据导入的依赖情况,给容器中添加相应的嵌入式容器工厂;
2、容器中某个组件要创建对象就会触发后置处理器
3、后置处理器会拿到容器中所有的EmbeddedServletContainerCustomizer,然后执行其customize

使用外置的Servlet容器
嵌入式Servlet容器缺点:默认不支持jsp、优化定制比较复杂。

外置的Servlet容器:外面安装Tomcat,应用WAR包形式
1、创建一个一个WAR项目(创建好相应的目录)
2、将嵌入式的Tomcat指定为Provided

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>

3、必须编写一个SpringBootServletInitializer的实现类,并调用configure方法

public class ServletInitializer extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        //将SpringBoot的主程序传入
        return application.sources(SpringbootApplication.class);
    }

}

在这里插入图片描述

Servlet3.0规范定义了相应的规则:
1、服务器启动(文本应用启动)会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例
2、ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类全类名,SpringBoot是放在web模板下的jar包
在这里插入图片描述

3、还可以使用@HandlesTypes,在应用启动的时候加载我们感兴趣的类。

启动流程:
1、启动Tomcat
2、org/springframework/spring-web/5.1.9.RELEASE/spring-web-5.1.9.RELEASE.jar!/META-INF/services/javax.servlet.ServletContainerInitializer,加载该文件中定义的org.springframework.web.SpringServletContainerInitializer类,@HandlesTypes({WebApplicationInitializer.class})标注的所有这个类型的类传入到onStartup方法 Set<Class<?>> webAppInitializerClasses参数中,并在方法中创建其实例。
3、每一个WebApplicationInitializer都会调用自己的onStartup方法,其实现类如下
在这里插入图片描述
而我们创建的war工程就是实现了SpringBootServletInitializer,所以会执行其onStartup方法,在该方法中会创建SpringBoot的应用程序,而configure方法是为了告诉SpringBoot的主程序在什么地方。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot 是一个快速构建基于 Spring 框架的应用程序的工具。它为 Spring 应用程序开发提供了一种简单的方法,无需繁琐地配置 XML,只需要使用注解即可实现常见的业务逻辑。 下面是一个基本的 Spring Boot Web 应用程序的步骤: 1. 创建一个 Maven 项目,并添加 Spring Boot 的依赖。 2. 创建一个 Controller 类,并添加处理请求的方法。 3. 配置应用程序的入口点,并启动应用程序。 以下是一个简单的示例: 1. 创建 Maven 项目 使用 Maven 创建一个新的项目,可以参考以下命令: ``` mvn archetype:generate -DgroupId=com.example -DartifactId=webapp -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false ``` 2. 添加 Spring Boot 依赖 在 pom.xml 文件中添加 Spring Boot Starter Web 依赖: ``` <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> ``` 3. 创建 Controller 类 创建一个 HomeController 类,并添加处理请求的方法: ``` @RestController public class HomeController { @GetMapping("/") public String home() { return "Hello, World!"; } } ``` 4. 配置应用程序的入口点 创建一个 SpringBootWebApplication 类,并使用 @SpringBootApplication 注解标记为应用程序的入口点: ``` @SpringBootApplication public class SpringBootWebApplication { public static void main(String[] args) { SpringApplication.run(SpringBootWebApplication.class, args); } } ``` 5. 启动应用程序 使用以下命令启动应用程序: ``` mvn spring-boot:run ``` 在浏览器中访问 http://localhost:8080/ ,即可看到 "Hello, World!"。 这就是一个简单的 Spring Boot Web 应用程序的开发过程。当然,除了以上步骤,还有很多其他的配置和实现方式,具体可以参考官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值