1. 概述

本文将全面介绍在Jakarta EE和Spring Boot中注册Servlet的方法。我们将重点探讨两种在Jakarta EE中注册Java Servlet的方式:一种使用web.xml文件,另一种使用注解。随后,我们将通过XML配置、Java配置和可配置属性三种方式在Spring Boot中注册Servlet。

关于Servlet的基础入门知识,可参考此文

2. 在Jakarta EE中注册Servlet

我们来看两种在Jakarta EE中注册Servlet的方法:通过web.xml或使用Jakarta EE的*@WebServlet*注解。

2.1. 通过web.xml

在Jakarta EE应用中注册Servlet最常见的方式是将其添加到web.xml文件:

<welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
    <servlet-name>Example</servlet-name>
    <servlet-class>com.baeldung.Example</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>Example</servlet-name>
    <url-pattern>/Example</url-pattern>
</servlet-mapping>

如上所示,这需要两个步骤:

  1. servlet标签中添加Servlet,并指定其所在类的完整路径
  2. url-pattern标签中指定Servlet暴露的URL路径

Jakarta EE的web.xml文件通常位于WebContent/WEB-INF目录下。

2.2. 通过注解

现在我们使用*@WebServlet注解直接在自定义Servlet类上注册。这种方式无需在server.xml中配置Servlet映射,也无需在web.xml*中注册:

@WebServlet(
  name = "AnnotationExample",
  description = "Example Servlet Using Annotations",
  urlPatterns = {"/AnnotationExample"}
)
public class Example extends HttpServlet {    
 
    @Override
    protected void doGet(
      HttpServletRequest request, 
      HttpServletResponse response) throws ServletException, IOException {
 
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<p>Hello World!</p>");
    }
}

上述代码展示了如何直接在Servlet上添加注解。该Servlet仍将在与之前相同的URL路径下可用。

3. 在Spring Boot中注册Servlet

了解了Jakarta EE中的Servlet注册方式后,我们来看看在Spring Boot应用中注册Servlet的几种方法。

3.1. 编程式注册

Spring Boot支持100%编程式配置Web应用。

首先,我们实现WebApplicationInitializer接口,然后实现WebMvcConfigurer接口。后者允许你覆盖预设默认值,而无需指定每个配置项,节省时间并直接使用经过验证的默认设置。

来看一个WebApplicationInitializer的实现示例:

public class WebAppInitializer implements WebApplicationInitializer {
 
    public void onStartup(ServletContext container) throws ServletException {
        AnnotationConfigWebApplicationContext ctx
          = new AnnotationConfigWebApplicationContext();
        ctx.register(WebMvcConfigure.class);
        ctx.setServletContext(container);

        ServletRegistration.Dynamic servlet = container.addServlet(
          "dispatcherExample", new DispatcherServlet(ctx));
        servlet.setLoadOnStartup(1);
        servlet.addMapping("/");
     }
}

接下来实现WebMvcConfigurer接口:

@Configuration
public class WebMvcConfigure implements WebMvcConfigurer {

    @Bean
    public ViewResolver getViewResolver() {
        InternalResourceViewResolver resolver
          = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/");
        resolver.setSuffix(".jsp");
        return resolver;
    }

    @Override
    public void configureDefaultServletHandling(
      DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
          .addResourceLocations("/resources/").setCachePeriod(3600)
          .resourceChain(true).addResolver(new PathResourceResolver());
    }
}

上面我们显式指定了JSP Servlet的一些默认设置,以支持*.jsp*视图和静态资源服务。

3.2. XML配置

在Spring Boot中配置和注册Servlet的另一种方式是通过web.xml

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/dispatcher.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

Spring中使用的web.xml与Jakarta EE中的类似。如上所示,我们在servlet标签下通过属性指定了更多参数。

这里我们使用另一个XML文件完成配置:

<beans ...>
    
    <context:component-scan base-package="com.baeldung"/>

    <bean 
      class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

记住,Spring的web.xml通常位于src/main/webapp/WEB-INF目录。

3.3. 混合XML与编程式注册

让我们将XML配置方式与Spring的编程式配置结合起来:

