
有一个 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 不可以,很多时候 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 PRO 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 |