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

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("\n" + 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();
    }
}

注: RequestDispatcher接口中定义了两个方法forward和include,这两个方法都可以将第一个Servlet的请求转发给第二个Servlet,所不同的是forward方法在转发请求后,第一个Servlet的响应终止而由第二个Servlet负责响应输出,而include方法则是在转发请求并且第二个Servlet响应后将响应并入第一个Servlet中,仍然由第一个Servlet负责响应输出(注意调用include方法时两个Servlet的响应输出编码最好保持一致否则可能会出现乱码.

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/121174.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • OC动态创建的问题变量数组.有数组,在阵列13要素,第一个数据包阵列,每3元素为一组,分成若干组,这些数据包的统一管理。最后,一个数组.(要动态地创建一个数组).两种方法

    OC动态创建的问题变量数组.有数组,在阵列13要素,第一个数据包阵列,每3元素为一组,分成若干组,这些数据包的统一管理。最后,一个数组.(要动态地创建一个数组).两种方法

    2022年1月12日
    36
  • mysql聚集索引和覆盖索引_索引快速全扫描

    mysql聚集索引和覆盖索引_索引快速全扫描查询优化:索引覆盖扫描——当索引中的列包含所有查询中要使用的列的时候,就会用到覆盖索引,效率比较高。因为尽量使select后面的字段是where中的索引字段。…

    2022年10月21日
    0
  • pinn求解ode_pt_pin

    pinn求解ode_pt_pin今天硕士论文开题答辩,想着学了这么长时间的PINN,七七八八也看了一些文献,一来是为了整理思路,二来可以方便以后回顾复习。使用PINN求解PDE与传统有限元、有限差分、谱方法等最大的区别是,无需做预先的假设,线性化和网格化。求解一般的偏微分方程的形式:PINN具体算法步骤如下:其中只有初边界训练数据包含u的值,内部配置点不包含u的值,只有定义域内部的点。(这一点一直迷糊,最近才理清楚)。PINN求解PDE框架图:可以通过自动微分最小化损失函数,得到神经网络最优参数,从而的到.

    2022年10月27日
    0
  • STM32独立看门狗实验

    STM32独立看门狗实验一为什么需要看门狗?在MCU微型计算机系统中,MCU运行程序很容易受到外界电磁场的干扰,从而造成程序运行错误甚至发生跑飞现象,从而陷入死循环,程序的正常运行被打乱,从而造成不可预料的严重后果,于是人们就设计了一款用于实时监测计算机运行状态的芯片,我们就将其称为“看门狗(WatchDog)”。二看门狗的作用看门狗在我们程序正常工作的时候是不工作的,也就是说他根本没有任何作用,只有在单片…

    2022年5月18日
    110
  • Percona Data Recovery Tool 单表恢复

    Percona Data Recovery Tool 单表恢复Percona Data Recovery Tool 单表恢复

    2022年4月20日
    37
  • 冰蝎下的反弹shell连接msfconsole

    冰蝎下的反弹shell连接msfconsole文章目录前言一、使用木马getshell1.搭建环境二、冰蝎配置三、kali监听总结前言好久没碰美少妇(MSF)了,恰巧昨天在群里水群,有个表哥问为什么msf监听不到数据。为此我带着表哥的疑问进行了简单的研究。大体的流程和思路我简单记录一下。其中的坑还是不少的,希望这篇文章对初识冰蝎的表哥们有点用处。一、使用木马getshell冰蝎之所以强还是在于他的动态二进制加密。这里呢,在冰蝎下载的包中给出了官方的webshell。在server文件夹下。这里呢,我踩过一个坑。不知道是我电脑配置的问题还是就

    2022年9月7日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号