新手求教,获取 bean 可以 return this 么 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
NoKey
V2EX    程序员

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

  •  
  •   NoKey 2022-09-30 09:47:06 +08:00 3439 次点击
    这是一个创建于 1186 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有一个 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 对象

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

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

    另外 4 楼的方式每次都会查找一次,反而不如楼主的方式吧(个人感觉)。
    facelezz
        6
    facelezz  
       2022-09-30 10:20:47 +08:00
    理论上讲肯定是不对的,demoParam 对不同的线程没有 JMM 的约束,那么调用者拿到的值可能是"this"也可能是 null (即使 demoParam = this 先执行)
    GuuJiang
        7
    GuuJiang  
       2022-09-30 10:25:05 +08:00   1
    不可以,很多时候 context.getBean 拿到的是一个经过了代理的对象,这也是 Spring 的各种黑科技能够发挥作用的基础,你这样的方式拿到的 bean 和 context.getBean 拿到的连 class 都不一样
    BQsummer
        8
    BQsummer  
       2022-09-30 10:31:50 +08:00
    7L 说的对, 可以试试自己注入自己, instance()返回注入的对象
    facelezz
        9
    facelezz  
       2022-09-30 10:47:34 +08:00
    而且感觉属于 "有问题 A 自己想通过 B 解决 来论坛问 B 怎么实现" 建议说明为什么不能通过注入的方式 避免设计问题
    NoKey
        10
    NoKey  
    OP
       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
        11
    zmal  
       2022-09-30 10:59:25 +08:00
    写代码测试了下,7L 说的对。
    NoKey
        12
    NoKey  
    OP
       2022-09-30 11:03:33 +08:00
    @facelezz 调用方的类,是一个普通类,不是 bean ,无法自动注入,所以得获取一次这个 bean 的对象
    zmal
        13
    zmal  
       2022-09-30 11:04:09 +08:00
    @NoKey 你把你的 DemoParam 写个方法加个事务注解再看看是不是一个对象。
    spring 的大部分功能是通过 AOP 实现的,AOP 可以是编译时,也可以是运行时。
    NoKey
        14
    NoKey  
    OP
       2022-09-30 11:04:11 +08:00
    @zmal 你看我 10 楼发的测试方法对不对
    facelezz
        15
    facelezz  
       2022-09-30 11:05:40 +08:00
    @NoKey 7L 说的是 你实在想要也只能 context.getBean 因为你的 DemoParam 如果有类似事务或者其他增强功能的注解 你拿到的 this 是源对象,getBean 返回的是代理对象 你的这个测试说明不了什么问题。

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

    可以看下 `org.springframework.beans.factory.config.BeanPostProcessor` 接口,这个接口允许对 bean 实例做处理,然后返回封装 /代理后的对象。
    facelezz
        19
    facelezz  
       2022-09-30 11:12:24 +08:00
    @NoKey 执行 demoParam = this 的线程 A 和 执行 instance()的线程 B 没有 happen-before 关系 B 可能永远看见的都是 null
    zmal
        20
    zmal  
       2022-09-30 11:16:49 +08:00
    @NoKey 我们平时写的 XXXservice 这是个源类,在 spring 容器里,spring 会对它进行各种增强,增强依赖于动态代理或 CGLIB 技术,不管底层原理是什么,最终都是生成了源类的子类这种方式。
    这就意味着你从 context 中拿到的 bean 和 源对象 this 返回的 bean 不一定是同一个。
    NoKey
        21
    NoKey  
    OP
       2022-09-30 11:21:01 +08:00
    @facelezz 单就这个问题进行讨论,我的想法是,把赋值放在 @PostConstruct 中,这个是 servlet 执行时必然调用一次,那么只要是服务可用状态,赋值肯定被执行了,然后,在单例模式下,这个就是对一个对象变量,应该没有线程安全问题
    NoKey
        22
    NoKey  
    OP
       2022-09-30 11:21:16 +08:00
    @zmal 感谢,明白这个问题了
    zmal
        23
    zmal  
       2022-09-30 11:22:20 +08:00
    @facelezz 这里没有 happen-before 问题,@PostConstruct 注解在 springboot 启动该对象初始化后已经执行了。
    happen-before 也不是这么用的。
    facelezz
        24
    facelezz  
       2022-09-30 11:29:28 +08:00
    @zmal 你说的应该是 实例方法下 获取没这个问题 因为注入时的获取 是 spring 在 map 上加锁才有先后顺序(lock-unlock) 楼主直接请求的静态方法静态变量 正常来讲是没有这个保证的
    facelezz
        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 )问题
    Bronya
        26
    Bronya  
       2022-09-30 13:30:27 +08:00
    所以结论是啥呀,楼主这么写到底有没有问题,是不是在没有增强时(@Transactional 或者其他 aop )楼主这么写也可以的?
    NoKey
        27
    NoKey  
    OP
       2022-09-30 15:42:07 +08:00
    @Bronya 我理解的是,保险起见,还是用 context
    siweipancc
        28
    siweipancc  
       2022-09-30 17:24:22 +08:00 via iPhone
    会寄,但是可以多测试一下,生命周期暂时稳定(服务器顺序不定)就行,爆了再说。
    siweipancc
        29
    siweipancc  
       2022-09-30 17:37:23 +08:00 via iPhone
    上边的提到非唯一问题,只要保证单栗模式+不允许覆盖定义就行。你这样写完全没有问题。

    甚至可以定义成内部静态字段=context.get(),只要第一次方法引用时 app ready 就行
    duteliang
        30
    duteliang  
       2022-09-30 18:43:10 +08:00
    不会有问题的,但是要保证这个静态方法不要在项目启动,初始化的时候调用。不然因为 bean 的加载顺序问题有概率空指针。
    liuzhaowei55
        31
    liuzhaowei55  
       2022-09-30 18:56:44 +08:00 via iPhone
    直接写个单例,然后自己 new 这个类?现在这样怪怪的,或者参考下许多 BeanUtils 的写法,都可以不用注入 context 静态获取实例,关键字如 cola- domains
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5386 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 07:56 PVG 15:56 LAX 23:56 JFK 02:56
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86