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();
}
}
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的方式,效果和使用起来更好!
五:参考文章
用HttpServletResponseWrapper 获取jsp输出内容:http://fjohnny.iteye.com/blog/713929
利用HttpServletResponseWrapper操作Response对象:http://www.itzk.com/b/1108/581313.shtml
RequestDispathcher中forward()和include()的区别:http://blog.csdn.net/huo2007201019/article/details/7584241
欢迎访问我的csdn博客,我们一同成长!
"不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!"
博客首页:http://blog.csdn.net/u010648555
SpringMVC——使用RequestDispatcher.include()和HttpServletResponseWrapper动态获取jsp输出内容的更多相关文章
- php动态获取常量
class A1{ const V1='100'; const V2='200'; const V3='Hello world'; } $v1 = 'V3'; $a1 = constant('A1:: ...
- Spring Security3实现,权限动态获取
Spring Security3实现,权限动态获取 原文 http://blog.csdn.net/yangwei19680827/article/details/9359113 主题 网络安全Sp ...
- 旧书重温:0day2【4】动态获取函数地址
通过以上3篇文章的学习,我们已经可以获取到kernel32.dll的地址了下一步 我们就是获取几个重要的函数 1.GetProcAddress 2.LoadLibrary 有了这两个函数很多函数都可以 ...
- web应用路径问题(相对路径,绝对路径,动态获取路径)
1.相对路径和绝对路径 绝对路径:以 “ / ” 开头的路径,是完整的路径. 相对路径:不以 “ / ” 开头的路径,是相对于当前web资源目录的路径. 在绝对路径中, “ / ” 的含义有两种解释: ...
- js动态获取子复选项并设计全选及提交
在做项目的时候,会遇到根据父选项,动态的获取子选项,并列出多个复选框,提交时,把选中的合并成一个字符提交后台 本章将讲述如何通过js控制实现该操作: 1:设计父类别为radio,为每一个radio都加 ...
- 日志系统实战(二)-AOP动态获取运行时数据
介绍 这篇距上一篇已经拖3个月之久了,批评自己下. 通过上篇介绍了解如何利用mono反射代码,可以拿出编译好的静态数据.例如方法参数信息之类的. 但实际情况是往往需要的是运行时的数据,就是用户输入等外 ...
- Js动态获取iframe子页面的高度////////////////////////zzzz
Js动态获取iframe子页面的高度 Js动态获取iframe子页面的高度总结 问题的缘由 产品有个评论列表引用的是个iframe,高度不固定于是引发这个总结. 方法1:父级页面获取子级页面的高度 ...
- map阶段动态获取CombineTextInputFormat各输入文件路径
老mr程序中map中conf的map.input.file参数只能获取获取CombineTextInputFormat的第一个输入文件,而新版mr程序则连第一个输入文件也无法获取,这是因为create ...
- 动态获取Android权限
@TargetApi(23)private void showPhotoDialog() { if (dialog != null && dialog.isShowing()) { d ...
随机推荐
- thinkjs学习-this.assign传递数据和ajax调用后台接口
在页面加载时,就需要显示在页面上的数据,可以在后台使用this.assign赋值,在前台通过ejs等模板获取:用户点击按钮,或者触发某些事件和后台进行交互时,就需要用到ajax调用后台接口.本文通过一 ...
- static和final修饰方法
static修饰的方法是静态方法,所有的对象共用一份,也就是共享方法.static方法是可以被继承,然后可以被重写和重载. final修饰的方法是不可变方法,final方法所在类被继承时,被final ...
- [2012-06-29]sed根据行号范围执行替换
测试数据: personball@vostro:SHELL$cat aaa <instrumentation android:name="aaa" android:name= ...
- java中的ArrayList 使得集合中的对象不重复
JAVA中的List接口存放的元素是可以重复的,在这个我重写对象里面的equals()方法,让集合里存放的对象不能重复 首先建一个类,在里面的main()方法中实现 list1中存放的是可以重复对象的 ...
- selenium系列------元素定位套路
selenium定位分为上三门,平三门,下三门, id,name,linktext上三门, class ,css,js平三门, xpath,tag名,复数定位(定位一组然后选index元素).
- py2 HTMLTestRunner报告
直接上代码吧. #coding:utf-8 #__author__ = 'carry' import unittest,HTMLTestRunner class Hello(unittest.Test ...
- macaca web(4)
米西米西滴,吃过中午饭来一篇,话说,上回书说道macaca 测试web(3),参数驱动来搞,那么有小伙本又来给雷子来需求, 登录模块能不能给我给重新封装一下吗, 我说干嘛封装,现在不挺好,于是乎,接着 ...
- java面向对象(三)之抽象类,接口,向上转型
java类 java类分为普通类和抽象类,接口,上一节我大概讲了java类的一般格式,今天将抽象类和接口.同时讲一下它们是怎样存储的. 最重要的是理解为什么要有抽象和接口,这样学下来你猜不会迷茫,才能 ...
- spring事务详解
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt122 Spring事务机制主要包括声明式事务和编程式事务,此处侧重讲解声明式 ...
- Java内存模型:volatile详解
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt202 Java内存模型:volatile是干什么用的Volatile字段是用 ...