public void onStartup(ServletContext container) throws ServletException {
   XmlWebApplicationContext xctx = new XmlWebApplicationContext();
   xctx.setConfigLocation('classpath:/context.xml');
   xctx.setServletContext(container);

   ServletRegistration.Dynamic servlet = container.addServlet(
     "dispatcher", new DispatcherServlet(ctx));
   servlet.setLoadOnStartup(1);
   servlet.addMapping("/");
}

同时配置Dispatcher Servlet:

<beans ...>

    <context:component-scan base-package="com.baeldung"/>
    <bean class="com.baeldung.configuration.WebAppInitializer"/>
</beans>

3.4. 通过Bean注册

我们还可以使用ServletRegistrationBean以编程方式配置和注册Servlet。下面我们用它来注册一个HttpServlet(实现了javax.servlet.Servlet接口):

@Bean
public ServletRegistrationBean exampleServletBean() {
    ServletRegistrationBean bean = new ServletRegistrationBean(
      new CustomServlet(), "/exampleServlet/*");
    bean.setLoadOnStartup(1);
    return bean;
}

这种方式的主要优势是: ✅ 可以向Spring应用添加多个Servlet ✅ 支持添加不同类型的Servlet

与3.1节中WebApplicationInitializer编程式配置常用的DispatcherServlet(一种更特殊的HttpServlet)不同,这里我们使用更简单的HttpServlet子类实例,它通过四个函数暴露基本的HttpRequest操作:*doGet()doPost()doPut()doDelete()*,就像在Jakarta EE中一样。

注意:HttpServlet是抽象类(不能直接实例化)。不过我们可以轻松创建自定义扩展:

public class CustomServlet extends HttpServlet{
    ...
}

4. 通过属性注册Servlet

另一种不太常见但可行的方式是使用自定义属性文件(通过PropertyLoaderPropertySourcePropertySources加载)来配置和注册Servlet。

这种方式提供了中间层配置能力,可以自定义application.properties(该文件对非嵌入式Servlet的直接配置支持有限)。

4.1. 系统属性方式

我们可以向application.properties或其他属性文件添加自定义设置。下面添加几个配置DispatcherServlet的设置:

servlet.name=dispatcherExample
servlet.mapping=/dispatcherExampleURL

将自定义属性加载到应用中:

System.setProperty("custom.config.location", "classpath:custom.properties");

现在可以通过以下方式访问这些属性:

System.getProperty("custom.config.location");

4.2. 自定义属性方式

custom.properties文件开始:

servlet.name=dispatcherExample
servlet.mapping=/dispatcherExampleURL

使用标准的属性加载器:

public Properties getProperties(String file) throws IOException {
  Properties prop = new Properties();
  InputStream input = null;
  input = getClass().getResourceAsStream(file);
  prop.load(input);
  if (input != null) {
      input.close();
  }
  return prop;
}

将这些自定义属性作为常量添加到WebApplicationInitializer实现中:

private static final PropertyLoader pl = new PropertyLoader(); 
private static final Properties springProps
  = pl.getProperties("custom_spring.properties"); 

public static final String SERVLET_NAME
  = springProps.getProperty("servlet.name"); 
public static final String SERVLET_MAPPING
  = springProps.getProperty("servlet.mapping");

然后可以用它们配置Dispatcher Servlet:

ServletRegistration.Dynamic servlet = container.addServlet(
  SERVLET_NAME, new DispatcherServlet(ctx));
servlet.setLoadOnStartup(1);
servlet.addMapping(SERVLET_MAPPING);

这种方式的优势: ✅ 无需维护*.xml*文件 ✅ 配置设置易于修改,无需重新部署代码库

4.3. PropertySource方式

更快捷的方式是使用Spring的PropertySource,它允许访问和加载配置文件。

PropertyResolverConfigurableEnvironment实现的接口,使应用属性在Servlet启动和初始化时可用:

@Configuration 
@PropertySource("classpath:/com/yourapp/custom.properties") 
public class ExampleCustomConfig { 
    @Autowired 
    ConfigurableEnvironment env; 

    public String getProperty(String key) { 
        return env.getProperty(key); 
    } 
}

