我们以前的架构是用来做一个产品,基本功能都在产品中实现,并针对每个模块用一个插件来加载,每个项目中用到的功能会有不同,在启动项目时根据当前项目所配置的插件来加载功能插件,达到同一个产品中不同项目加载不同功能的效果,整个架构中就用到struts2框架,我们通过扩展FilterDispatcher来加载所使用的插件中指定的struts2的xml配置文件,这样需要用到哪些struts配置文件就加载哪些,不用去修改struts.xml了,只用在启动前指定要启动的项目即可。
最近自己重新弄一个架构,采用这种插件的思想,并想试试用SpringMvc替换struts2,但是发现要动态加载springmvc的配置文件并没有struts2那么好做,在网上搜了很多资料都没找到一个合适的方法,其中有一篇基本也差不多实现了,网址:http://www.blogjava.net/bigbigtooth/archive/2007/11/19/85756.html 但是需要改spring的源码,并且他每次获取WebApplicationContext时会重新加载xml,研究了会spring的源码后自己找到个切入点,思路大致如下。
我们在web.xml中配置了DispatcherServlet,大致是这样的:
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
在init-param中配置的就是指定它的配置文件路径,我要做的就是不改这个参数值让它加载到我其他指定的配置文件。
我们从这个DispatcherServlet看起,它实现了多层的继承:DispatcherServlet->FrameworkServlet->HttpServletBean->HttpServlet,总之最终它就是个Servlet,首先我找它在哪里创建WebApplicationContext的,在FrameworkServlet中有个protected WebApplicationContext createWebApplicationContext(ApplicationContext parent)方法(大概399行),大约在第438行处有代码wac.setConfigLocation(getContextConfigLocation()); 看得懂这个是设置mvc的配置文件,试着改这行的代码,在参数中给它加上些其他的配置文件,然后启动,发现除了init-param中配置外其他的配置文件均不生效,于是又找到HttpServletBean的public final void init() throws ServletException 方法(大概115行),这个是这个Servlet初始化时调用的方法,大概122行处有代码PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); 看得出大概是设置参数值,在web.xml中给servlet配置的参数值都可以用ServletConfig获取到的,从这里找ServletConfigPropertyValues这个类,发现它就是个内部类,大概208行处是它调用的构造方法,其中的实现很简单,就是通过ServletConfig获取到在web.xml中设置的参数,然后通过addPropertyValue(new PropertyValue(property, value));将参数设置到spring环境中,于是思路来了,我们判断参数名,如果参数名是contextConfigLocation,则在参数值中加上其他需要的配置文件即可,通过测试,这样还真行,主要代码如下:
Enumeration en = config.getInitParameterNames();
while (en.hasMoreElements()) {
String property = (String) en.nextElement();
Object value = config.getInitParameter(property);
//对SpringMvc的xml文件和插件注入的SpringMvc配置文件整合
if(ConfigLocation.equals(property)){
value = ConfigFactory.getFactory().fusionSpringXml((String)value, "SpringMvc");//这句就是将原有的value后面拼上需要加的配置文件
}
addPropertyValue(new PropertyValue(property, value));
if (missingProps != null) {
missingProps.remove(property);
}
}
这种方法可行,于是打算自己写一个类继承DispatcherServlet再来重写init方法和ServletConfigPropertyValues这个内部类,但是发现init这个方法用final标识了,不能够重写它,并且其中用到几个变量都是私有变量,子类调用不了,于是只好直接改spring源代码了,在自己的源码中新建org.springframework.web.servlet.HttpServletBean类,将spring中这个类的源码复制过来改就行了,启动tomcat时它默认会先从你的代码中找到这个类的。
和自己的插件制架构整合的话就注意到在Servlet的init加载前就得确定好需要加载的springmvc配置文件。