新手求教,获取 bean 可以 return this 么

2022-09-30 09:47:06 +08:00
 NoKey

有一个 bean

@Component
public class DemoParam {
}

然后,我有个类,不能通过注入的方式拿到这个 bean 的对象

常规的方式是通过 context 去获取

但是我觉得这样写出来不好看,还无法复用

于是我在DemoParam中写了几个方法

 private static DemoParam demoParam;
 @PostConstruct
 public void init(){
 	demoParam = this;
 }
 public static DemoParam instance(){
 	return demoParam;
 }

这样,我只需要调用instance()方法,就可以拿到 spring 创建的这个 bean 对象

请教一下大家,这样有没啥弊端呢,谢谢

2921 次点击
所在节点    程序员
31 条回复
Bronya
2022-09-30 10:04:43 +08:00
应该需要注意一下调用的先后顺序吧,instance()是静态方法,如果调用时间比 bean 初始化还早就空指针了。
(我也是菜鸟 0.0 )
NoKey
2022-09-30 10:06:45 +08:00
@Bronya @PostConstruct 这个可以保证是在构造函数执行完成后执行,那就是对象已经创建完成了吧
zmal
2022-09-30 10:07:55 +08:00
如果是单例的,这样写使用上没啥问题。只是说不能替换实现了。
rookie4show
2022-09-30 10:08:23 +08:00
public static DemoParam instance(){
return context.getBean("");
}
Bronya
2022-09-30 10:12:55 +08:00
@NoKey #2 spring 容器起来之后就没啥问题,我指的是 spring 启动过程中,也就是这个 bean 还没初始化的时候,你就调用了那就肯定获取不到,当然这种情况估计比较少(因为不知道你这个 bean 是干啥的)。

另外 4 楼的方式每次都会查找一次,反而不如楼主的方式吧(个人感觉)。
facelezz
2022-09-30 10:20:47 +08:00
理论上讲肯定是不对的,demoParam 对不同的线程没有 JMM 的约束,那么调用者拿到的值可能是"this"也可能是 null (即使 demoParam = this 先执行)
GuuJiang
2022-09-30 10:25:05 +08:00
不可以,很多时候 context.getBean 拿到的是一个经过了代理的对象,这也是 Spring 的各种黑科技能够发挥作用的基础,你这样的方式拿到的 bean 和 context.getBean 拿到的连 class 都不一样
BQsummer
2022-09-30 10:31:50 +08:00
7L 说的对, 可以试试自己注入自己, instance()返回注入的对象
facelezz
2022-09-30 10:47:34 +08:00
而且感觉属于 "有问题 A 自己想通过 B 解决 来论坛问 B 怎么实现" 建议说明为什么不能通过注入的方式 避免设计问题
NoKey
2022-09-30 10:59:23 +08:00
@GuuJiang 我试了一下,写了一个测试代码

@Autowired
DemoParam demoParam;
@Test
public void testest(){
DemoParam myDemo = DemoParam.instance();
System.out.println(demoParam);
System.out.println(myDemo);

DemoParam myDemo2 = new DemoParam();
System.out.println(myDemo2);
}

就是把注入的,this 返回的,new 的 几个对象打印出来
得到的结果是

com.demo.mydemo.controller.Vo.DemoParam@134d7ffa
com.demo.mydemo.controller.Vo.DemoParam@134d7ffa
com.demo.mydemo.controller.Vo.DemoParam@3cc817bd

也就是自动注入的和 this 返回的是同一个对象?
zmal
2022-09-30 10:59:25 +08:00
写代码测试了下,7L 说的对。
NoKey
2022-09-30 11:03:33 +08:00
@facelezz 调用方的类,是一个普通类,不是 bean ,无法自动注入,所以得获取一次这个 bean 的对象
zmal
2022-09-30 11:04:09 +08:00
@NoKey 你把你的 DemoParam 写个方法加个事务注解再看看是不是一个对象。
spring 的大部分功能是通过 AOP 实现的,AOP 可以是编译时,也可以是运行时。
NoKey
2022-09-30 11:04:11 +08:00
@zmal 你看我 10 楼发的测试方法对不对
facelezz
2022-09-30 11:05:40 +08:00
@NoKey 7L 说的是 你实在想要也只能 context.getBean 因为你的 DemoParam 如果有类似事务或者其他增强功能的注解 你拿到的 this 是源对象,getBean 返回的是代理对象 你的这个测试说明不了什么问题。

此外你的 instance 代码 本身就是错的 没什么意义
BQsummer
2022-09-30 11:05:44 +08:00
@NoKey 你在类上加个事务注解就会发现对象不一样了
NoKey
2022-09-30 11:07:12 +08:00
@facelezz 啊,请问一下,instance 代码 本身就是错的 这个怎么理解,谢谢
bianjp
2022-09-30 11:09:26 +08:00
Spring 并不会对所有 bean 做代理,只有使用到了一些功能时才会,比如 `@Transactional`, `@Validated`, `@Async`。

可以看下 `org.springframework.beans.factory.config.BeanPostProcessor` 接口,这个接口允许对 bean 实例做处理,然后返回封装 /代理后的对象。
facelezz
2022-09-30 11:12:24 +08:00
@NoKey 执行 demoParam = this 的线程 A 和 执行 instance()的线程 B 没有 happen-before 关系 B 可能永远看见的都是 null
zmal
2022-09-30 11:16:49 +08:00
@NoKey 我们平时写的 XXXservice 这是个源类,在 spring 容器里,spring 会对它进行各种增强,增强依赖于动态代理或 CGLIB 技术,不管底层原理是什么,最终都是生成了源类的子类这种方式。
这就意味着你从 context 中拿到的 bean 和 源对象 this 返回的 bean 不一定是同一个。

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

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

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

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

© 2021 V2EX