没有web.xml怎么写web程序
1:servlet2.5时代在servlet 2.5时代,servlet配置在web.xml中,然后web容器通过加载解析web.xml完成诸如servlet,filter,listener等servlet组件的加载和初始化,简单来回忆下这个过程,这里以servlet和filter二者为例来进行说明。1.1:定义servletpublic class HelloWorldServlet exten
1:servlet2.5时代
在servlet 2.5
时代,servlet配置在web.xml
中,然后web容器通过加载解析web.xml
完成诸如servlet
,filter
,listener
等组件加载和初始化,简单来回忆下这个过程,这里以servlet和filter二者为例来进行说明。
1.1:定义servlet
public class HelloWorldServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/plain");
PrintWriter out = resp.getWriter();
out.println("hello world from before servlet 3");
}
}
1.2:定义filter
public class HelloWorldFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("触发 hello world 过滤器 before servlet 3...");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
1.3:在web.xml中配置
<servlet>
<servlet-name>HelloWorldServlet</servlet-name>
<servlet-class>dongshi.beforeservlet3.servlet.HelloWorldServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorldServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<filter>
<filter-name>HelloWorldFilter</filter-name>
<filter-class>dongshi.beforeservlet3.filter.HelloWorldFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HelloWorldFilter</filter-name>
<url-pattern>/hello</url-pattern>
</filter-mapping>
1.4:启动web服务测试
2:servlet3.0时代
在servlet3.0
规范中增加了一个非常重要的接口javax.servlet.ServletContainerInitializer
,该接口定义如下:
public interface ServletContainerInitializer {
public void onStartup(Set<Class<?>> c, ServletContext ctx)
throws ServletException;
}
其中onStartUp
方法就是我们可以大做文章的地方
。
我们接着再来看javax.servlet.ServletContext
接口,该接口定义了若干个关于动态添加servlet,filter,listener等的方法,servlet相关如下:
public ServletRegistration.Dynamic addServlet(String servletName, String className);
public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet);
public ServletRegistration.Dynamic addServlet(String servletName, Class <? extends Servlet> servletClass);
public <T extends Servlet> T createServlet(Class<T> clazz) throws ServletException;
filter相关如下:
public FilterRegistration.Dynamic addFilter(
String filterName, String className);
public FilterRegistration.Dynamic addFilter(
String filterName, Filter filter);
public FilterRegistration.Dynamic addFilter(String filterName,
Class <? extends Filter> filterClass);
listener相关如下:
public void addListener(String className);
public <T extends EventListener> void addListener(T t);
public void addListener(Class <? extends EventListener> listenerClass);
下面实例只涉及到servlet和filter,接下来我们需要做的就是实现javax.servlet.ServletContainerInitializer
接口,然后通过javax.servlet.ServletContext
的相关addXXX
的API动态的添加servlet,filter,listener即可。我们所做的这些最终还是需要让web容器知道才可以,使用SPI便可以,具体做法是在classpath
下定义多层文件夹META-INF/services
,然后创建名称为javax.servlet.ServletContainerInitializer
文本文件,将我们实现的javax.servlet.ServletContainerInitializer
的实现类的全限定名称,以一行一个的格式配置进去即可。
2.1:定义servlet
public class HelloWorldAfter3Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/plain");
PrintWriter out = resp.getWriter();
out.println("hello world after servlet 3.0");
}
}
2.2:定义filter
public class HelloWorldAfter3Filter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("触发 hello world 过滤器 after servlet 3.0...");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
2.3:定义javax.servlet.ServletContainerInitializer
实现类
public class CustomServletContainerInitializer implements ServletContainerInitializer {
private final static String JAR_HELLO_URL = "/hello111";
@Override
public void onStartup(Set<Class<?>> c, ServletContext servletContext) {
System.out.println("创建 helloWorldServlet after servlet 3...");
ServletRegistration.Dynamic servlet = servletContext.addServlet(
HelloWorldAfter3Servlet.class.getSimpleName(),
HelloWorldAfter3Servlet.class);
servlet.addMapping(JAR_HELLO_URL);
System.out.println("创建 helloWorldFilter after servlet 3...");
FilterRegistration.Dynamic filter = servletContext.addFilter(
HelloWorldAfter3Filter.class.getSimpleName(), HelloWorldAfter3Filter.class);
EnumSet<DispatcherType> dispatcherTypes = EnumSet.allOf(DispatcherType.class);
dispatcherTypes.add(DispatcherType.REQUEST);
dispatcherTypes.add(DispatcherType.FORWARD);
filter.addMappingForUrlPatterns(dispatcherTypes, true, JAR_HELLO_URL);
}
}
2.4:创建META-INF/services/javax.servlet.ServletContainerInitializer
-
目录结构
-
内容
dongshi.afterservlet3.CustomServletContainerInitializer
2.5:启动web容器
- 启动过程中加载
custom的ServletContainerInitializer
- 测试访问
3:HandlesTypes注解
该注解用在javax.servlet.ServletContainerInitializer
实现类上,用于定义感兴趣的类型,传入的参数为接口Class数组
,web容器在加载时会将感兴趣的类型的所有子类作为参数传递进来,例如定义如下接口:
public interface MyHandlerTypeClass {
}
然后定义两个实现类:
public class MyHandlerTypeClassSon implements MyHandlerTypeClass {
}
public class MyHandlerTypeClassSon2 implements MyHandlerTypeClass {
}
然后在javax.servlet.ServletContainerInitializer
实现类定义如下:
@HandlesTypes(value = {MyHandlerTypeClass.class})
public class CustomServletContainerInitializer implements ServletContainerInitializer {
}
意思就是请web容器在启动时,将MyHandlerTypeClass.class
的子类作为参数传递到方法onStartup(Set<Class<?>> c, ServletContext servletContext)
的c
中。debug的话可以看到如下结果:
4:spring对于servlet3.0的支持
那么spring又是如何支持servlet3.0的呢,毫无疑问,想要使用,spring和我们一样也需要实现javax.servlet.ServletContainerInitializer
接口来提供一个具体的实现类,我们可以通过idea来方便的找到这类,肯定是以org.springframework
开头的,如下图:
红框中的就是我们要找的目标spring定义的custom的javax.servlet.ServletContainerInitializer
类,也可以看出其实在spring-web
模块中的,那么根据规范,肯定是需要定义META-INF/services/javax.servlet.ServletContainerInitializer
文件的,然后将org.springframework.web.SpringServletContainerInitializer
配置到文件中,确实是有的,如下图:
具体内容为:
org.springframework.web.SpringServletContainerInitializer
是吧?和我们的猜测是一样的。
4.1:SpringServletContainerInitializer
org.springframework.web.SpringServletContainerInitializer
类是spring提供的javax.servlet.ServletContainerInitializer
的实现类,实现脱离web.xml
对于web相关组件的加载,其源码如下:
// <1>
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// <2>
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
}
}
}
// <3>
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
<1>
处代码,就是告知web容器如tomcat,在调用我的onStartup方法的时候,请将所有的org.springframework.web.WebApplicationContext
的子类作为方法参数给我,这里的方法自然就是onStartup
,参数就是webAppInitializerClasses
。<2>
处代码是因为有些web服务器厂商并不会按照<1>
处的配置进行传递参数,所以这里进行了相关的判断,避免因为web服务器厂商实现问题,导致自己出现问题。<3>
处代码是调用具体的初始化器们
完成相关web组件的注册的工作。接下来继续看org.springframework.web.WebApplicationInitializer
接口。
4.2:WebApplicationInitializer
先看下org.springframework.web.WebApplicationInitializer
类结构:
我们这里重点关注org.springframework.web.servlet.suppport.AbstractDispatherServletInitializer
类,在有web.xml
情况下,加载springmvc的org.springframework.web.servlet.DispatcherServlet
是通过配置<servlet>
标签进行配置,可能如下:
<servlet>
<servlet-name>letsGO</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/letsGO-servlet.xml</param-value>
</init-param>
<init-param>
<param-name>name</param-name>
<param-value>jack</param-value>
</init-param>
<!-- 值越小优先级越高 -->
<load-on-startup>1</load-on-startup>
</servlet>
那么在没有web.xml的时候,就可以通过该类来完成加载org.springframework.web.servlet.DispathcerServlet
的工作了,依赖的方法是org.springframework.web.servlet.support.AbstractDispatcherServletInitializer#registerDispatcherServlet
,方法核心逻辑大概如下:
// 动态添加DispatcherServlet
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
// 设置高的启动优先级
registration.setLoadOnStartup(1);
// 设置请求映射
registration.addMapping(getServletMappings());

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐
所有评论(0)