GVKun编程网logo

ThreadLocal是否比HttpServletRequest.setAttribute(“ key”,“ value”)更好?(threadlocal与thread)

9

在本文中,我们将给您介绍关于ThreadLocal是否比HttpServletRequest.setAttribute的详细内容,并且为您解答“key”,“value”更好?的相关问题,此外,我们还将

在本文中,我们将给您介绍关于ThreadLocal是否比HttpServletRequest.setAttribute的详细内容,并且为您解答“ key”,“ value”更好?的相关问题,此外,我们还将为您提供关于Handler.handle (String target, HttpServletRequest request, HttpServletResponse response, boolean [] isHandled) 最后一个为什么是数组?、HttpServletRequest & HttpServletResponse 中 Body 的获取、HttpServletRequest.getHeader("X-Real-IP")报错java.lang.IllegalStateException、HttpServletRequest.getParameterValues()中值的排序的知识。

本文目录一览:

ThreadLocal是否比HttpServletRequest.setAttribute(“ key”,“ value”)更好?(threadlocal与thread)

ThreadLocal是否比HttpServletRequest.setAttribute(“ key”,“ value”)更好?(threadlocal与thread)

Servlet规范(请参阅我的上一个问题)保证同一线程将执行所有过滤器和关联的Servlet。鉴于此,HttpServletRequest.setAttribute如果可以使用a选项ThreadLocal(假设您正确清理),我看不到使用传递数据有任何用处。我觉得使用有两个好处ThreadLocal:类型安全和更好的性能,因为没有使用任何字符串键或映射(除非可能是通过(非字符串)线程id进入线程集合)。

有人可以确认我是否正确,以便我可以放弃setAttribute吗?

答案1

小编典典

ThreadLocal是否比HttpServletRequest.setAttribute(“ key”,“ value”)更好?

取决于具体的功能要求。

例如,JSF将储存在FacesContextThreadLocal。这样,您就可以访问所有JSF工件,包括“原始”对象HttpServletRequest以及HttpServletResponse由所执行的代码中的任何位置FacesServlet,例如托管Bean。大多数其他基于Java的MVC框架都遵循相同的示例。

根据您的评论,

我首先需要将User和EntityManager对象从用户和数据库过滤器传输到Servlet。
我还发现,在更深层的代码中经常需要这些功能,而我出乎意料的是,我很想在Servlet之外使用它们(即在doGet调用的嵌套代码中)。我觉得可能有更好的方法来编写更深入的代码-
建议?

User对于我假设这是一个会话属性的示例,我宁愿采用与JSF相同的方法。ThreadLocal<Context>Context您的自定义包装器类中创建一个,其中包含对当前类的引用HttpServletRequest,也许还HttpServletResponse包含对它们的引用,以便您可以在代码中的任何位置访问它们。如有必要,请提供方便的方法来User直接从Context课堂中获取其他信息。

至于EntityManager例如,您可以按照同样的方法,但我个人不把它放在 同一个
ThreadLocal<Context>,而是一个不同。或者更好的是,只需从服务层的JNDI获取它,这将使您对事务进行更精细的控制。无论如何,请绝对确保正确处理提交/关闭。从容器中接管持久性和事务管理应该格外小心。我真的会重新考虑避免使用现有的和设计良好的EJB
/ JPA这样的API /框架,否则您将冒着完全浪费时间重新发明所有已经标准化的API和内容的风险。

Handler.handle (String target, HttpServletRequest request, HttpServletResponse response, boolean [] isHandled) 最后一个为什么是数组?

Handler.handle (String target, HttpServletRequest request, HttpServletResponse response, boolean [] isHandled) 最后一个为什么是数组?

OSC 请你来轰趴啦!1028 苏州源创会,一起寻宝 AI 时代

@jfinal

HttpServletRequest & HttpServletResponse 中 Body 的获取

HttpServletRequest & HttpServletResponse 中 Body 的获取

获取 HttpServletRequest 中的请求体

    HttpServletRequest#getInputStream() 获取到请求的输入流,从该输入流中可以读取到请求体。不过这个流在被我们的代码 read 过后,之后的代码就会报错,因为流已经被我们读取过了 , 尝试使用 mark() , reset() 也是不行的,会抛出异常。可以通过将 HttpServletRequest 对象包装一层的方式来实现这个功能。

package org.hepeng.commons.http;

import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.commons.io.IOUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * 
 * @author he peng
 * @date 2018/9/11
 */
public class BodyCachingHttpServletRequestWrapper extends HttpServletRequestWrapper {


    private byte[] body;
    private ServletInputStreamWrapper inputStreamWrapper;

    public BodyCachingHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        this.body = IOUtils.toByteArray(request.getInputStream());
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.body);
        this.inputStreamWrapper = new ServletInputStreamWrapper(byteArrayInputStream);
        resetInputStream();
    }

    private void resetInputStream() {
        this.inputStreamWrapper.setInputStream(new ByteArrayInputStream(this.body != null ? this.body : new byte[0]));
    }

    public byte[] getBody() {
        return body;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return this.inputStreamWrapper;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.inputStreamWrapper));
    }


    @Data
    @AllArgsConstructor
    private static class ServletInputStreamWrapper extends ServletInputStream {

        private InputStream inputStream;

        @Override
        public boolean isFinished() {
            return true;
        }

        @Override
        public boolean isReady() {
            return false;
        }

        @Override
        public void setReadListener(ReadListener readListener) {

        }

        @Override
        public int read() throws IOException {
            return this.inputStream.read();
        }
    }
}

 

获取 HttpServletResponse 中的响应体

    通过使用 ByteArrayOutputStream 将原 HttpSevletResponse 进行一层包装就可以实现。ByteArrayOutputStream 是将数据写入到它内部的缓冲区中,这样我们就可以获取到这个数据了。

