Spring MVC性能提升

来源:互联网 发布:网络协议与分析8题 编辑:程序博客网 时间:2024/05/16 17:50

这几天在做公司内部SpringMVC的培训,培训之余看了看大家的代码,总感觉一个很不爽的地方,就是在每一个控制器的执行方法里,总要写这样一段代码: 
代码1:
Map model = new HashMap(); 
model.put("yourKey", "yourObject");

ModelAndView mv = new ModelAndView("yourPage", model); 
... 
... 
return mv; 
每个请求就要new 一个Map和一个ModeAndView对象,这样如果请求多,就在不停的做new操作。new操作对JVM来说开销算是比较大的,所以尽量少建新对象。
在上面的例子中我们用ModelAndView的构造函数是public ModelAndView(String viewName, Map model),打开该构造函数的源码: 

代码2
public ModelAndView(String viewName, Map model) { 
    this.view = viewName; 
    if (model != null) { 
      getModelMap().addAllObjects(model); 
    } 


//getModelMap()的代码: 
public ModelMap getModelMap() { 
    if (this.model == null) { 
      this.model = new ModelMap(); 
    } 
    return this.model; 


可以看出我们在java 代码1中创建的Map,在被提取值以后就没有什么用了。所以在Controller里面创建Map对象是冗余操作。
代码1应该如下写:

代码3
ModelAndView mv = new ModelAndView(); 
Map model = mv.getModelMap(); 
model.put("yourKey", "yourObject"); 
... 
... 
return vm; 
这样一来就少创建了一个对象,节省了一点点开销。
可是还是每次都要创建ModelAndView新实例,如果一个网站的日访问量是10万(当然,访问理大的网站都会考虑用静态页面的方法),那么一天这个JVM就要创建10万个ModelAndView实例。这样的开销就很大了。

一般的解决办法就是让ModelAndView能够循环利用,要想循环利用ModelAndView就要有一个Pool,在我们需要可以通过Pool.get方法来取出ModelAndView实例,在不需要时在调用Pool.release(ModelAndView)把它放回。需要进取出总是很好办的,问题在什么时候可以调用release方法来释放。解决这个问题只能看SpringMVC的源码,看看有没有解决办法。
最笨方法的就是顺藤摸瓜,每一个请求到来都要先到DispatchServlet的doService。打开源码找到这个方法,发现也没有什么了不起的地方,可是发现一个醒目的片段: try { 
    doDispatch(request, response); 

finally 
看来重要工作还在doDispatch中,那么就再找到doDispatch。
在doDispatch方法的前半部分是得到Controller返回的ModelAndView对象,后半部分是做显示处理,其中有这样一个显赫的代码段:  
// Did the handler return a view to render? 
if (mv != null && !mv.wasCleared()) { 
render(mv, processedRequest, response); 

在被调的render方法里,就可以看出当render方法执行完后,ModelAndView对象已经不会再被使用,唯一能用到它的就是垃圾回收器了。(如果不信,你可以继续往下看, 以后工作都在View的子类里跑)
所以在render方法结束以后,再把ModelAndView对象放入缓存是正确的。我们可以继承DispatchServlet,然后把render方法重写,再用一个List作为简单的缓存池,实现如下:

代码4

public class CustomDispatchServlet extends DispatcherServlet { 
   
private static List<ModelAndView> modeAndViews = new ArrayList<ModelAndView>(20); 
   
static{ 
       for(int i = 0; i < 20; i++){   //缓存量为20 
         modeAndViews.add(new ModelAndView()); 
       } 

   
public void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) 
throws Exception{ 
       super.render(mv, request, response); 
       releaseModelAndView(mv); 

   
       //回收ModelAndView 
private void releaseModelAndView(ModelAndView mv){ 
       mv.clear(); 
       mv.getModelMap().clear(); 
       synchronized(modeAndViews){ 
         modeAndViews.add(mv); 
       } 

   
       //取ModelAndView 
public static ModelAndView getModeAndView(){ 
       synchronized(modeAndViews){ 
         if(modeAndViews.size() > 0) 
            return modeAndViews.remove(0); 
         else 
            return new ModelAndView(); 
       } 

   

当然你的web.xml的配置应改为: 
代码5

<servlet> 
<servlet-name>test</servlet-name> 
       <servlet-class>your.package.CustomDispatchServlet</servlet-class> 
<load-on-startup>1</load-on-startup> 
</servlet> 
然后你Controller里的执行代码也应改成如下: 
代码6

ModelAndView mv = CustomDispatchServlet.getModeAndView(); 
mv.setViewName("yourPage"); 
mv.getModel().put("attribute", "yourObject"); 
... 
   ... 
return mv; 
整个完成以后,测试起来屡试不爽。
整个下来,没有做太大的改到,也降低了开销。

还有一点要提醒一下,如果你用JSP做为页面,请不要用在ModelAndView的Map里放什么东西,这样Spring要做很多无用功,如果有东东全放入HttpServletRequest中,要想研究的话可以看看InternalResourceView的方法renderMergedOutputModel。

原创粉丝点击