文章源自JAVA秀-https://www.javaxiu.com/19928.html
前一章节回顾: 前面的章节,我们对spring和springmvc有了比较深入的了解。知道了spring容器时如何启动的,知道了springmvc是如何工作的,但是还是留下了一些疑问。比如说war包丢到webapps路径下后,是怎么到最后启动成功的?本章节和后续的三个章节,我们将进一步探讨更深一步的问题。文章源自JAVA秀-https://www.javaxiu.com/19928.html
文章源自JAVA秀-https://www.javaxiu.com/19928.html
首先我们回顾一下web后台服务开发的一些基础用法文章源自JAVA秀-https://www.javaxiu.com/19928.html
[1] 过滤器的基本使用文章源自JAVA秀-https://www.javaxiu.com/19928.html
第一步:创建一个全局过滤器文章源自JAVA秀-https://www.javaxiu.com/19928.html
public class GlobalFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//过滤器初始化方法
System.out.println("Execute GlobalFilter` init method.");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//过滤器过滤方法
System.out.println("Execute GlobalFilter` doFilter method.");
//调用下一个过滤器的过滤方法
chain.doFilter(request, response);
}
@Override
public void destroy() {
//过滤器的销毁方法
System.out.println("Execute GlobalFilter` destroy method.");
}
}
第二步:在web.xml中配置GlobalFilter拦截器文章源自JAVA秀-https://www.javaxiu.com/19928.html
<filter>
<filter-name>globalFilter</filter-name>
<filter-class>com.minesoft.tutorial.filter.GlobalFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>globalFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
通过这两步的配置,基本上就可以使用拦截器做定制化的业务处理了,还是比较简单。文章源自JAVA秀-https://www.javaxiu.com/19928.html
[2] Servlet的基本使用文章源自JAVA秀-https://www.javaxiu.com/19928.html
第一步:创建一个UserServlet类文章源自JAVA秀-https://www.javaxiu.com/19928.html
public class UserServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost ---------------");
System.out.println(req.getMethod());
System.out.println(req.getRequestURI());
System.out.println(req.getServletPath());
System.out.println(req.getServerPort());
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet ---------------");
resp.setContentType("text/json;charset=UTF-8");
resp.setCharacterEncoding("UTF-8");
PrintWriter out = resp.getWriter();
User user = new User();
user.setId(1000L);
user.setName("张山");
user.setIdentity("43057818787873628X");
user.setBankcard("36457736355363");
user.setMobile("16752652625");
user.setGender(2);
user.setAge(18);
String str = JSONObject.toJSONString(user);
out.println(str);
out.flush();
out.close();
}
}
第二步:在web.xml文件中配置UserServlet类文章源自JAVA秀-https://www.javaxiu.com/19928.html
<servlet>
<servlet-name>userServlet</servlet-name>
<servlet-class>com.minesoft.tutorial.servlet.UserServlet</servlet-class>
<!-- contextConfigLocation不是必须的, 如果不配置contextConfigLocation, springmvc的配置文件默认在:WEB-INF/servlet的name+"-servlet.xml" -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>userServlet</servlet-name>
<url-pattern>/user</url-pattern>
</servlet-mapping>
通过这两步的配置,基本上就可以使用Servlet做定制化的业务处理了,还是比较简单。文章源自JAVA秀-https://www.javaxiu.com/19928.html
[3] 监听器的基本使用文章源自JAVA秀-https://www.javaxiu.com/19928.html
第一步:创建一个GlobalListener监听器类文章源自JAVA秀-https://www.javaxiu.com/19928.html
public class GlobalListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("Execute GlobalListener` contextInitialized method.");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("Execute GlobalListener` contextDestroyed method.");
}
}
第二步:在web.xml文件中配置GlobalListener监听器类文章源自JAVA秀-https://www.javaxiu.com/19928.html
<listener>
<listener-class>com.minesoft.tutorial.listener.GlobalListener</listener-class>
</listener>
通过这两步的配置,基本上就可以使用监听器做一些容器启动后的初始化的业务操作了。文章源自JAVA秀-https://www.javaxiu.com/19928.html
熟悉了解了上面三个web开发的常用功能,其它的标签配置信息就不多做说明了,感兴趣的朋友可以自己学习使用。文章源自JAVA秀-https://www.javaxiu.com/19928.html
1. web.xml的被解析过程简单介绍(也就是后台服务的启动过程)
在spring还没有出现之前,那时的java的web后台服务开发者很多都是基于servlet做开发的。当我们完成一个web后台服务的开发后,我们会将其打成一个war包,然后丢到web容器的指定目录下(tomcat时webapps)。此时web容器会解压该war包,然后读取web.xml文件存放到某个对象中,然后启动这个web后台服务。来我们细致的看看这个过程是怎样的:文章源自JAVA秀-https://www.javaxiu.com/19928.html
[1] tomcat这个web容器会扫描到当前有一个新的web服务的存在,tomcat将这个新的war包解压出来;文章源自JAVA秀-https://www.javaxiu.com/19928.html
[2] tomcat会读取这个war包内的web.xml文件,然后将该文件的所有信息读取,并存放到一个内存对象中;仅仅只是将文件的filter,listener,servlet,session等等标签信息读取到一个对象中,并不会开始解析这些标签信息。文章源自JAVA秀-https://www.javaxiu.com/19928.html
[3] 开始filter标签信息的解析,根据filter标签创建出对应的filter实例对象,并按照web.xml中的配置顺序构建成为一个过滤器链。文章源自JAVA秀-https://www.javaxiu.com/19928.html
[4] 开始listener标签信息的解析,根据listener标签信息创建出对应的listener实例对象,存放到某个内容对象中。文章源自JAVA秀-https://www.javaxiu.com/19928.html
[5] 开始servlet标签信息的解析,根据servlet标签信息创建出对应的servlet实例对象,并按照web.xml中的配置顺序构建url-servlet映射对象,统一存放到某个内存对象中。文章源自JAVA秀-https://www.javaxiu.com/19928.html
[6] 完成所有的web.xml中的标签信息的解析之后,开始调用各个监听器的contextInitialized方法。等所有的监听器的contextInitialized方法被调用完毕,我们的web后台服务也就启动成功了。文章源自JAVA秀-https://www.javaxiu.com/19928.html
另外有些好奇的同学可能在问,这些都只是博主的自己的文字描述,怎么确定在解析的时候,就是按照先解析filter,后解析listener,最后解析servlet呢?先不要着急,本章只是一些概念的基本介绍和引入,详细的解析web.xml文件的过程下一章节会讲述。文章源自JAVA秀-https://www.javaxiu.com/19928.html
另外还有一个问题,就是面试的时候经常问道的一个,就是listner,filter,servlet的执行顺序问题,这个问题其实很简单,就是listner->filter->servlet这个顺序。首先是一定是listner,因为服务刚刚启动起来的时候,就会调用监听器的contextInitialized方法;其次服务启动成功后,后面才有http请求过来,但是http请求不会直接交给我们的servlet,而是要先进行过滤操作,因为像一些未登录的请求,我们可能要拒绝掉;最后所有的过滤器都过滤完毕之后,确定这个请求没有问题之后,才交到servlet进行业务处理。文章源自JAVA秀-https://www.javaxiu.com/19928.html
2. ServletContext的深入理解
首先我们运行下面一段的代码:文章源自JAVA秀-https://www.javaxiu.com/19928.html
private void analysisMethod(HttpServletRequest req) {
try {
//1.从ServletContext对象中拿到contextConfigLocation标签所解析出来的对象
ServletContext servletContext = req.getServletContext();
Enumeration<String> namesEnum = servletContext.getInitParameterNames();
while(namesEnum.hasMoreElements()){
String key = namesEnum.nextElement();
String value = servletContext.getInitParameter(key);
System. out.println(key + " : " + value);
}
//2.从ServletContext对象中拿到Servlet标签解析出来的UserServlet对象
ApplicationContextFacade contextFacade = (ApplicationContextFacade) servletContext;
ServletRegistration registration = contextFacade.getServletRegistration("userServlet");
ApplicationServletRegistration servletRegistration = (ApplicationServletRegistration) registration;
Field field = servletRegistration.getClass().getDeclaredField("wrapper");
field.setAccessible(true);
Wrapper wrapper = (Wrapper) field.get(servletRegistration);
HttpServlet userServlet = (HttpServlet) wrapper.getServlet();
if (userServlet != null) {
System.out.println(userServlet.getClass().getSimpleName());
}
//3.从ServletContext对象中拿到过滤器标签解析出来的GlobalFilter对象
FilterRegistration filterRegistration = contextFacade.getFilterRegistration("globalFilter");
ApplicationFilterRegistration applicationFilterRegistration = (ApplicationFilterRegistration) filterRegistration;
field = applicationFilterRegistration.getClass().getDeclaredField("context");
field.setAccessible(true);
StandardContext standardContext = (StandardContext) field.get(applicationFilterRegistration);
field = standardContext.getClass().getDeclaredField("filterConfigs");
field.setAccessible(true);
Map<String, ApplicationFilterConfig> filterConfigs = (Map<String, ApplicationFilterConfig>) field.get(standardContext);
ApplicationFilterConfig filterConfig = filterConfigs.get("globalFilter");
field = filterConfig.getClass().getDeclaredField("filter");
field.setAccessible(true);
GlobalFilter globalFilter = (GlobalFilter) field.get(filterConfig);
if (globalFilter != null) {
System.out.println(globalFilter.getClass().getSimpleName());
}
//4.从ServletContext对象中拿到监听器标签解析出来的GlobalListener对象
field = standardContext.getClass().getDeclaredField("applicationLifecycleListenersObjects");
field.setAccessible(true);
Object applicationLifecycleListenersObjects[] = (Object[]) field.get(standardContext);
ServletContextListener listener = (ServletContextListener) applicationLifecycleListenersObjects[0];
GlobalListener globalListener = (GlobalListener) listener;
if (globalListener != null) {
System.out.println(globalListener.getClass().getSimpleName());
}
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
我们看看控制台的打印如下:文章源自JAVA秀-https://www.javaxiu.com/19928.html
contextConfigLocation : classpath:applicationContext*.xml
UserServlet
GlobalFilter
GlobalListener
从上面的代码,我们发现了一个事实,那就是通过ServletContext对象,我们可以拿到globalFilter过滤器对象,我们可以拿到UserServlet业务对象,我们甚至可以拿到监听器对象,我们可以拿到配置的context-param标签配置的数据。如果我们的UserServlet持有service对象,而service对象又持有mapper对象,那么我们通过ServletContext对象,也就可以拿到这些service和mapper对象,开发者可以通过ServletContext对象拿到一个web后台服务的任何一个实例对象。由上述的这些分析,我们可以得出一个结论,我们可以把ServletContext对象看作是一个web后台实例。我们的整个后台服务在内存中是一颗对象树,而ServletContext对象是这颗对象树是根节点。每当一个url请求到达web容器的时候,web容器会根据配置的url信息,将这个请求转交给对应的ServletContext对象,然后再由ServletContext对象,转交给对应的过滤器对象和servlet对象做业务处理。文章源自JAVA秀-https://www.javaxiu.com/19928.html
3. 基本概念的总结
[1] web.xml文件是什么?
就是一个还未运行的后台服务实例核心对象的前身,也就是ServletContext对象的前身,它记录和承载了一个web后台服务的核心信息。文章源自JAVA秀-https://www.javaxiu.com/19928.html
[2] ServletContext对象是什么?
当我们的web容器启动我们的web后台应用的时候,会把web.xml文件读取解析成为一个ServletContext对象,这个对象也就可以看成是一个运行中的后台服务实例。文章源自JAVA秀-https://www.javaxiu.com/19928.html
[3] web容器能否持有多个ServletContext对象?
如果我们在web容器中部署了多个后台服务war包,web容器会依次启动这几个war包,依次解析war包中的web.xml文件,这样一个web容器就会持有多个这样的ServletContext对象,每个不同的ServletContext对象都是一个web后台服务。文章源自JAVA秀-https://www.javaxiu.com/19928.html
[4] web容器和web服务实例的关系?
一个web容器(tomcat容器或者jetty容器)持有多个web服务实例(在一个web容器下部署多个war包)。文章源自JAVA秀-https://www.javaxiu.com/19928.html
文章源自JAVA秀-https://www.javaxiu.com/19928.html
更多知识请关注公众号文章源自JAVA秀-https://www.javaxiu.com/19928.html

评论