Todo lo expuesto hasta ahora sobre servlets corresponde a la versión 2.4 del API, una de las más populares debido a que era la versión vigente en los años que se popularizó el desarrollo de aplicaciones web.

La versión 2.5 que apareció posteriormente introdujo algunos cambios menores en el descriptor de despliegue y en el API, pero sobre todo la necesidad de utilizar Java 5, ya que obligaba a los servidores web a tener en cuenta la anotaciones que pudieran tener declaradas las clases. La posibilidad de inyectar metainformación directamente en las clases aportaba la ventaja de que el código y la configuración estuvieran en un mismo lugar en vez de en ficheros separados.

Las anotaciones que se introdujeron en aquel entonces trataban de permitir a una aplicación web acceder fácilmente a toda la gama de recursos disponibles dentro de un contenedor J2EE de la versión 5 de Java, como por ejemplo @PersistenceContext, @PersistenceUnit, @WebServiceRef, @EJB o @Resource. Así como de controlar de una forma más sencilla el ciclo de vida de los servlets con @PostConstruct y @PreDestroy.

La versión 3.0 actual añade sobre todo más anotaciones, la introducción del concepto de web fragments, la ejecución asíncrona de servlets, y un nuevo API que posibilita crear servlets, filtros y listeners en tiempo de ejecución por parte de una aplicación, aunque esto último más orientado a ser usado por frameworks.

Configuración
Para utilizar el nuevo API hay que sustituir la dependencia original con la versión 2.4 del fichero pom.xml de Maven por la siguiente:

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.0.1</version>
  <scope>provided</scope>
</dependency>

Y utilizar la etiqueta metadata-complete en el descriptor de despliegue para indicar al servidor web que es necesario que revise las clases contenidas dentro de un war en busca de anotaciones:

<web-app ...
         id="WebApp_ID"
         version="3.0"
         metadata-complete="false">
...

Anotaciones
Una vez realizados los cambios se puede empezar a utilizar las nuevas anotaciones, como @WebServlet, @WebFilter y @WebListener, que eliminan la necesidad de configurar el fichero web.xml, ya que la configuración se realiza directamente sobre las clases:

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
...
@WebFilter("/*")
public class HelloFilter implements Filter {
...
@WebListener
public class HelloListener implements ServletContextListener {
...

Web Fragments
Otro de los cambios realizados en la nueva versión de la especificación es la posibilidad de que las librerías incluidas dentro del directorio libs de un war definan sus propios servlets, filtros, o listeners, mediante un fichero de configuración local llamado web-fragment.xml que debe encontrarse dentro de su directorio META-INF, evitando así tener que polucionar el fichero web.xml global.

Sevlets Asíncronos
La posibilidad de ejecutar servlets de forma asíncrona se consigue añadiendo el parámetro asyncSupported en la configuración del servlet, o filtro, que se quiera poder ejecutar asíncronamente, y usando una instancia de una nueva clase AsyncContext que puede obtenerse llamando al método startAsync del objeto petición.

Al indicar que una petición se va a ejecutar de forma asíncrona se puede lanzar un thread y terminar el proceso del servlet. El cliente se quedará esperando de la misma forma que si el servlet siguiera ejecutándose. La diferencia es que el servidor web consumirá menos recursos ya que será el nuevo thread el que se quede esperando, en vez de el del servlet impidiendo que atienda nuevas peticiones. Lógicamente el thread lanzado debe ser un objeto de la clase Runnable, y para que pueda terminar el proceso de la petición original se le debe pasar una referencia a la instancia del objeto AsyncContext. Cuando el thread termine su proceso llamará al método complete si se encarga además de generar la respuesta, o al método forward para delegar en otro servlet.

@WebServlet(value="/hello", asyncSupported=true)
public class HelloServlet extends HttpServlet {

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        AsyncContext async = request.startAsync();

        HelloRunnable runnable = new HelloRunnable(async);

        new Thread(runnable).start();
    }

    public class HelloRunnable implements Runnable {
        private AsyncContext async;

        public HelloRunnable(AsyncContext async) {
            this.async = async;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(5 * 1000);

                PrintWriter out = async.getResponse().getWriter();
                out.write("<html><head></head><body>Hello Servlet!</body></html>");

                async.complete();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

Un detalle muy importante a tener en cuenta es que todos los filtros y servlets ejecutados dentro de la cadena por la que pasa una petición deben soportar la ejecución asíncrona. Si alguno no la soporta se eleva una excepción de tipo IllegalStateException.