• SpringMVC——使用RequestDispatcher.include()和HttpServletResponseWrapper动态获取jsp输出内容


    介绍本篇内容前,先抛出我遇到的问题或者说是需求!(精读阅读本篇可能花费您15分钟,略读需5分钟左右)

    一:需求说明

    有一个Controller有两个方法
    
    第一个方法通过指定的路径和参数去渲染jsp内容,并返回html数据
    
    第二个方法获取第一个方法中的html进行封装
    
    现在的做法是在第二个方法通过发送Http请求获取数据,然后返回进行封装!
    问题:
    需要优化的是 不通过Http请求,第二个方法可以拿到第一个方法中的Html数据
    

    二:简化例子(待优化的例子)

    注:使用的SpringMVC框架,使用贴出视图解析器 配置文件

    <!-- 默认的视图解析器 在上边的解析错误时使用 (默认使用html)- -->
    	<bean id="defaultViewResolver"
    		class="org.springframework.web.servlet.view.InternalResourceViewResolver"
    		p:order="2">
    		<property name="viewClass"
    			value="org.springframework.web.servlet.view.JstlView" />
    		<property name="contentType" value="text/html" />
    		<property name="prefix" value="/WEB-INF/jsp/" />
    		<property name="suffix" value=".jsp" />
    	</bean>
    

    1.简化请求图示说明

    这里写图片描述

    简单说明:一个Controller中有三个方法,访问/index 返回html输出到页面,
    浏览器页面显示内容为:

     hello 
     url = http://blog.csdn.net/u010648555
     world  
     url = http://blog.csdn.net/u010648555
     
    

    /index中通过Http去请求/hello ,渲染hello.jsp 返回 hello.jsp 对应的html代码,去请求/world ,渲染world.jsp 返回 world..jsp 对应的html代码!

    2.简化代码说明

    (1):Java代码

    package com.dufy.web;
    
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.net.URLEncoder;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * Created by dufy on 2017/3/21.
     */
    
    @Controller
    public class JspController {
    
        /**
         * 跳转到WEB_INF/jsp/welcome/index.jsp
         * @param request
         * @param response
         * @return
         */
        @RequestMapping("/index")
        public String index(HttpServletRequest request ,HttpServletResponse response) {
            List<String> list = new ArrayList<String>();
            list.add("hello");
            list.add("wrold");
            String commonUrl = "http://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath();
            StringBuilder pageHtml = new StringBuilder();//使用StringBuilder拼接字符串效率高
            if (list != null && list.size() > 0) {
                for (String str : list
                        ) {
                    StringBuffer paramsBuffer = new StringBuffer();//线程安全
                    paramsBuffer .append(str + "=" + str);//hello=hello  world=world
                    //如果参数中url 需要使用URLEncoder
                    paramsBuffer .append("url"+ "=" + URLEncoder.encode("http://blog.csdn.net/u010648555"));
    
                    //使用post请求 ,后台获取每个接口方法对应生成的html
                    String urlStr = commonUrl +"/" + str + "?jsppath=" + str;//reqest url 这里会调用 /hello  /world
    
                    String urlSourceResult = getURLSourcePost(urlStr,paramsBuffer.toString());
    
                    pageHtml.append(urlSourceResult);
    
                }
    
            }
            request.setAttribute("pageHtml",pageHtml);
            return "welcome/index";
    
        }
    
        /**
         * 跳转到WEB_INF/jsp/welcome/hello.jsp
         * @param request
         * @param response
         * @return
         */
        @RequestMapping(value = "/hello",method = RequestMethod.POST)
        public String hello(HttpServletRequest request,HttpServletResponse response){
    
            String jsppath = request.getParameter("jsppath");
            //处理一些业务逻辑
            Map<String,String> params = new HashMap<>();
    
            for (Object p : request.getParameterMap().keySet()) {
                try {
                    String s = request.getParameter(p.toString());
                    params.put(p.toString(), s);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            request.setAttribute("params",params);
            return "welcome/" + jsppath;
        }
    
        /**
         * 跳转到WEB_INF/jsp/welcome/world.jsp
         * @param request
         * @param response
         * @return
         */
        @RequestMapping(value = "/wrold" ,method = RequestMethod.POST)
        public String world(HttpServletRequest request,HttpServletResponse response){
            String jsppath = request.getParameter("jsppath");
            //处理一些业务逻辑
            Map<String,String> params = new HashMap<>();
    
            for (Object p : request.getParameterMap().keySet()) {
                try {
                    String s = request.getParameter(p.toString());
                    params.put(p.toString(), s);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            request.setAttribute("params",params);
            return "welcome/" + jsppath;
        }
    
    
        /**
         * 通过网站域名URL使用POST方式 获取该网站的源码
         * @param url 请求的地址
         * @param param 请求的参数
         * @return 返回请求的结果
         */
        private  String getURLSourcePost(String url,String param) {
            PrintWriter out = null;
            BufferedReader in = null;
            StringBuilder htmlResult = new StringBuilder();
            try
            {
                URL realUrl = new URL(url);
                //打开和URL之间的连接
                HttpURLConnection conn = (HttpURLConnection)realUrl.openConnection();
                //设置请求的方式
                conn.setRequestMethod("POST");
                conn.setConnectTimeout(5 * 1000);
                //设置通用的请求属性
                conn.setRequestProperty("accept", "*/*");
                conn.setRequestProperty("connection", "Keep-Alive");
                conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
                // charset=UTF-8以防止乱码!
                conn.setRequestProperty("content-type", "application/x-www-form-urlencoded; charset=UTF-8");
                //发送POST请求必须设置如下两行
                conn.setDoOutput(true);
                conn.setDoInput(true);
                //获取URLConnection对象对应的输出流
                out = new PrintWriter(conn.getOutputStream());
                //发送请求参数
                out.print(param);
                //flush输出流的缓冲
                out.flush();
                //定义BufferedReader输入流来读取URL的响应
                in = new BufferedReader(
                        new InputStreamReader(conn.getInputStream()));
                String line;
                while ((line = in.readLine())!= null)
                {
                    if(StringUtils.isNotBlank(line)){
                        htmlResult.append("
    " + line);
                    }
                }
            }
            catch(Exception e)
            {
                throw new RuntimeException("发送POST请求出现异常!",e);
    
            }
            //使用finally块来关闭输出流、输入流
            finally
            {
                try
                {
                    if (out != null)
                    {
                        out.close();
                    }
                    if (in != null)
                    {
                        in.close();
                    }
                }
                catch (IOException ex)
                {
                    ex.printStackTrace();
                }
            }
            return htmlResult.toString();
        }
    
    }
    
    

    (2):对于的JSP页面

    a:index.jsp

    <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
    
    <head>
    	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    	<title>welcome index</title>
    </head>
    <body>
    	${pageHtml}
    </body>
    </html>
    

    b:hello.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <%@ page import="java.util.*" %>
    <%@ page import="java.net.URLDecoder" %>
    
    <%
    
    	Map<String, String> params = (Map<String, String>)request.getAttribute("params");
    	String hello = params.get("hello");
    	String url = URLDecoder.decode(params.get("url")); //URl解码
    	out.print("<p> " + hello + " </p>");
    	out.print("<p> " + url + " </p>");
    %>
    
    

    c:wrold.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <%@ page import="java.util.*" %>
    <%@ page import="java.net.URLDecoder" %>
    
    <%
    
    	Map<String, String> params = (Map<String, String>)request.getAttribute("params");
    	String world = params.get("world");
    	String url = URLDecoder.decode(params.get("url")); //URl解码
    	out.print("<p> " + world+ " </p>");
    	out.print("<p> " + url + " </p>");
    %>
    
    

    3.待解决(优化)的问题说明
    如图是在上面的基础上要进行的优化图!

    这里写图片描述

    简单说明,优化方法不使用Http请求,调用/index 可以直接拿到hello.jsp和world.jsp的内容,有什么好的办法能够动态获取JSP的内容呢???思考ing...............

    注:可能有人会说,不就是输出

     hello 
     url = http://blog.csdn.net/u010648555
     world  
     url = http://blog.csdn.net/u010648555
     
    
    **需要搞的这么复杂吗。直接使用 在方法里面返回对应的字符串不就好了,对不起,我这里只是举个例子,我实际情况就是需要从jsp中渲染动态获取html字符串!** **三:优化后代码(使用RequestDispatcher.include()和HttpServletResponseWrapper获取JSP内容)**

    上面举例子只是为了说明这个需求,下面贴出解决方案,若看博文的你有其他好的办法,可以添加右侧QQ群和我进行讨论!
    实现原理简单说明:

    在不跳转下访问目标jsp。就是利用RequestDispatcher.include(ServletRequest request, 
    
    ServletResponse response)。 该方法把RequestDispatcher指向的目标页面写到response中。
    
    利用HttpServletResponseWrapper封装HttpServletResponse,使HttpServletResponse采用我们自己定
    
    义的输入流(OutputStream)。
    
    这样,我们就可以通过这个OutputStream得到目标jsp页面内容。
    

    代码如下:

    a:优化Controller

    package com.dufy.web;
    
    import com.aebiz.pub.util.JspToHtmlUtil;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.net.URLEncoder;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * Created by dufy on 2017/3/21.
     */
    @Controller
    public class JsptoHtmlController {
    
    
        @RequestMapping("/index")
        public String testJsp(HttpServletResponse response,HttpServletRequest request){
            StringBuilder pageHtml = new StringBuilder();//使用StringBuilder拼接字符串效率高
            List<String> list = new ArrayList<>();
            list.add("/WEB-INF/jsp/welcome/hello.jsp");
            list.add("/WEB-INF/jsp/welcome/world.jsp");
            try {
                if (list != null && list.size() > 0) {
                    for (String str : list
                            ) {
                        Map<String,String> params = new HashMap<String,String>();
                        params.put(str,str);//put(hello,hello);
                        //如果参数中url 需要使用URLEncoder
                        params.put("url",URLEncoder.encode("http://blog.csdn.net/u010648555"));
    
                        request.setAttribute("params",params);
                        String jspOutput = JspToHtmlUtil.getJspOutput(str, request, response);
                        pageHtml.append(jspOutput);
                    }
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            request.setAttribute("pageHtml",pageHtml);
            return "welcome/index";
        }
    
    
    }
    
    

    b:JspToHtmlUtil

    package com.dufy.util;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * Created by dufy on 2017/3/22.
     */
    public class JspToHtmlUtil {
    
        /**
         * 根据JSP所在的路径获取JSP的内容<br/>
         * 在不跳转下访问目标jsp。就是利用RequestDispatcher.include(ServletRequest request, ServletResponse response)。
         * 该方法把RequestDispatcher指向的目标页面写到response中。
         * @param jspPath jsp路径
         * @param request HttpServletRequest对象
         * @param response HttpServletResponse对象
         * @return
         * @throws Exception
         */
        public static String getJspOutput(String jspPath, HttpServletRequest request, HttpServletResponse response)
                throws Exception
        {
            WrapperResponse wrapperResponse = new WrapperResponse(response);
            request.getRequestDispatcher(jspPath).include(request, wrapperResponse);
            return wrapperResponse.getContent();
        }
    }
    
    

    c:WrapperResponse .java

    package com.dufy.util;
    
    import org.apache.log4j.Logger;
    
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpServletResponseWrapper;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.io.UnsupportedEncodingException;
    
    /**
     * Created by dufy on 2017/3/21.<p/>
     *利用HttpServletResponseWrapper封装HttpServletResponse,使HttpServletResponse采用我们自己定义的输入流(OutputStream)。<p/>
     * 这样,我们就可以通过这个OutputStream得到目标jsp页面内容。
     */
    public class WrapperResponse extends HttpServletResponseWrapper {
        private Logger log = Logger.getLogger(WrapperResponse.class);
    
        private MyPrintWriter tmpWriter;
        private ByteArrayOutputStream output;
        public WrapperResponse(HttpServletResponse httpServletResponse) {
            super(httpServletResponse);
            output = new ByteArrayOutputStream();;// 真正存储数据的返回流(保存数据返回的结果)
            tmpWriter = new MyPrintWriter(output);
        }
    
        public String getContent() {
            String str = "";
            try {
                //刷新该流的缓冲,详看java.io.Writer.flush()
                tmpWriter.flush();
                str = tmpWriter.getByteArrayOutputStream().toString("utf-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                log.error("不支持的编码异常,Unsupported Encoding Exception!");
            }finally {
                try {
                    output.close();
                    tmpWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    log.error("释放资源异常,release resource error!");
                }
    
            }
            return str;
        }
    
        //覆盖getWriter()方法,使用我们自己定义的Writer
        public PrintWriter getWriter() throws IOException {
            return tmpWriter;
        }
    
        public void close() throws IOException {
            tmpWriter.close();
        }
    
        //自定义PrintWriter,为的是把response流写到自己指定的输入流当中
        //而非默认的ServletOutputStream
        private static class MyPrintWriter extends PrintWriter {
            ByteArrayOutputStream myOutput;
            //此即为存放response输入流的对象
    
            public MyPrintWriter(ByteArrayOutputStream output) {
                super(output);
                myOutput = output;
            }
    
            public ByteArrayOutputStream getByteArrayOutputStream() {
                return myOutput;
            }
    
        }
    }
    

    d:Jsp保持不变,和之前一样,不需要进行修改

    四:why write this article,why not use http request ?

    1:使用Http发起请求返回数据,这是最容易想到的方法,实现起来也简单!(只是感觉简单而已)
    2:使用Http发起请求,涉及到网络层,此时就会有网络问题出现,网络策略有问题的话,可能导致无法访问数据,假若网络是通的,http请求也是有延时(延时在网络中存在很常见,可能导致代码因为超时出错)。
    3:通过上面两点,也就是为什么不适用Http请求返回数据,也就是这篇博文为了实现(优化)功能需要而进行的思考!

    4:建议 系统内部调用,最好不要用Http,如果可以的话,请使用rpc的方式,效果和使用起来更好!

    五:参考文章


    欢迎访问我的csdn博客,我们一同成长!

    "不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!"

    博客首页http://blog.csdn.net/u010648555

  • 相关阅读:
    影视感悟
    缩写字母
    从工程文化和运维理念理解Netflix
    telinit:Did not receive a reply.Possible causes include:the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired
    centos6 ext4修复
    windows显示日期时间(精确到秒)
    【C#】IDispose接口的应用
    【转】【WPF】WPF 自定义快捷键命令(Command)
    【转】【WPF】MVVM模式的3种command
    【转】【WPF】WriteableBitmap应用及图片数据格式转换
  • 原文地址:https://www.cnblogs.com/aflyun/p/6603107.html
Copyright © 2020-2023  润新知