本系列文章旨在记录和总结自己在Java Web开发之路上的知识点、经验、问题和思考,原来已经分享在我的CSDN博客,现在分享在头条,希望能帮助更多码农和想成为码农的人。版权声明:本文为CSDN博主「普通的码农」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。原文链接:
目录
- 介绍
- 使用WebApplicationInitializer接口
- 使用Abstract DispatcherServlet Initializer 抽象类
- 使用AbstractContextLoaderInitializer抽象类
- 总结
介绍
介绍了基于XML来配置Spring MVC的DispatcherServlet并整合Spring IoC容器,本篇文章继续介绍Spring MVC提供的基于Java的方式来配置DispatcherServlet并整合Spring IoC容器。
我们仍然使用上篇文章的示例工程。
使用基于Java的方式进行配置的基本步骤,就是要实现 Spring MVC提供的某个接口或扩展某个抽象类,往往扩展抽象类会更简单更清晰一些;然后覆盖某些方法即可。
这些接口和抽象类的名字都是以 Initializer 结尾的,我们就叫它们为 初始化器 吧。
Spring MVC会自动扫描是否有初始化器的 具体类 ,如果有,就会自动实例化它们并执行相应的方法来配置DispatcherServlet并整合Spring IoC容器。
而且,基于XML和基于Java这两种方式可以同时使用。
使用WebApplicationInitializer接口
我们先使用实现WebApplicationInitializer接口的方式来配置DispatcherServlet并整合Spring IoC容器。
首先,假设我们要为spring-mvc-test工程的 appA应用 使用基于Java的方式来配置DispatcherServlet并整合Spring IoC容器。
先把web.xml中的关于appA的相关配置注释掉或删掉,我这里选择注释(使用<!–和–>):
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns=" java ee" xmlns:xsi="" xsi:schemaLocation=" " id="WebApp_ID" version="3.0"> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>context config Location</param-name> <param-value>/WEB-INF/root-context.xml</param-value> </context-param> <!-- 注释掉appA的相关配置 <servlet> <servlet-name>appA</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/appA-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appA</ Servlet -name> <url-pattern>/appA/*</url-pattern> </servlet-mapping> --> <servlet> <servlet-name>appB</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/appB-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appB</servlet-name> <url-pattern>/appB/*</url-pattern> </servlet-mapping> </web-app>
1.新建类AppAInitializer
我把新建的这个初始化器类放到test.config包下,你也可以放在自己定义的包下,AppAInitializer.java的代码如下:
package test.config;
import javax.servlet. ServletContext ;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class AppAInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) throws ServletException {
XmlWebApplicationContext appAContext = new XmlWebApplicationContext();
appAContext.setConfigLocation("/WEB-INF/appA-context.xml");
ServletRegistration.Dynamic registrationA = container.addServlet("appA", new DispatcherServlet(appAContext));
registrationA.setLoadOnStartup(1);
registrationA.addMapping("/appA/*");
}
}
WebApplicationInitializer接口有唯一的方法onStartup需要我们实现。那怎么实现呢?
2.实例化Spring IoC 容器
还记得我们在standalone应用中是如何在main方法中实例化Spring IoC容器的吗?
没错,就是使用ClassPathXmlApplicationContext来实例化一个ApplicationContext对象,参考 。
同样,在Web应用中也是类似,只不过Spring MVC为我们提供的是WebApplicationContext这个接口,如果配置元数据是基于XML的,那么我们就可以使用XmlWebApplicationContext这个具体类,我们实例化一个它的对象,然后设置它的配置元数据的位置即可(这个位置默认是从资源的根目录开始)。
XmlWebApplicationContext appAContext = new XmlWebApplicationContext(); appAContext.setConfigLocation("/WEB-INF/appA-context.xml");
3.动态注册DispatcherServlet
我们直接实例化一个DispatcherServlet对象,别忘了把我们前面生成的IoC容器传给它,这个IoC容器就是它的 Servlet IoC容器 了:
new DispatcherServlet(appAContext)
接下来,就应该配置DispatcherServlet了,这里,主要采用的是Servlet规范中的动态注册Servlet的API,即接口ServletContext的addServlet方法,ServletContext对象container是onStartup方法的参数:
ServletRegistration.Dynamic registrationA = container.addServlet("appA", new DispatcherServlet(appAContext));
最后,使用得到的注册对象设置Servlet的其他属性,比如是否容器启动时加载、URL映射模式等:
registrationA.setLoadOnStartup(1); registrationA.addMapping("/appA/*");
4.验证
同上篇文章,不再赘述。
结论是整个Web应用可以由Tomcat正常加载并运行,包括Java配置的appA应用和原来web.xml中配置的appB应用。
如果完全不用web.xml配置呢?
那就把web.xml中的内容都注释或删除。
然后,可以仍然在AppAInitializer的onStartup方法中添加appB应用以及Root IoC容器的配置代码,不过这样的话代码会显得有些臃肿且不明确,记住,一个类只干一件事。
当然,也可以为appB应用创建独立的初始化器AppBInitializer,然后实现onStartup方法;
为Root IoC容器的配置创建独立的初始化器MyWebApplicationInitializer,然后实现onStartup方法。这里的类名都可以自由命名的。
AppBInitializer跟AppAInitializer类似,读者可以自行实现。
Root IoC容器则因为使用的是监听器而非Servlet,而有所不同。我们可以根据上面的动态注册Servlet的代码举一反三,既然有addServlet方法,是否也会有 addListener 方法呢?答案是肯定的,我们可以使用Eclipse的代码补全功能可以很快的找到这个方法(其他IDE都应该有此功能)。于是,我们应该先new一个Spring MVC提供的 ContextLoaderListener ,将Root IoC容器的配置元数据的位置传给这个监听器即可:
XmlWebApplicationContext rootContext = new XmlWebApplicationContext(); rootContext.setConfigLocation("/WEB-INF/root-context.xml"); container.addListener(new ContextLoaderListener(rootContext));
使用AbstractDispatcherServletInitializer抽象类
上面覆盖WebApplicationInitializer接口的onStartup方法的方式感觉不够简练,所以Spring MVC为我们提供了更方便的AbstractDispatcherServletInitializer抽象类来配置DispatcherServlet并整合Spring IoC容器。
实际上,这个抽象类里面也可以配置Root IoC容器,不过这个抽象类也是通过继承 AbstractContextLoaderInitializer 抽象类的,因此我们也可以直接使用AbstractContextLoaderInitializer抽象类来配置Root IoC容器,见下一节。
我为appB应用的DispatcherServlet创建了一个独立的初始化器,AppBInitializer.java:
package test.config; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.XmlWebApplicationContext; import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer; public class AppBInitializer extends AbstractDispatcherServletInitializer { @Override protected WebApplicationContext createServletApplicationContext() { XmlWebApplicationContext appBContext = new XmlWebApplicationContext(); appBContext.setConfigLocation("/WEB-INF/appB-context.xml"); return appBContext; } @Override protected String[] getServletMappings() { return new String[] { "/appB/*" }; } @Override protected WebApplicationContext createRootApplicationContext() { return null; } @Override protected String getServletName() { return "appB"; } }
我们必须要覆盖三个方法:
- createServletApplicationContext:创建Servlet IoC容器的实例并传入配置元数据的位置;
- getServletMappings:返回Servlet的URL映射模式;
- createRootApplicationContext:创建Root IoC容器的实例并传入配置元数据的位置,我这里直接返回null,因为我下一节直接使用AbstractContextLoaderInitializer抽象类来配置Root IoC容器。
我还覆盖了一个方法:
- getServletName:返回Servlet的名字即可,如果不覆盖此方法,会使用AbstractDispatcherServletInitializer抽象类默认的名字DEFAULT_SERVLET_NAME = “dispatcher”。大家可以自行看它的源码(如何查看源码参考 )。
一开始我并没有覆盖此方法,但是验证的时候我发现Console视图中 打印的日志信息是初始化dispatcher这个DispatcherServlet ,于是就想该如何使用AbstractDispatcherServletInitializer抽象类配置Servlet的名字,于是就查看了一下其源码。
使用AbstractContextLoaderInitializer抽象类
AbstractContextLoaderInitializer抽象类与上一节的AbstractDispatcherServletInitializer抽象类的配置模式是一样的,都是覆盖某些方法而已,不再赘述,直接上代码。
MyWebApplicationInitializer.java:
package test.config; import org.springframework.web.context.AbstractContextLoaderInitializer; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.XmlWebApplicationContext; public class MyWebApplicationInitializer extends AbstractContextLoaderInitializer { @Override protected WebApplicationContext createRootApplicationContext() { XmlWebApplicationContext rootContext = new XmlWebApplicationContext(); rootContext.setConfigLocation("/WEB-INF/root-context.xml"); return rootContext; } }
总结
- 基于Java和基于XML在一个Web应用中可以混合使用;
- 基于Java的方式又有两种:WebApplicationInitializer接口和AbstractDispatcherServletInitializer抽象类;
- 基于Java的整合Spring IoC的思想是一致的:都是先new一个容器对象,设置元数据的所在位置,然后把容器对象传给DispatcherServlet或ContextLoaderListener对象。
- 我们要善于举一反三,触类旁通,推此及彼。
- 我们要多看源代码和Javadoc。