Spring注解驱动之ServletContext注册三大组件

概述

上一讲,我们说了基于运行时插件的ServletContainerInitializer机制,这一讲我们来详细讲一下ServletContext用来注册Web组件的用法,即使用ServletContext注册Web组件。
其实就是把Servlet、Filter、Listener添加到web容器中。
为什么要使用ServletContext来注册web组件呢?因为一定会遇到如下场景:
如果是以注解的方式来注册web组件,那么前提是这些web组件是我们自己来编写的,这样我们才可以把注解加到上面。但是,如果项目中导入的是第三方jar包,它们里面是有一些组件的,比如在项目中导入阿里巴巴的连接池里面的Filter,对于这些组件而言,如果项目中有web.xml文件,那么我们就可以将它们配置在该配置文件中了,但是我们现在的项目是没有web.xml文件的,所以我们就要利用ServletContext将它们注册进来。

使用ServletContext注册web三大组件

ServletContext里面有如下方法,比如增加servlet、filter、listener等,有了这些方法,我们就可以给ServletContext里面注册一些组件了。
在这里插入图片描述

编写Servlet、Filter、Listener示例

首先,编写一个Servlet,例如UserServlet,如下所示。

package com.meimeixia.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class UserServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		resp.getWriter().write("tomcat...");
	}	
}

然后,再来编写一个Filter,例如UserFilter,要想成为一个Filter,它必须得实现Servlet提供的Filter接口。

package com.meimeixia.servlet;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class UserFilter implements Filter {

	// 销毁方法
	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
			throws IOException, ServletException {
		// 过滤请求
		System.out.println("UserFilter...doFilter方法...");
		// 放行
		arg2.doFilter(arg0, arg1);
	}

	// 初始化方法
	@Override
	public void init(FilterConfig arg0) throws ServletException {
	}
}

接着,再来编写一个Listener,例如UserListener,我们要知道监听器是有很多的,所以这儿我们不妨让UserListener来实现ServletContextListener接口,以监听ServletContext的创建和启动过程。

package com.meimeixia.servlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class UserListener implements ServletContextListener {
	// 这个方法是来监听ServletContext销毁的,也就是说,我们这个项目的停止
	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
		// TODO Auto-generated method stub
		System.out.println("UserListener...contextDestroyed...");
	}

	// 这个方法是来监听ServletContext启动初始化的
	@Override
	public void contextInitialized(ServletContextEvent arg0) {
		System.out.println("UserListener...contextInitialized...");
	}
}

下面,我们就可以直接调用ServletContext的方法来注册这些组件了。

注册Servlet

package com.meimeixia.servlet;

import java.util.Set;

import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.HandlesTypes;

import com.meimeixia.service.HelloService;

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

	/*
	 * 参数:
	 *    ServletContext sc:代表当前web应用。一个web应用就对应着一个ServletContext对象,此外,它也是我们常说的四大域对象之一,
	 *    我们给它里面存个东西,只要应用在不关闭之前,我们都可以在任何位置获取到
	 *    
	 *    Set<Class<?>> arg0:我们感兴趣的类型的所有后代类型
	 *    
	 */
	@Override
	public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
		System.out.println("我们感兴趣的所有类型:");
		// 好,我们把这些类型来遍历一下
		for (Class<?> clz : arg0) {
			System.out.println(clz);
		}	
		// 注册Servlet组件
		ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet());
	}
}

至此,我们只是给ServletContext中注册了一个UserServlet组件。但是,该UserServlet的映射信息我们还没配置呢,即它是来处理什么样的请求的。那怎么办呢?很简单,返回的ServletRegistration.Dynamic对象有一个addMapping方法,调用它即可配置UserServlet的映射信息,如下所示。

package com.meimeixia.servlet;

import java.util.Set;

import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.HandlesTypes;

import com.meimeixia.service.HelloService;

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

	/*
	 * 参数:
	 *    ServletContext sc:代表当前web应用。一个web应用就对应着一个ServletContext对象,此外,它也是我们常说的四大域对象之一,
	 *    我们给它里面存个东西,只要应用在不关闭之前,我们都可以在任何位置获取到
	 *    
	 *    Set<Class<?>> arg0:我们感兴趣的类型的所有后代类型
	 *    
	 */
	@Override
	public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
		// TODO Auto-generated method stub
		System.out.println("我们感兴趣的所有类型:");
		// 好,我们把这些类型来遍历一下
		for (Class<?> clz : arg0) {
			System.out.println(clz);
		}
		
		// 注册Servlet组件
		ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet());
		// 配置Servlet的映射信息
		servlet.addMapping("/user");
	}
}

注册Listener

package com.meimeixia.servlet;

import java.util.Set;

import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.HandlesTypes;

import com.meimeixia.service.HelloService;

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

	/*
	 * 参数:
	 *    ServletContext sc:代表当前web应用。一个web应用就对应着一个ServletContext对象,此外,它也是我们常说的四大域对象之一,
	 *    我们给它里面存个东西,只要应用在不关闭之前,我们都可以在任何位置获取到
	 *    
	 *    Set<Class<?>> arg0:我们感兴趣的类型的所有后代类型
	 *    
	 */
	@Override
	public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
		// TODO Auto-generated method stub
		System.out.println("我们感兴趣的所有类型:");
		// 好,我们把这些类型来遍历一下
		for (Class<?> clz : arg0) {
			System.out.println(clz);
		}
		
		// 注册Servlet组件
		ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet());
		// 配置Servlet的映射信息
		servlet.addMapping("/user");
		
		// 注册Listener组件
		sc.addListener(UserListener.class);
	}
}

