在上一篇中我们可以使用注解注册自己编写的组件,但是对于第三方的组件,没有了web.xml,我们不能直接到人家类上去贴个注解,这时候该怎么呢。就得使用servlet3.0的 Sharedlibraries(共享库) / runtimes pluggability(运行时插件能力)。比如下面这个我们原本配置在web.xml中的组件,是没办法用注解替代的。
<!-- 编码过滤器开始 -->
<filter>
<filter-name>EncodeingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>EncodeingFilter</filter-name>
<servlet-name>springmvc</servlet-name>
</filter-mapping>
<!-- 编码过滤器结束 -->
<!-- 监听器开始 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 监听器结束 -->
1.ServletContainerInitializer接口
看名字翻译就是servlet容器初始化器,这是属于servlet3.0的内容。
servlet3.0的 Sharedlibraries(共享库) / runtimes pluggability(运行时插件能力)
1.Servlet容器启动时(这里是tomcat)会扫描当前应用里面的每一个jar包的ServletContainerInitializer实现类
2.ServletContainerInitializer的实现类放的位置有要求:
必须绑定在,META-INF/services/javax.servlet.ServletContainerInitializer
文件的内容就是ServletContainerInitializer实现类的全类名;
使用举例:我这里直接在src目录下创建META-INF/services/javax.servlet.ServletContainerInitializ文件
里面写我编写的ServletContainerInitializer实现类的全类名即可 (这个可以理解为web.xml文件的配置类)
首先要创建一个类继承ServletContainerInitializer接口,然后将类的全类名配置在META-INF/services/javax.servlet.ServletContainerInitializer文件中(这个文件自己新建)。文件的内容就是ServletContainerInitializer实现类的全类名;
当应用启动时,会运行ServletContainerInitializer实现类里面的onStartup方法,我们可以在这个方法里面利用ServletContext参数对象创建web3大组件(包括自己写到的组件和第三方的组件)
2.编写ServletContainerInitializer实现类
package cn.hzu.web;
import java.util.EnumSet;
import java.util.Set;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
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 cn.hzu.filter.UserFilter;
import cn.hzu.listener.UserListener;
import cn.hzu.service.Hello;
import cn.hzu.servlet.HelloServlet;
@HandlesTypes(value= {Hello.class})
public class MyServletContainerInitializer implements ServletContainerInitializer{
/*
* 应用启动的时候,会运行onStartup方法;
* 参数说明:
* Set<Class<?>> arg0 :可以拿到你要放入的类的所有实现类和继承接口类(注意不会拿到本身),通过@HandlesTypes注解设置你要传入的类
* ServletContext arg1 :应用上下文对象,可以用这个对象来创建监听器,过滤器,servlet
*
* 重要说明:ServletContext注册web组件只能在应用启动的时候创建
* 必须在项目启动的时候来添加;(一般有两个地方)
* 1)、ServletContainerInitializer得到的ServletContext;
* 2)、ServletContextListener得到的ServletContext;
*
* 这种方式我们一般用来创建第三方的组件,如果是自己写的,直接贴注解就好了,不用这么麻烦
* 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener)
* 2)、使用编码的方式,在项目启动的时候给ServletContext里面添加组件;
*
*/
@Override
public void onStartup(Set<Class<?>> arg0, ServletContext arg1) throws ServletException {
//打印@HandlesTypes(value= {Hello.class}) 注解传过来的类的子类型
for (Class<?> claz : arg0) {
System.out.println(claz);
System.out.println(arg0.size());
}
//注册组件 ServletRegistration
//addServlet方法参数:servlet名称,servlet对象,返回Dynamic对象,用于配置servlet的映射信息
ServletRegistration.Dynamic servlet = arg1.addServlet("helloServlet", new HelloServlet());
//配置servlet的映射信息
servlet.addMapping("/hello");
//注册Listener
arg1.addListener(UserListener.class);
//注册Filter FilterRegistration
FilterRegistration.Dynamic filter = arg1.addFilter("userFilter", (Class<? extends Filter>) UserFilter.class);
//配置Filter的映射信息
//参数:拦截的类型,第二个直接写true,第三个是拦截的路径
filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
}
}
注解@HandlesTypes(value= {Hello.class})这个注解传过去一个class对象,在onStartup的方法中有一个Set<Class<?>> arg0参数可以拿到你传入的对象的所有实现类和继承类(但是不会拿到你传入的对象本身)这里演示注册自己编写的组件,注册第三方的组件原理一样,也是new对象出来,一看就懂,等servlet3.0与ssm整合的时候在演示注册第三方组件。下面看一下在ServletContainerInitializer实现类中注册和使用到的类
3.类的说明
这里我使用@HandlesTypes(value= {Hello.class})注解传过去了Hello这个接口
Hello
public interface Hello {
}
Hello1
public interface Hello1 extends Hello {
}
HelloImpl
public class HelloImpl implements Hello{
}
监听器
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) {
//从ServletContextEvent中获取ServletContext对象
ServletContext servletContext = arg0.getServletContext();
System.out.println("UserListener...contextInitialized...");
}
}
过滤器
public class UserFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("过滤器放行...................................................");
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器初始化............................................");
}
}
servlet
public class HelloServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("hhh");
try {
resp.getWriter().write("hello...");
} catch (java.io.IOException e) {
e.printStackTrace();
}
}
}
项目结构
4.运行项目测试
通过下图可以看出,在没有web.xml的情况下,我们配置了ServletContainerInitializer实现类,在项目启动的时候最先执行了ServletContainerInitializer实现类的onStartup方法,跟我们前面说的一样,在onStartup方法中可以拿到我用@HandlesTypes(value= {Hello.class})注解传过去了Hello接口的子类和实现类,但是拿不到它自己本身,只打印了2个对象。然后配置的监听器也生效了,过滤器也生效了,访问路径可以看出servlet也起作用了。这就是在没有web.xml的情况下不使用注解动态注册web组件。
总结:动态注册web组件用的是ServletContext对象(但只有在容器启动的时候注册才有用)使用下面三个方法注册(其实在springMVC中没有xml的情况下也是这样,原理一样,下次servlet3.0集成ssm框架的时候测试)。在项目启动的时候注册一般就两个地方可以注册
1)、使用ServletContext注册Web组件(Servlet、Filter、Listener)就是我们刚才的onStartup方法中刚注册
2)、编写监听器,里面也有ServletContext对象,也可以用这个注册
动态注册组件的方法
ServletContext.addFilter
ServletContext.addListener
ServletContext.addServlet
动态注册这种方式我们一般用来创建第三方的组件,如果是自己写的,直接贴注解就好了,不用这么麻烦