package org.hepeng.commons.http;

import lombok.AllArgsConstructor;
import lombok.Data;

import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

/**
 * @author he peng
 * @date 2018/10/1
 */
public class BodyCachingHttpServletResponseWrapper extends HttpServletResponseWrapper {

    private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    private HttpServletResponse response;

    public BodyCachingHttpServletResponseWrapper(HttpServletResponse response) {
        super(response);
        this.response = response;
    }

    public byte[] getBody() {
        return byteArrayOutputStream.toByteArray();
    }

    @Override
    public ServletOutputStream getOutputStream() {
        return new ServletOutputStreamWrapper(this.byteArrayOutputStream , this.response);
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        return new PrintWriter(new OutputStreamWriter(this.byteArrayOutputStream , this.response.getCharacterEncoding()));
    }


    @Data
    @AllArgsConstructor
    private static class ServletOutputStreamWrapper extends ServletOutputStream {

        private ByteArrayOutputStream outputStream;
        private HttpServletResponse response;

        @Override
        public boolean isReady() {
            return true;
        }

        @Override
        public void setWriteListener(WriteListener listener) {

        }

        @Override
        public void write(int b) throws IOException {
            this.outputStream.write(b);
        }

        @Override
        public void flush() throws IOException {
            if (! this.response.isCommitted()) {
                byte[] body = this.outputStream.toByteArray();
                ServletOutputStream outputStream = this.response.getOutputStream();
                outputStream.write(body);
                outputStream.flush();
            }
        }
    }
}

flush() 函数是必须提供的 ,否则流中的数据无法响应到客户端 , ByteArrayOutputStream 没有实现 flush() 。像 SpringMVC 这类框架会去调用这个响应输出流中的 flush() 函数 ,而且有可能在出现多次调用的情况,多次调用会产生问题使得客户端得到错误的数据,比如这样的 :

{"errorCode":30001,"errorMsg":"用户未认证","token":null,"entity":null}{"errorCode":30001,"errorMsg":"用户未认证","token":null,"entity":null}  ,出现这种情况就说明 flush() 被调用了两次。所以需要在这里判断一下 HttpServletResponse#isCommitted()  。

获取请求体、相应体的包装类在 Filter 中的使用

package org.hepeng.commons.http.filter;

import com.tepin.commons.http.BodyCachingHttpServletRequestWrapper;
import com.tepin.commons.http.BodyCachingHttpServletResponseWrapper;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author he peng
 * @date 2018/10/2
 */
public class DemoFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        BodyCachingHttpServletRequestWrapper requestWrapper =
                new BodyCachingHttpServletRequestWrapper((HttpServletRequest) request);

        byte[] requestBody = requestWrapper.getBody();

        // TODO do something
        BodyCachingHttpServletResponseWrapper responseWrapper =
                new BodyCachingHttpServletResponseWrapper((HttpServletResponse) response);

        chain.doFilter(requestWrapper , responseWrapper);

        byte[] responseBody = responseWrapper.getBody();
        // TODO do something

    }

    @Override
    public void destroy() {

    }
}

 

HttpServletRequest.getHeader(

HttpServletRequest.getHeader("X-Real-IP")报错java.lang.IllegalStateException

定时任务(就一个定时任务)调一个service,service中注入了HttpServletRequest  request作为参数,定时任务进入方法时,参数request为Current HttpServletRequest,request.getHeader("X-Real-IP")时就报错了,两个类似的项目,配置基本相同,另一个却没事。希望各路大神能给指点一下迷津。

异常信息:

java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
        at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
        at org.springframework.web.context.support.WebApplicationContextUtils.currentRequestAttributes(WebApplicationContextUtils.java:271)
        at org.springframework.web.context.support.WebApplicationContextUtils.access$0(WebApplicationContextUtils.java:270)
        at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:286)
        at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:1)
        at org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke(AutowireUtils.java:178)

        .................

方法就是普通的方法:

public static String getRemoteHost(HttpServletRequest request){
		if(null != request){
			try {
				String ip = request.getHeader("X-Real-IP");
			    if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
			        ip = request.getHeader("x-forwarded-for");
			    }	
			    if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
			        ip = request.getHeader("Proxy-Client-IP");
			    }
			    if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
			        ip = request.getHeader("WL-Proxy-Client-IP");
			    }
			    if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
			        ip = request.getRemoteAddr();
			    }
			    return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
				return "127.0.0.1";
			}
		}else{
			return "127.0.0.1";
		}
	}

HttpServletRequest.getParameterValues()中值的排序

HttpServletRequest.getParameterValues()中值的排序

HttpServletRequest.getParameterValues()返回一个String[]包含给定HTTP请求参数的所有值的。有人知道规范中是否保证此数组中值的顺序与请求中这些值的传递顺序相同?

例如,如果我有GET查询字符串x=1&x=2&x=3,那么我 保证String[] {"1","2","3"}在致电时会收到getParameterValues()吗?它似乎在实践中可行,但是我找不到任何可以说明这种情况的东西,因此我不愿意依靠它。

今天关于ThreadLocal是否比HttpServletRequest.setAttribute“ key”,“ value”更好?的分享就到这里,希望大家有所收获,若想了解更多关于Handler.handle (String target, HttpServletRequest request, HttpServletResponse response, boolean [] isHandled) 最后一个为什么是数组?、HttpServletRequest & HttpServletResponse 中 Body 的获取、HttpServletRequest.getHeader("X-Real-IP")报错java.lang.IllegalStateException、HttpServletRequest.getParameterValues()中值的排序等相关知识,可以在本站进行查询。

本文标签: