Controller 和 Service 中注入 HttpServletResponse 有什么差异吗

2022-04-06 17:06:55 +08:00
 twofox

项目里需要导出一个文件。一开始我使用的在 Service 的实现类里面注入 response

//serviceImpl

//服务类注入
@Autowired
private HttpServletResponse response;


public void export(params){
    // ...
}

然后就很常规的打开流,获取文件,写入,关闭 但是无法通过项目的网关鉴权,报了一个 null 错误。

但是如果是在 controller 里面注入,再直接传给 Service ,就可以通过鉴权。 就像这样。

//Controller 注入

@Autowired
private HttpServletResponse response;

public void export(params){

	//传给 service
    
	exportSevice.export(params, response);
}

请问这两种方式注入的差别在哪?

2330 次点击
所在节点    Java
18 条回复
chendy
2022-04-06 17:23:28 +08:00
service 层不应该依赖 HttpServletResponse
应该是 controller 层接 HttpServeltResponse ,serivce 层接 OutputStream

至于鉴权啥的只能说不了解,但是这个注入方法真的有点厉害……梦回 struts2 了
darkengine
2022-04-06 17:26:02 +08:00
service 的实现类前有没有加 @ Component 注解?
zmal
2022-04-06 17:32:41 +08:00
response 也能注入啊,还真不知道...
paradoxs
2022-04-06 17:33:21 +08:00
为啥要这样注入
shanghai1943
2022-04-06 18:07:44 +08:00
这也能注入。。涨知识了。。
TWorldIsNButThis
2022-04-06 18:13:11 +08:00
为什么注入成成员变量,不是应该是方法参数吗
不同请求过来每次都会改这个字段?
wolfie
2022-04-06 18:18:21 +08:00
struts2 写法,strut2 升级到 springmvc 时候,不少架构这么玩,配合 spring 默认单例 可能串会话。
night98
2022-04-07 00:26:04 +08:00
信息量太少,简单说一下,controller 是注入在 springmvc 容器里的,所以应该可以正常拿到 response ,service 层用的 spring 容器,所以拿不到,会报 null 异常,至于鉴权应该是在 request 期间鉴权的,大致应该是这样子的,
EscYezi
2022-04-07 02:46:19 +08:00
还真没在成员变量注入过 response ,不过想了想都是单例,并发可能会有问题吧?
twofox
2022-04-07 08:59:15 +08:00
@chendy 因为这是公司的项目。。同个 service 里面有类似的写法,为了保持风格一致我就这么写了。service 层确实不应依赖 Response ,我写自己的项目的时候都是传参进去的=.=


@darkengine 加了 @Service 注解


@TWorldIsNButThis 可能是为了少写个参数?


@night98 很有可能,下班了试一下
cnzjl
2022-04-07 09:23:18 +08:00
啊,我刚入行的时候就是这么注入的,比较方便
nothingistrue
2022-04-07 13:46:05 +08:00
HttpServletResponse 是绑定当前连接的有状态对象,Service 通常是无状态 Bean ,Controller 通常是( Spring MVC 就绝对是)有状态 Bean ,因此,HttpServletResponse 注入 Controller (的成员)没问题,注入 Service (的成员)会产生严重问题。

你如果注入 Service ,那么第一个请求来了会把当前请求的 HttpServletResponse 注入单例的 Service 。单例的 Service 中的这个成员,一旦注入过就不会再重新注入了,以后所有的请求调用的 Service ,使用的都是第一个请求的 HttpServletResponse 。这个 HttpServletResponse 对象本身因为还被引用着不会被回收,但它里面引用的其他对象,会在第一个请求完成之后就销毁,于是后面就会出现 NullPointException 。


最后说一点,exportSevice.export(params, response) 这样的方法,即把依赖对象通过方法参数传入,不是用 Spring 实现的依赖注入,但它也是依赖注入。
twofox
2022-04-07 15:29:10 +08:00
@nothingistrue 那正确的写法呢?
exportSevice.export(params, response.getOutputStream())
nothingistrue
2022-04-07 16:27:03 +08:00
你的第二次使用已经正确了:注入到 Controller ,再传递给 Service 。Controller 是非单例的有状态 Bean ,可以注入 HttpServletResponse 。 @twofox

其实对于 HttpServletResponse 这种跟当前请求绑定的对象,最好全程通过方法参数注入,不要将他注入到类的成员变量上。Spring MVC 中,如果把 HttpServletResponse 作为 Controller 的方法的参数,不需要加 Autowired 它都是自动注入的。
codergrowing
2022-04-07 18:08:41 +08:00
@nothingistrue #14 「 Controller 是非单例的有状态 Bean 」?不对吧,Controller 默认应该也是单例的。
nothingistrue
2022-04-08 09:16:52 +08:00
@codergrowing #15 可能我记错了,不过 Spring Boot 之后,Spring MVC 部分推荐的都是通过方法参数注入,已经基本不用成员变量注入了,这种注入单例非单例都没区别。一般来说,从 Struts2 之后,控制器部分都是单例的,可能 Spring MVC 发现没有成员变量注入之后会自动优化成单例模式。
twofox
2022-04-08 15:32:59 +08:00
@nothingistrue 感谢答疑!
golangLover
2022-04-28 09:21:30 +08:00

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/845285

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX