servlet的多种注册方式

基于 servlet3.0以及以上版本

注册Servlet的多种方式

web.xml

metadata-complete

metadata-complete=true ,表示只使用 web.xml,不读取annotation 以及Web fragment

编写Servlet
public class XmlServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("<h1> XML servlet </h1>");
    }
}
注册Servlet
  <?xml version="1.0" encoding="UTF-8"?>
 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                  http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
      id="WebApp_9527" version="3.1" metadata-complete="false">
    <!-- metadata-complete=true ,表示只使用 web.xml,不读取annotation 以及Web fragment-->

  <display-name>servlet3.x</display-name>
  <servlet>
    <servlet-name>xmlServlet</servlet-name>
    <servlet-class>cn.jhs.XmlServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>xmlServlet</servlet-name>
    <url-pattern>/xml</url-pattern>
  </servlet-mapping>
</web-app>

结果

在这里插入图片描述


Annotation

servlet3.0之后支持@WebServlet,@WebFilter,@WebListener ,通过注解的方式来实现servlet/filter/listener的注册。

编写Servlet2
@WebServlet(name = "annoServlet",value = "/anno")
public class AnnoServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("<h1> ANNO servlet </h1>");
    }
}
结果

无需在web.xml(注册)。
在这里插入图片描述

若存在web.xml 且metadata-complete="true",则会使得注解不生效.


SPI - javax.servlet.ServletContainerInitializer

定义接口 Page 以及 实现类
public interface Page {
    String getPageInfo(); //具体的处理逻辑
    String getPath(); //定义path,类似 urlmapping-pattern
}

///----//
public class PageOne  implements Page {
    @Override
    public String getPageInfo() {
        return "page one info";
    }
    @Override
    public String getPath() {
        return "/pageOne";
    }
}

///----//
public class PageTwo implements Page {
    @Override
    public String getPageInfo() {
        return "model two info";
    }
    @Override
    public String getPath() {
        return "/pageTwo";
    }
}
定义Controller
public class PageController extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp)
            throws ServletException, IOException {

        resp.setContentType("text/html");
        Page page = resolvePage(req);
        if (page != null) {
            resp.getWriter().write("Page info: " + page.getPageInfo() + "<br/>");
        }
    }

    //根据路径,来确定具体的Page
    private Page resolvePage(HttpServletRequest req) {
        String path = req.getRequestURI().substring(req.getContextPath().length());
        Object obj = req.getServletContext().getAttribute("pages");
        if (obj instanceof List) {
            Optional<Page> first = ((List<Page>) obj).stream()
                    .filter(p -> path.equals(p.getPath()))
                    .findFirst();

            if (first.isPresent()) {
                return first.get();
            }
        }
        return null;
    }
}

该servlet没有带有注解,也没有在web.xml中注册!

自定义ServletContainerInitializer

通过自定义ServletContainerInitializer,实现servlet的动态注册

//实现类必须使用: javax.servlet.annotation.HandlesTypes 注解
@HandlesTypes({Page.class})  //classload查找所有实现Page.class的类
public class MyInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> pageClasses, ServletContext ctx) throws ServletException {
        List<Page> pages = new ArrayList<>();
        if (pageClasses != null) {
            for (Class<?> pageClass : pageClasses) {
                if (!pageClass.isInterface() && !Modifier.isAbstract(pageClass.getModifiers())) {
                    try {
                        Page page = (Page) pageClass.newInstance();
                        pages.add(page);
                    } catch (Throwable ex) {
                        throw new ServletException( "Failed to instantiate WebApplicationInitializer class", ex);
                    }
                }
            }
        }
        if (pages.size() > 0) {
            ctx.setAttribute("pages", pages);
            ServletRegistration.Dynamic servletRegistration = ctx.addServlet("pageController", PageController.class);
            pages.forEach(p -> {
                //为 PageServlet.class 添加mappings ,多个
                servletRegistration.addMapping(p.getPath());
            });
        }
    }
}
SPI

新建META-INF/services/javax.servlet.ServletContainerInitializer文件,内容为:

cn.jhs.framework.MyInitializer
结果

在这里插入图片描述


web fragment

