为什么在SpringMVC和SpringBoot中可以直接注入HttpServletRequest

如题:为什么在SpringMVC和SpringBoot中可以直接注入HttpServletRequest对象。

代码示例

@Autowired
private HttpServletRequest request;

@GetMapping(value = "/rest/current")
public ReturnResult current() {
    System.out.println(request.getRequestURL().toString());
    JSONObject res = new JSONObject();
    res.put("requestId", UUID.randomUUID().toString());
    res.put("datatime", DateUtils.getDatetime());
    return ReturnResult.get(res);
}

理论上,我们需要注入其他包中的对象的时候,是必须在@Configuration修饰的类下面使用@Bean进行方法修饰的才行的,如下所示:

@Configuration
public class ApplicationConfig {
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

那为什么HttpServletRequest这个对象如此优秀,可以直接注入呢?

阅读 4.4k
3 个回答

它这么秀的根本原因是因为……注入的并不是真正的 HttpServletRequest,只是一个代理对象,其背后的核心对象是 org.springframework.web.context.support.WebApplicationContextUtils.RequestObjectFactory

你看这个类长啥样:

private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {

        @Override
        public ServletRequest getObject() {
        
                // 获取当前 ServletRequest
            return currentRequestAttributes().getRequest();
        }

        @Override
        public String toString() {
            return "Current HttpServletRequest";
        }
    }
    
    
    private static ServletRequestAttributes currentRequestAttributes() {
        RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
        if (!(requestAttr instanceof ServletRequestAttributes)) {
            throw new IllegalStateException("Current request is not a servlet request");
        }
        return (ServletRequestAttributes) requestAttr;
    }

那这个“当前 ServletRequest”从哪来呢?

Servlet 除了 ServletContextListener 之外,还有一个 ServletRequestListener

public interface ServletRequestListener extends EventListener {
    void requestDestroyed(ServletRequestEvent var1);

    void requestInitialized(ServletRequestEvent var1);
}

只要你注册了这个,就能在请求前执行requestDestroyed

Spring 弄了一个 org.springframework.web.context.request.RequestContextListener,在这个里面获取并存储的

@Override
    public void requestInitialized(ServletRequestEvent requestEvent) {
        if (!(requestEvent.getServletRequest() instanceof HttpServletRequest)) {
            throw new IllegalArgumentException(
                    "Request is not an HttpServletRequest: " + requestEvent.getServletRequest());
        }
        HttpServletRequest request = (HttpServletRequest) requestEvent.getServletRequest();
        ServletRequestAttributes attributes = new ServletRequestAttributes(request);
        request.setAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE, attributes);
        LocaleContextHolder.setLocale(request.getLocale());
        
        // 存储 ServletRequest
        RequestContextHolder.setRequestAttributes(attributes);
    }

特殊情况特殊处理。不要把ioc死板的搬过来。

Request, Response, HttpEntity等都是特殊处理的。而且这几个对象和请求有关,不在窗口里面。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