注册Filter

现在我们来注册Filter,即UserFilter。注册Servlet和Filter有一点特殊之处,那就是注册它俩之后都得配置其映射信息。
调用第一个addFilter方法,在第一个参数的位置传入我们UserFilter的名字,例如userFilter,在第二个参数的位置传入UserFilter的类型,即UserFilter.class,这样,Servlet容器(即Tomcat服务器)就会帮我们创建出一个UserFilter对象,并将其注册到ServletContext中。
然后,我们需要利用返回的FilterRegistration.Dynamic对象中的addMappingForXxx方法配置UserFilter的映射信息。

package com.meimeixia.servlet;

import java.util.EnumSet;
import java.util.Set;

import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.HandlesTypes;

import com.meimeixia.service.HelloService;

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

	/*
	 * 参数:
	 *    ServletContext sc:代表当前web应用。一个web应用就对应着一个ServletContext对象,此外,它也是我们常说的四大域对象之一,
	 *    我们给它里面存个东西,只要应用在不关闭之前,我们都可以在任何位置获取到
	 *    
	 *    Set<Class<?>> arg0:我们感兴趣的类型的所有后代类型
	 *    
	 */
	@Override
	public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
		// TODO Auto-generated method stub
		System.out.println("我们感兴趣的所有类型:");
		// 好,我们把这些类型来遍历一下
		for (Class<?> clz : arg0) {
			System.out.println(clz);
		}
		
		// 注册Servlet组件
		ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet());
		// 配置Servlet的映射信息
		servlet.addMapping("/user");
		
		// 注册Listener组件
		sc.addListener(UserListener.class);
		
		// 注册Filter组件
		FilterRegistration.Dynamic filter = sc.addFilter("userFilter", UserFilter.class);
		// 配置Filter的映射信息
		filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
	}
}

可以看到,addMappingForUrlPatterns方法中传入的第一个参数还是蛮奇怪的,居然是EnumSet.of(DispatcherType.REQUEST),该参数表示的是Filter拦截的请求类型,即通过什么方式过来的请求,Filter会进行拦截。我们不妨点进DispatcherType枚举的源码里面去看一看,如下图所示,可以看到好多的请求类型,不过常用的就应该是FORWARD和REQUEST它俩。
在这里插入图片描述
现在addMappingForUrlPatterns方法中传入的第一个参数是EnumSet.of(DispatcherType.REQUEST),表明我们写的UserFilter会拦截通过request方式发送过来的请求。
该方法中的第二个参数(即isMatchAfter)我们直接传入true就行,第三个参数(即urlPatterns)就是Filter要拦截的路径,目前我们传入的是/*,即拦截所有请求。

测试

现在我们来启动项目进行测试,看我们注册的以上三个组件有没有起到作用。
在这里插入图片描述
可以看到我们注册的UserListener确实起到作用了,在项目启动的时候,有相关内容输出,因为它本来就是监听项目的启动和停止的。
然后,我们来访问项目的首页,此时,浏览器中显示的是首页的内容,如下图所示。
在这里插入图片描述
我们在浏览器地址栏中输入http://localhost:8080/servlet3.0-liayun/user进行访问,即向服务器发送了一个user请求,此时,我们注册的UserServlet就会来处理该请求,并给浏览器响应相应内容,如下图所示。
在这里插入图片描述

总结

我们可以通过编码的方式在项目启动的时候,给ServletContext里面来注册组件。
在项目启动的时候,我们可以有两处来使用ServletContext对象注册组件。
第一处就是利用基于运行时插件的ServletContainerInitializer机制得到ServletContext对象,然后再往其里面注册组件。本讲通篇所讲述的就是在这一处使用ServletContext对象来注册组件。
第二处,你可能想不到,我们上面不是编写过一个监听器(即UserListener)吗?它是来监听项目的启动和停止的,在监听项目启动的方法中,传入了一个ServletContextEvent对象,即事件对象,我们就可以通过该事件对象的getServletContext方法拿到ServletContext对象,拿到之后,是不是就可以往它里面注册组件了啊?

package com.meimeixia.servlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * ServletContextListener的作用:监听项目的启动和停止
 * @author liayun
 *
 */
public class UserListener implements ServletContextListener {

	// 这个方法是来监听ServletContext销毁的,也就是说,我们这个项目的停止
	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
		// TODO Auto-generated method stub
		System.out.println("UserListener...contextDestroyed...");
	}

	// 这个方法是来监听ServletContext启动初始化的
	@Override
	public void contextInitialized(ServletContextEvent arg0) {
		// TODO Auto-generated method stub
		ServletContext servletContext = arg0.getServletContext();
		System.out.println("UserListener...contextInitialized...");
	}
}

温馨提示:在项目运行的时候,再给ServletContext对象里面来注册组件,那是不行的,这是出于安全来考虑的。

参考

Spring注解驱动开发第52讲——使用ServletContext注册web三大组件

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

融极

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值