最近基于SpringMVC的视图配置文件进行了一些小的改写,原因是为了实现在同一平台下实现不同项目的视图文件view*.xml的模块化配置。
来看一段常规程序及其配置实例:
controller:
public ModelAndView delete(@RequestParam("rowid") Long rowid)throws ServletException, IOException {
return new ModelAndView("sojoJsonView",AppUtil.delRecord(rowid,manager));
}
views.xml配置:
<bean name="sojoJsonView" class="org.springframework.web.servlet.view.json.JsonView">
<property name="encoding" value="UTF-8"/>
<property name="contentType" value="application/json"/>
<property name="jsonWriter" ref="sojoJsonWriter"/>
<property name="jsonErrors">
<list>
<ref bean="statusError" />
<ref bean="modelflagError" />
</list>
</property>
</bean>
xxx-servlet.xml配置:
<bean id="viewResolver" class="org.springframework.web.servlet.view.XmlViewResolver"/>
我们可以看到,在controller的方法里面返回一个名叫"sojoJsonView"的模型和视图对象,那么在返回客户端之前的视图类里面,SpringMVC的框架程序就会去按照我们传入的模型和视图对象名称去解析了。那么为什么视图类就知道到views.xml里面去找相应的配置视图bean呢?为什么只能写一个views.xml配置文件,而且名称只能写成views.xml。带着这些问题,我们接下来看一段源码:
public class XmlViewResolver extends AbstractCachingViewResolver implements Ordered, InitializingBean, DisposableBean
{
public static final String DEFAULT_LOCATION = "/WEB-INF/views.xml";
...
private Resource location;
...
protected synchronized BeanFactory initFactory()throws BeansException{
...
Resource actualLocation = this.location;
if (actualLocation == null) {
actualLocation = getApplicationContext().getResource("/WEB-INF/views.xml");
}
...
}
...
}
注:“...”表示省略。
从引用的源码中知道:在初始化bean工厂的时候,将views.xml文件写死在程序里了,这就是在引用SpringMVC框架的时候,为什么视图类就知道到views.xml里面去找相应的配置视图bean,为什么只能写一个views.xml配置文件,而且名称只能写成views.xml。在这里就会有一个问题产生了,就是在不同的项目里面,软件研发人员希望每个项目有独立的配置文件,这样既可以实现模块化配置,也更容易保护框架本身的安全性配置。
那么如何来实现呢?看看思路:
就是能否利用getApplicationContext()方法返回的实体对象,调用一个本身的返回数组的方法,也许可以实现。下面我们需要找一些已经实现的理论依据来支撑这种想法:
从源码:
actualLocation = getApplicationContext().getResource("/WEB-INF/views.xml");
中可以看到,我们需要可以找到接口类:ApplicationContext。
下面看ApplicationContext接口类源码:
public abstract interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver
{
public abstract String getId();
public abstract String getDisplayName();
public abstract long getStartupDate();
public abstract ApplicationContext getParent();
public abstract AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}
在接口ApplicationContext中,实现了ResourcePatternResolver接口类。
下面看ResourcePatternResolver接口类源码:
public abstract interface ResourcePatternResolver extends ResourceLoader {
public static final String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
public abstract Resource[] getResources(String paramString) throws IOException;
}
在接口ResourcePatternResolver里面定义了一个资源类的返回数据的方法getResources(String paramString)。
从上面的连续接口实现中,我们可以知道,ApplicationContext接口类可以返回一个数组对象,即:getApplicationContext().getResources(String paramString);
这样就为我们的视图文件模块化实现找到了可行性的方法,看一段改造后的源码:
public class XmlViewResolver extends AbstractCachingViewResolver implements Ordered, InitializingBean, DisposableBean {
private final static String VIEW_FILE="/WEB-INF/view*.xml";
...
private Resource[] location;
...
protected synchronized BeanFactory initFactory() throws BeansException {
...
Resource[] actualLocation = this.location;
if (actualLocation == null) {
try {
actualLocation = getApplicationContext().getResources(VIEW_FILE);
} catch (IllegalStateException e) {
} catch (IOException e) {
}
}
...
}
...
}
从改造后的源码中,我们就可以实现不同项目的视图配置文件的模块化配置了,比如:view-a.xml、view-b.xml。同时XXX-servlet.xml的配置如下:
<bean id="viewResolver" class="com.sunline.framework.util.XmlViewResolver"/>