servlet 3.0使用和原理

Servlet 3.0 起步

Servlet 3.0新增了若干注解,用于简化Servlet、过滤器和监听器的声明,使得web.xml部署描述文件从该版本开始不再是必选的。
好了,我们先开始体验一下Servlet 3.0的新特性吧,我们新建一个web3的maven项目,项目的目录结构如图:

在POM文件中配置需要依赖的jar包,如图:

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

        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
        </dependency>
    </dependencies>

编写我们的第一个servlet程序HelloWorldServlet,重写HttpServlet的doGet 和 doPost方法,并在HelloServlet类上添加@WebServlet 注解。

/**
 * 注解版Servlet 3.0
 */
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //这句话的意思,是让浏览器用utf8来解析返回的数据
        resp.setHeader("Content-type", "text/html;charset=UTF-8");
        //这句话的意思,是告诉servlet用UTF-8转码,而不是用默认的ISO8859
        resp.setCharacterEncoding("UTF-8");
        resp.getWriter().write("Hello , Servlet 3.0 !");
    }

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

AuthFilter 过滤器代码实现Filter接口,在AuthFilter类上添加@WebFilter注解,其中urlPatterns表示需要过滤的路径地址,asyncSupported表示是否支持异步。

@WebFilter(urlPatterns = {"/*"}, asyncSupported = true)
public class AuthFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("AuthFilter init ");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        String name = servletRequest.getParameter("name");

        if (Objects.equals("yangyanping", name)) {
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            servletResponse.setCharacterEncoding("UTF-8");
            servletResponse.setContentType("text/html;charset=utf-8");
            servletResponse.getWriter().write("没有权限访问");
        }
    }

    @Override
    public void destroy() {
        System.out.println("AuthFilter destroy ");
    }
}

MyListener 监听器类,需要在类上添加@WebListener注解,并实现ServletContextListener接口。

@WebListener
public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("MyListener contextInitialized");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("MyListener contextDestroyed");
    }
}

观察系统启动日志,我们发现 先加载的是MyListener的contextInitialized方法,然后加载AuthFilter的init 方法。

MyListener contextInitialized
AuthFilter init 

启动程序,在浏览器中输入 http://localhost:8080/hello?name=yangyanping 运行效果如图:
在这里插入图片描述

Servlet 3.0 异步

异步处理支持。有了该特性,Servlet线程不再需要一直阻塞,直到业务处理完毕才能输出响应,最后才结束该Servlet。在接收到请求之后,Servlet线程可以将耗时的操作委托给另一个线程来完成,自己在不生成响应的情况下返回至容器。针对业务处理较耗时的情况,将大大减少服务器资源的占用,并且提高并发处理速度。

@WebServlet(value = "/order", asyncSupported = true)
public class AsyncOrderServlet extends javax.servlet.http.HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("主" + Thread.currentThread().getName() + "  start  !");

        resp.setHeader("Content-type","text/html;charset=UTF-8");
        req.startAsync();
        AsyncContext asyncContext = req.getAsyncContext();

        asyncContext.start(() -> {
            System.out.println("子" + Thread.currentThread().getName() + "  start  !");
            try {
                Thread.sleep(5000);
                ServletResponse servletResponse = asyncContext.getResponse();
                servletResponse.setCharacterEncoding("UTF-8");
                servletResponse.getWriter().write("Hello ! Async Servlet 。");
            }catch (Exception ex){
            }
            System.out.println("子" + Thread.currentThread().getName() + "  start  !");
            asyncContext.complete();

        });
        System.out.println("主" + Thread.currentThread().getName() + "  end  !");
    }
}         

启动程序,在浏览器中输入 http://localhost:8080/order?name=yangyanping 运行效果如图:
在这里插入图片描述

SPI原理分析

SPI的全名为Service Provider Interface。
它的约定:

  1. 在META-INF/services/目录中创建以接口全限定名命名的文件该文件内容为Api具体实现类的全限定名
  2. 使用ServiceLoader类动态加载META-INF中的实现类
  3. 如SPI的实现类为Jar则需要放在主程序classPath中
  4. Api具体实现类必须有一个不带参数的构造方法

我们可以在jar包的META-INF/services/目录下创建一个以服务接口命名的文件(如下图的com.example.demo.People文件)。该文件里就是实现该服务接口的具体实现类(如com.example.demo.impl.ChinaPeople)。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200420145642876.jpg)

我们定义一个People接口 和它的2个实现类(ChinaPeople,EnglishPeople),代码如下

public interface People {
    String hello();
}
public class ChinaPeople implements People {
    @Override
    public String hello() {
        return "您好,世界 !";
    }
}
public class EnglishPeople implements People {
    @Override
    public String hello() {
        return "Hello World !";
    }
}

启动类代码

public class DemoApplication {

    public static void main(String[] args) {
        ServiceLoader<People> services = ServiceLoader.load(People.class);
        Iterator<People> iterable = services.iterator();

        while (iterable.hasNext()){
            People people = (People)iterable.next();
             System.out.println(people.hello());
        }
    }
}

运行效果如图:
在这里插入图片描述
我们把com.example.demo.People文件里的内容换成com.example.demo.impl.EnglishPeople 。如图:
在这里插入图片描述
再次运行程序,看看打印的日志,如图:
在这里插入图片描述

使用SPI手写Servlet 3.0

同理容器在启动的时候(如tomcat启动),会扫描当前应用中的每一个jar包里面META-INF/services/javax.servlet.ServletContainerInitializer文件中指定的实现类,启动并运行这个实现类的方法onStartup(Set<Class<?>> set, ServletContext servletContext)。

我们使用SPI机制,自己手写一个servlet 3.0。首先实现一个ServletContainerInitializer接口的类MyServletContainerInitializer。在类上添加@HandlesTypes({OrderService.class}) 注解,关注我们的OrderService的实现类定义。

@HandlesTypes({OrderService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {

        for (Class<?> c : set) {
            System.out.println(c.getName());
        }

        ServletRegistration.Dynamic orderDynamic = servletContext.addServlet("order", HelloServlet.class);
        orderDynamic.addMapping("/order");
        orderDynamic.setLoadOnStartup(1);


        FilterRegistration.Dynamic filter = servletContext.addFilter("authFilter", AuthFilter.class);
        filter.addMappingForServletNames(EnumSet.of(DispatcherType.REQUEST), true, "userServlet");
        filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");


        servletContext.addListener(new MyListener());

        System.out.println(".....init ......");
    }
}

HttpServlet,ServletContextListener,Filter的实现类如下 (无需使用注解):

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("Hello  My Servlet !");
    }
}

public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("MyListener...contextInitialized...");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("MyListener...contextDestroyed...");
    }
}

public class AuthFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("AuthFilter.......");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void destroy() {

    }
}

定义一个OrderService接口 和 一个抽象类AbstractOrderService,实现类OrderServiceImpl

public interface OrderService {
    String create();
}

public abstract class AbstractOrderService implements OrderService {
}

public class OrderServiceImpl extends AbstractOrderService {
    @Override
    public String create() {
        return null;
    }
}

运行程序,在浏览器中访问地址:http://localhost:8080/hello
在这里插入图片描述
好了,我们自己使用SPI的机制,已经自己手写了一个servlet3.0 的HttpServlet 。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值