有一个 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 对象
请教一下大家,这样有没啥弊端呢,谢谢
1
Bronya 2022-09-30 10:04:43 +08:00
应该需要注意一下调用的先后顺序吧,instance()是静态方法,如果调用时间比 bean 初始化还早就空指针了。
(我也是菜鸟 0.0 ) |
2
NoKey OP @Bronya @PostConstruct 这个可以保证是在构造函数执行完成后执行,那就是对象已经创建完成了吧
|
3
zmal 2022-09-30 10:07:55 +08:00
如果是单例的,这样写使用上没啥问题。只是说不能替换实现了。
|
4
rookie4show 2022-09-30 10:08:23 +08:00
public static DemoParam instance(){
return context.getBean(""); } |
5
Bronya 2022-09-30 10:12:55 +08:00
@NoKey #2 spring 容器起来之后就没啥问题,我指的是 spring 启动过程中,也就是这个 bean 还没初始化的时候,你就调用了那就肯定获取不到,当然这种情况估计比较少(因为不知道你这个 bean 是干啥的)。
另外 4 楼的方式每次都会查找一次,反而不如楼主的方式吧(个人感觉)。 |
6
facelezz 2022-09-30 10:20:47 +08:00
理论上讲肯定是不对的,demoParam 对不同的线程没有 JMM 的约束,那么调用者拿到的值可能是"this"也可能是 null (即使 demoParam = this 先执行)
|
7
GuuJiang 2022-09-30 10:25:05 +08:00 1
不可以,很多时候 context.getBean 拿到的是一个经过了代理的对象,这也是 Spring 的各种黑科技能够发挥作用的基础,你这样的方式拿到的 bean 和 context.getBean 拿到的连 class 都不一样
|
8
BQsummer 2022-09-30 10:31:50 +08:00
7L 说的对, 可以试试自己注入自己, instance()返回注入的对象
|
9
facelezz 2022-09-30 10:47:34 +08:00
而且感觉属于 "有问题 A 自己想通过 B 解决 来论坛问 B 怎么实现" 建议说明为什么不能通过注入的方式 避免设计问题
|
10
NoKey OP @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 返回的是同一个对象? |
11
zmal 2022-09-30 10:59:25 +08:00
写代码测试了下,7L 说的对。
|
13
zmal 2022-09-30 11:04:09 +08:00
@NoKey 你把你的 DemoParam 写个方法加个事务注解再看看是不是一个对象。
spring 的大部分功能是通过 AOP 实现的,AOP 可以是编译时,也可以是运行时。 |
15
facelezz 2022-09-30 11:05:40 +08:00
@NoKey 7L 说的是 你实在想要也只能 context.getBean 因为你的 DemoParam 如果有类似事务或者其他增强功能的注解 你拿到的 this 是源对象,getBean 返回的是代理对象 你的这个测试说明不了什么问题。
此外你的 instance 代码 本身就是错的 没什么意义 |
18
bianjp 2022-09-30 11:09:26 +08:00
Spring 并不会对所有 bean 做代理,只有使用到了一些功能时才会,比如 `@Transactional`, `@Validated`, `@Async`。
可以看下 `org.springframework.beans.factory.config.BeanPostProcessor` 接口,这个接口允许对 bean 实例做处理,然后返回封装 /代理后的对象。 |
19
facelezz 2022-09-30 11:12:24 +08:00
@NoKey 执行 demoParam = this 的线程 A 和 执行 instance()的线程 B 没有 happen-before 关系 B 可能永远看见的都是 null
|
20
zmal 2022-09-30 11:16:49 +08:00
@NoKey 我们平时写的 XXXservice 这是个源类,在 spring 容器里,spring 会对它进行各种增强,增强依赖于动态代理或 CGLIB 技术,不管底层原理是什么,最终都是生成了源类的子类这种方式。
这就意味着你从 context 中拿到的 bean 和 源对象 this 返回的 bean 不一定是同一个。 |
21
NoKey OP @facelezz 单就这个问题进行讨论,我的想法是,把赋值放在 @PostConstruct 中,这个是 servlet 执行时必然调用一次,那么只要是服务可用状态,赋值肯定被执行了,然后,在单例模式下,这个就是对一个对象变量,应该没有线程安全问题
|
23
zmal 2022-09-30 11:22:20 +08:00
@facelezz 这里没有 happen-before 问题,@PostConstruct 注解在 springboot 启动该对象初始化后已经执行了。
happen-before 也不是这么用的。 |
24
facelezz 2022-09-30 11:29:28 +08:00
@zmal 你说的应该是 实例方法下 获取没这个问题 因为注入时的获取 是 spring 在 map 上加锁才有先后顺序(lock-unlock) 楼主直接请求的静态方法静态变量 正常来讲是没有这个保证的
|
25
facelezz 2022-09-30 11:33:21 +08:00
@zmal
@NoKey 对这个问题有疑惑的话 可以参考 https://stackoverflow.com/questions/23906808/should-i-mark-object-attributes-as-volatile-if-i-init-them-in-postconstruct-in 里面讲了一般情况下 为什么没有可见性( happen-before )问题 |
26
Bronya 2022-09-30 13:30:27 +08:00
所以结论是啥呀,楼主这么写到底有没有问题,是不是在没有增强时(@Transactional 或者其他 aop )楼主这么写也可以的?
|
28
siweipancc 2022-09-30 17:24:22 +08:00 via iPhone
会寄,但是可以多测试一下,生命周期暂时稳定(服务器顺序不定)就行,爆了再说。
|
29
siweipancc 2022-09-30 17:37:23 +08:00 via iPhone
上边的提到非唯一问题,只要保证单栗模式+不允许覆盖定义就行。你这样写完全没有问题。
甚至可以定义成内部静态字段=context.get(),只要第一次方法引用时 app ready 就行 |
30
duteliang 2022-09-30 18:43:10 +08:00
不会有问题的,但是要保证这个静态方法不要在项目启动,初始化的时候调用。不然因为 bean 的加载顺序问题有概率空指针。
|
31
liuzhaowei55 2022-09-30 18:56:44 +08:00 via iPhone
直接写个单例,然后自己 new 这个类?现在这样怪怪的,或者参考下许多 BeanUtils 的写法,都可以不用注入 context 静态获取实例,关键字如 cola- domains
|