上面我们通过依赖注入指定了自定义属性文件的位置。然后可以通过调用*getProperty()*方法并传入键名来获取相关属性值。

4.4. PropertySource编程式方式

我们可以将上述获取属性值的方式与下面这种编程式指定值的方式结合起来:

ConfigurableEnvironment env = new StandardEnvironment(); 
MutablePropertySources props = env.getPropertySources(); 
Map map = new HashMap(); map.put("key", "value"); 
props.addFirst(new MapPropertySource("Map", map));

我们创建了一个将键映射到值的Map,然后将其添加到PropertySources中,以便按需调用。

5. 注册嵌入式Servlet

最后,我们来看看在Spring Boot中配置和注册嵌入式Servlet的基础方法。

嵌入式Servlet提供了完整的Web容器(Tomcat、Jetty等)功能,无需单独安装或维护Web容器

你可以添加必要的依赖和配置,在任何支持此功能的地方实现简单、紧凑、快速的服务器部署。

我们仅以Tomcat为例说明,Jetty等其他容器的方法类似。

pom.xml中指定嵌入式Tomcat 8 Web容器的依赖:

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
     <artifactId>tomcat-embed-core</artifactId>
     <version>8.5.11</version>
</dependency>

添加以下标签,确保在构建时将Tomcat成功添加到Maven生成的*.war*中:

<build>
    <finalName>embeddedTomcatExample</finalName>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>appassembler-maven-plugin</artifactId>
            <version>2.0.0</version>
            <configuration>
                <assembleDirectory>target</assembleDirectory>
                <programs>
                    <program>
                        <mainClass>launch.Main</mainClass>
                        <name>webapp</name>
                    </program>
            </programs>
            </configuration>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>assemble</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

如果使用Spring Boot,可以改为在pom.xml中添加Spring的spring-boot-starter-tomcat依赖:

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

5.1. 通过属性注册

Spring Boot支持通过application.properties配置大多数Spring设置。在pom.xml中添加必要的嵌入式Servlet依赖后,可以使用以下配置选项自定义和配置嵌入式Servlet:

server.jsp-servlet.class-name=org.apache.jasper.servlet.JspServlet 
server.jsp-servlet.registered=true
server.port=8080
server.servlet-path=/

以上是一些可用于配置DispatcherServlet和静态资源共享的应用设置。嵌入式Servlet、SSL支持和会话的设置也可用。

可配置参数太多无法在此全部列出,完整列表请参考Spring Boot文档

5.2. 通过YAML配置

同样,我们可以使用YAML配置嵌入式Servlet容器。这需要使用专门的YAML属性加载器——YamlPropertySourceLoader,它暴露YAML文件中的键值对供应用使用:

YamlPropertySourceLoader sourceLoader = new YamlPropertySourceLoader();
PropertySource<?> yamlProps = sourceLoader.load("yamlProps", resource, null);

5.3. 通过TomcatEmbeddedServletContainerFactory编程式配置

通过EmbeddedServletContainerFactory的子类实例可以实现嵌入式Servlet容器的编程式配置。例如,可以使用TomcatEmbeddedServletContainerFactory配置嵌入式Tomcat Servlet。

TomcatEmbeddedServletContainerFactory包装了org.apache.catalina.startup.Tomcat对象,提供额外的配置选项:

@Bean
public ConfigurableServletWebServerFactory servletContainer() {
    TomcatServletWebServerFactory tomcatContainerFactory
      = new TomcatServletWebServerFactory();
    return tomcatContainerFactory;
}

然后可以配置返回的实例:

tomcatContainerFactory.setPort(9000);
tomcatContainerFactory.setContextPath("/springboottomcatexample");

这些特定设置都可以使用前面介绍的任何方法进行配置。

我们还可以直接访问和操作org.apache.catalina.startup.Tomcat对象:

Tomcat tomcat = new Tomcat();
tomcat.setPort(port);
tomcat.setContextPath("/springboottomcatexample");
tomcat.start();

6. 总结

本文中,我们回顾了在Jakarta EE和Spring Boot应用中注册Servlet的多种方式

本教程使用的源代码可在Github项目中获取。


原始标题:How to Register a Servlet in Java