Servlet3.0

以前,不管是编写servlet、filter 和 Listener等等,还是编写SpringMVC的前端控制器,都是在web.xml中编写的。Servlet3.0版本之后,只需要使用注解就可以完成组件的注入,还有运行时的组件式插拔开发。

注意: servlet3.0的容器Tomcat 必须是7.0.x以上版本才能使用servlet3.0。

创建dynamic Web项目

我这里使用的是idea,大家也可以使用eclipse开发。大家估计都对eclipse开发熟悉,如果对idea创建动态web项目不熟悉的话,可以看我另一篇博客idea 创建动态Web项目

创建好工程后如下:

使用web.xml的方式

1)在src下创建com.liuzhuo.servlet包,并创建 HelloServlet(继承 HttpServlet)

public class HelloServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        super.doPost(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().write("hello world servlet3.0 ~~~");
    }
}

2) 在WBE-INF下的index.jsp中:

添加:<a href="hello">hello</a>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>servlet3.0</title>
</head>
<body>
<a href="hello">hello</a>
</body>
</html>

3) 在web.xml中配置servlet的映射关系

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.liuzhuo.servlet.HelloServlet</servlet-class>
    </servlet>

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

</web-app>

4) 运行web应用

点击hello的超链接

以上就是servlet3.0之前的版本开发web工程的演示。


不使用web.xml(servlet3.0)

1) 去掉web.xml中的servlet的配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

</web-app>

2) 此时再运行web项目,点击hello超链接

3)使用@WebServlet注解

在HelloServlet类上面写上@WebServlet注解
name : 就是servlet的名字
value:就是映射的路径

@WebServlet上的name <==> <servlet-name>hello</servlet-name>
@WebServlet上的value <==> <url-pattern>/hello</url-pattern>

@WebServlet(value ="/hello" )
public class HelloServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        super.doPost(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().write("hello world servlet3.0 ~~~");
    }
}

4) 重新启动web项目

会发现,项目启动正常了。

ps:在idea中,有一个web的窗口,在编辑器的左下角,点开,会看见当前web项目的映射信息,很方便排错。

其他注解:

@WebFilter :注解过滤器
@WebListener :注解监听器
@WebInitParam:注解初始化的参数

具体的使用情况:看官方文档即可。


servlet3.0的共享库和运行时插件

Shared libraries / runtimes pluggability

1)在Servlet容器启动的时候,会扫描当前应用里面的每一个jar包的ServletContainerInitializer的实现类

2)提供ServletContainerInitializer的实现类

必须绑定在:META-INF/services/javax.servlet.ServletContainerInitializer文件中。
文件名就是 javax.servlet.ServletContainerInitializer 没有后缀。

文件中的内容就是:ServletContainerInitializer实现类的全类名

总结:容器在启动的时候,会扫描当前应用中每一个jar包里面的:META-INF/services/javax.servlet.ServletContainerInitializer文件中ServletContainerInitializer实现类,启动并运行这个实现类中的方法和传入感兴趣的类型。

3)测试

3.1) 这里就不创建jar包了,直接在src下创建META-INF/services目录,然后在该目录下,创建javax.servlet.ServletContainerInitializer文件。

注意:目录和文件名称不要打错!!!

3.2)在src下的com.liuzhuo.servlet下创建MyServletContainerInitializer,并实现ServletContainerInitializer接口。

@HandlesTypes(value = {HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {

    /**
     * @param set:感兴趣的类型的所有子类型.@HandlesTypes注解中的value即为感兴趣的类型。
     * @param servletContext :代表当前Web应用的ServletContext,一个Web应用一个上下文
     * @throws ServletException
     */
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        System.out.println("感兴趣的类型:");
        for (Class<?> aClass : set) {
            System.out.println(aClass);
        }
    }
}

3.3) 在src下的com.liuzhuo.service下创建
HelloService:接口
HelloServiceExt:实现了HelloService的子接口
AbstractHelloService:实现了HelloService的抽象类
HelloServiceImpl:HelloService的实现类

3.4)将MyServletContainerInitializer的全类名添加到:javax.servlet.ServletContainerInitializer文件中

3.5)运行web项目

能看到,输出感兴趣的类型:

class com.liuzhuo.service.HelloServiceImpl
interface com.liuzhuo.service.HelloServiceExt
class com.liuzhuo.service.AbstractHelloService

注意感兴趣的类型:不包括本身(HelloService接口)!!!


使用ServletContainerInitializer给容器添加组件

根据上文,我们已经了解到了,我们使用@WebServlet注解,给容器添加我们自己写的Servlet类,但是无法添加第三方的组件。只能使用Web.xml文件来添加。

现在,我们了解了Servlet3.0的共享库和运行时机制,可以使用ServletContainerInitializer的机制来注册Web组件(Servlet、Filter、Listener),不必使用Web.xml配置文件。

接着现有的项目继续开发。

1)在com.liuzhuo.servlet下创建UserServlet、UserFilter、UserListener三大组件

UserServlet:

public class UserServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().write("UserServlet ·····");
    }
}

UserFilter:

public class UserFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //执行过滤的方法
        System.out.println("UserFilter ~~~");
        //放行
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }

}

UserListener:

public class UserListener implements ServletContextListener {

    //容器初始化的时候
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        //获取ServletContext容器的上下文,这里也可以注册Servlet、Fliter、Listener组件
        ServletContext servletContext = servletContextEvent.getServletContext();
        System.out.println(servletContext);
        System.out.println("UserListener监听ServletContextListener的初始化");
    }

    //容器销毁的时候
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("UserListener监听ServletContextListener的销毁");
    }
}

2) 在MyServletContainerInitializer中的onStartup方法中使用ServletContext注册三大组件:

@HandlesTypes(value = {HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {

    /**
     * @param set:感兴趣的类型的所有子类型.@HandlesTypes注解中的value即为感兴趣的类型。
     * @param servletContext :代表当前Web应用的ServletContext:一个Web应用一个上下文
     * @throws ServletException
     */
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        System.out.println("感兴趣的类型:");
        for (Class<?> aClass : set) {
            System.out.println(aClass);
        }

        //添加userServelt组件。
        ServletRegistration.Dynamic userServelt = servletContext.addServlet("userServelt", new UserServlet());
        //添加Servlet的映射路径
        userServelt.addMapping("/user");

        //添加监听器
        servletContext.addListener(UserListener.class);

        //添加过滤器
        FilterRegistration.Dynamic userFilter = servletContext.addFilter("userFilter", UserFilter.class);
        //添加拦截规则
        //EnumSet<DispatcherType> var1 :拦截的请求类型
        // boolean var2                :true
        // String... var3              :拦截的路径
        userFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
    }
}

3) 运行Web项目

说明userListener监听器注册成功。

在浏览器中输入:http://localhost:8080/user

说明UserServlet和userFliter注册也成功了。

停止Web项目:

以上,就是不使用Web.xml的注册Servlet、Filter、Listener三大组件的过程,使用硬编码的形式。

注意:servlet3.0动态注册,只能在webapp启动时进行注册,可能是为了安全考虑吧.不能在运行时完成对servlet的注册和销毁

在初始化情况下的注册Servlet组件有除了上面的方式,还有另外一种方式:
就是在实现ServletContextListener接口 , 在contextInitialized方法中完成注册.

就是在上述UserListener中contextInitialized方法中获取:
ServletContext servletContext = servletContextEvent.getServletContext();
然后进行三大组件的注册。


  转载请注明: 解忧杂货店 Servlet3.0

  目录