为了给开发人员更好的可插拔性和更少的配置,在这个版本(Servlet 3.0)的规范中,我们引入了web模块部署描述符片段(web fragment)的概念。web fragment是web.xml的部分或全部,可以在一个类库或框架 jar包的META-INF/web-fragment.xml
web fragment是web应用的一个逻辑分区,描述符的顶级元素必须是web-fragment且对应的描述符文件必须被称为web-fragment.xml.

定义fragment.jar

1. 定义servlet

public class HiServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("<h2> hi:" + LocalDateTime.now() + " <h2>");
    }
}

2.创建META-INF/fragment.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-fragment version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd">

  <servlet>
    <servlet-name>HiServlet</servlet-name>
    <servlet-class>cn.jhs.HiServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>HiServlet</servlet-name>
    <url-pattern>/hi</url-pattern>
  </servlet-mapping>

</web-fragment>

3.打包
fragment.jar的maven坐标为:

    <groupId>cn.jhs</groupId>
    <artifactId>servlet3x_web_fragment</artifactId>
    <version>1.0-SNAPSHOT</version>

执行mvn install

引入fragment.jar

pom依赖中增加fragment.jar的maven坐标即可。

执行

在这里插入图片描述

Listener动态添加

@WebListener
public class ServletContextLoaderListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletRegistration.Dynamic dynamic =  sce.getServletContext().addServlet("innerServlet", new HttpServlet() {
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                resp.getWriter().write("<h1> inner servlet </h1>");
            }
        });

        dynamic.addMapping("/inner");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {

    }
}


执行顺序

web.xml和web-fragment.xml 注册顺序

由于规范允许应用配置由多个配置文件组成(web.xml 和 web-fragment.xml)的资源,从应用中多个不同位置发现和加载,顺序问题必须被解决。

web-fragment.xml有一个顶级元素<name></name>属性,且在一个web-fragment.xml中仅能有一个<name>元素。

绝对顺序

web.xml中的<absolute-ordering>元素。在一个web.xml中仅能有一个<absolute-ordering>元素

    <absolute-ordering>
        <name>fragment1</name>
        <name>fragment3</name>
        <others/>
    </absolute-ordering>
  1. web.xml和WEB-INF/classes 必须在列在absolute-ordering元素中的所有web-fragment之前处理.
  2. 按照<absolute-ordering><name>...</name></absolute-ordering><name>绝对顺序执行
    • 当遍历<absolute-ordering>子元素,遇到多个子元素具有相同<name>元素,只需考虑首次出现的。
  3. <absolute-ordering>可以包含零个或一个<others />元素。
    • 如果没有<others/>元素,没有在<name />明确提到的web-fragment必须被忽略
相对顺序

web-fragment.xml中的<ordering>元素,一个web-fragment.xml只能有一个<ordering>元素。

  <name>fragment1</name>
  <ordering>
    <after><name>fragment2</name></after>
    <before><others/></before>
  </ordering>

对于一个应用Listener、Servlet和Filter的调用顺序是很重要的!

Filter执行顺序

匹配请求的Filter链的顺序是它们在web.xml中声明的顺序
如果是注解的Filter,按照 filter-name 自然顺序执行。

filter-name 不要带有如- * 之类的符号, 最好是英文字母+数字组合。

Servlet匹配优先级

精准匹配
一个url匹配到多个Servlet时,按照最精准匹配的方式选择Servlet.

    <servlet-mapping>
        <servlet-name>xmlServlet</servlet-name>
        <url-pattern>/p/*</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>annoServlet</servlet-name>
        <url-pattern>/p/anno</url-pattern>
    </servlet-mapping>

当访问$IP:PORT/CONTEXT_PATH$/p/anno时,执行的是annoServlet逻辑。

Servlet 的load-on-startup 元素表示的顺序实例化。

Listener执行顺序

在Servlet3.0,Listener以它们在web.xml中声明的顺序调用,如下所示:

  1. javax.servlet.ServletContextListener实现的contextInitialized方法以声明时顺序调用,contextDestroyed 以相反顺序调用。
  2. javax.servlet.ServletRequestListener实现的requestInitialized 以声明时顺序调用,requestDestroyed 方法以相反顺序调用。
  3. javax.servlet.http.HttpSessionListener 实现的sessionCreated方法以声明时顺序调用,sessionDestroyed 方法以相反顺序调用。
  4. 其他任何Listener接口的调用顺序是未指定的。


springboot 自动注入servlet原理

https://blog.csdn.net/it_freshman/article/details/126012413

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值