Java Web & Servlets Fundamentals

ServletContext & Init Parameters

18 min Lesson 8 of 13

ServletContext & Init Parameters

Every servlet-based application runs inside a single web application context — a shared runtime environment managed by the servlet container. The object that represents this environment is ServletContext. Understanding it is the key to wiring together application-wide configuration, sharing objects across servlets, and avoiding the tight coupling that comes from hardcoding values inside servlet classes.

What Is ServletContext?

When the container (Tomcat, Jetty, WildFly, etc.) starts your web application, it creates exactly one ServletContext instance for that application. All servlets, filters, and listeners in the same .war file share this single instance. Think of it as the application's global memory and configuration store.

You obtain it from inside any servlet with:

ServletContext ctx = getServletContext(); // inside HttpServlet

Or from a ServletConfig object:

ServletContext ctx = getServletConfig().getServletContext();
One context per application, one application per deployment unit. If you deploy two .war files, they each get their own ServletContext and cannot see each other's attributes directly. This is an intentional isolation boundary.

Context Init Parameters

Context init parameters are name-value pairs declared in web.xml (or its annotation-based equivalent) that are visible to every servlet and filter in the application. They are the right place for application-wide configuration that should not be hardcoded — database URLs, API base addresses, feature flags, environment names.

Declare them in web.xml:

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" version="6.0"> <context-param> <param-name>app.env</param-name> <param-value>production</param-value> </context-param> <context-param> <param-name>db.url</param-name> <param-value>jdbc:postgresql://db-host:5432/shop</param-value> </context-param> </web-app>

Read them from any servlet:

import jakarta.servlet.ServletContext; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/info") public class InfoServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { ServletContext ctx = getServletContext(); String env = ctx.getInitParameter("app.env"); // "production" String dbUrl = ctx.getInitParameter("db.url"); resp.setContentType("text/plain"); resp.getWriter().printf("ENV=%s DB=%s%n", env, dbUrl); } }

getInitParameter(String name) returns null if the name does not exist, so always guard against that in production code.

Servlet Init Parameters

Sometimes a parameter makes sense only for a single servlet — a per-servlet timeout, a template path, a page size. Declare these as servlet init parameters, either in web.xml inside a <servlet> element or via the @WebServlet annotation's initParams attribute.

Using the annotation (Jakarta EE 9+):

import jakarta.servlet.annotation.WebInitParam; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet( urlPatterns = "/products", initParams = { @WebInitParam(name = "pageSize", value = "25"), @WebInitParam(name = "sortDefault", value = "name") } ) public class ProductListServlet extends HttpServlet { private int pageSize; private String sortDefault; @Override public void init() { pageSize = Integer.parseInt(getInitParameter("pageSize")); sortDefault = getInitParameter("sortDefault"); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.getWriter().printf( "Listing products — page size: %d, default sort: %s%n", pageSize, sortDefault ); } }

Notice the pattern: read init parameters once in init() and store them in instance fields. Calling getInitParameter() on every request is legal but wasteful; the values never change after deployment.

Prefer init() for one-time setup. Parse, validate, and cache init parameters there. If a required parameter is missing or malformed, throw a ServletException — the container will refuse to bring the servlet online, making the misconfiguration immediately visible rather than silently blowing up at request time.

Context Attributes — Sharing Live Objects

Init parameters carry only String values read from configuration files. For sharing live application objects (a connection pool, a service registry, a parsed configuration object) across servlets, use context attributes. These are arbitrary Object references stored under a string key.

// Stored once — typically in a ServletContextListener import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; import jakarta.servlet.annotation.WebListener; import javax.sql.DataSource; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; @WebListener public class AppStartupListener implements ServletContextListener { private HikariDataSource pool; @Override public void contextInitialized(ServletContextEvent sce) { HikariConfig cfg = new HikariConfig(); cfg.setJdbcUrl(sce.getServletContext().getInitParameter("db.url")); cfg.setMaximumPoolSize(10); pool = new HikariDataSource(cfg); // share it with every servlet sce.getServletContext().setAttribute("dataSource", pool); } @Override public void contextDestroyed(ServletContextEvent sce) { if (pool != null) pool.close(); // release connections on shutdown } }

Any servlet can then retrieve the shared pool:

DataSource ds = (DataSource) getServletContext().getAttribute("dataSource"); try (Connection conn = ds.getConnection()) { // ... }
Context attributes are shared across all threads. Every concurrent request runs in a different thread but they all read the same ServletContext. Store only thread-safe, immutable, or fully synchronized objects as context attributes — a DataSource is safe; a plain ArrayList is not.

Context vs Servlet Init Parameters — Decision Guide

  • Context init parameter (<context-param>): configuration shared by the whole application — database URL, environment name, default locale.
  • Servlet init parameter (@WebInitParam / <init-param>): configuration that is specific to one servlet and irrelevant to others — page size, template name, timeout.
  • Context attribute (setAttribute): live objects that need to be shared across servlets at runtime — connection pools, service instances, application-level caches.

Practical Logging with ServletContext

ServletContext also exposes a basic logging method that writes to the container's log file — useful for startup diagnostics:

@Override public void init() throws jakarta.servlet.ServletException { String env = getServletContext().getInitParameter("app.env"); getServletContext().log("ProductListServlet starting in env: " + env); }

For production applications you will replace this with a proper logging framework (SLF4J + Logback), but during development it is a quick way to confirm that parameters are being read correctly.

Summary

ServletContext is the application-wide shared environment — one instance per deployed .war. Use context init parameters (<context-param>) for application-wide string configuration, servlet init parameters (@WebInitParam) for per-servlet configuration, and context attributes (setAttribute / getAttribute) to share live thread-safe objects across servlets. Reading init parameters once in init() and caching them in fields is the standard, efficient pattern. In the next lesson you will use request forwarding and including to compose servlet responses.