• spring中bean的构造函数,Autowired(Value)注入与@PostConstruct调用顺序


    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://blog.csdn.net/yyysylvia/article/details/83177345

    最近在项目开发中遇到这样一个需求,由于元数据在短时间内被客户端多次读取,因此希望直接将数据存储到内存,以减少网络开销,借助guava cache于是有了下面这个类

    /**
     * Created on 2018/10/18
     */
    @Component
    public class CacheUtil {
    
    <span class="token annotation punctuation">@Autowired</span>
    CaseGraphService caseGraphService<span class="token punctuation">;</span>
    
    <span class="token annotation punctuation">@Value</span><span class="token punctuation">(</span><span class="token string">"${cache.expire.duration}"</span><span class="token punctuation">)</span>
    <span class="token keyword">long</span> expireDuration<span class="token punctuation">;</span>
    
    <span class="token keyword">private</span> Cache<span class="token generics function"><span class="token punctuation">&lt;</span>Long<span class="token punctuation">,</span> CaseGraphDTO<span class="token punctuation">&gt;</span></span> metaNodeCache <span class="token operator">=</span> CacheBuilder
                <span class="token punctuation">.</span><span class="token function">newBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">maximumSize</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">expireAfterAccess</span><span class="token punctuation">(</span>expireDuration<span class="token punctuation">,</span> TimeUnit<span class="token punctuation">.</span>MINUTES<span class="token punctuation">)</span> <span class="token comment">//设置过期时间</span>
                <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    <span class="token keyword">public</span> CaseGraphDTO <span class="token function">getMetaNode</span><span class="token punctuation">(</span><span class="token keyword">long</span> caseId<span class="token punctuation">)</span> <span class="token keyword">throws</span> ExecutionException <span class="token punctuation">{</span>
        <span class="token keyword">return</span> metaNodeCache<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>caseId<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">&gt;</span> caseGraphService<span class="token punctuation">.</span><span class="token function">getCaseGraph</span><span class="token punctuation">(</span>caseId<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">removeMetaNodeByKey</span><span class="token punctuation">(</span><span class="token keyword">long</span> caseId<span class="token punctuation">)</span><span class="token punctuation">{</span>
        metaNodeCache<span class="token punctuation">.</span><span class="token function">invalidate</span><span class="token punctuation">(</span>caseId<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">removeMetaNodeAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
        metaNodeCache<span class="token punctuation">.</span><span class="token function">invalidateAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    

    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    我们在另一个类中注入CacheUtil并调用它的getMetaNode方法,类似于这样:

    @Component
    public class TestComponent {
    	@Autowired
    	CacheUtil cacheUtil;
    
    <span class="token keyword">public</span> CaseGraphDTO <span class="token function">getMetaData</span><span class="token punctuation">(</span><span class="token keyword">long</span> caseId<span class="token punctuation">)</span><span class="token punctuation">{</span>
    	<span class="token keyword">return</span> cacheUtil<span class="token punctuation">.</span><span class="token function">getMetaNode</span><span class="token punctuation">(</span>caseId<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    

    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    当我们第一次调用getMetaNode时,cache使用caseGraphService获取元数据,而后将这一元数据存放的cache中,我们在CacheUtil的getMetaNode方法中添加两行代码来测试一下:

    public CaseGraphDTO getMetaNode(long caseId) throws ExecutionException {
            //metaNodeCache的get方法,如果缓存中已有数据,直接返回数据,如果没有则通过Callable方法获取存入缓存再返回数据
            CaseGraphDTO caseGraphDTO = metaNodeCache.get(caseId, () -> caseGraphService.getCaseGraph
                    (caseId));
            //getIfPresent方法,如果存在数据即返回
            CaseGraphDTO caseGraphDTO1 = metaNodeCache.getIfPresent(caseId);
            System.out.println(caseGraphDTO1);
            return caseGraphDTO;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    启动application, 打开断点调试:
    在这里插入图片描述
    可以看到caseGraphDTO1为null,而不是我们预想的元数据;metaNodeCache中localCache的大小为0,也就是根本没有缓存任何数据。

    而后我采用另一种方式@PostConstruct的方式来初始化metaNodeCache, 代码如下:

    @Component
    public class CacheUtil {
    
    <span class="token annotation punctuation">@Autowired</span>
    CaseGraphService caseGraphService<span class="token punctuation">;</span>
    
    <span class="token annotation punctuation">@Value</span><span class="token punctuation">(</span><span class="token string">"${cache.expire.duration}"</span><span class="token punctuation">)</span>
    <span class="token keyword">long</span> expireDuration<span class="token punctuation">;</span>
    
    <span class="token keyword">private</span> Cache<span class="token generics function"><span class="token punctuation">&lt;</span>Long<span class="token punctuation">,</span> CaseGraphDTO<span class="token punctuation">&gt;</span></span> metaNodeCache<span class="token punctuation">;</span>
    
    <span class="token annotation punctuation">@PostConstruct</span>
    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
        metaNodeCache <span class="token operator">=</span> CacheBuilder
                <span class="token punctuation">.</span><span class="token function">newBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">maximumSize</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">expireAfterAccess</span><span class="token punctuation">(</span>expireDuration<span class="token punctuation">,</span> TimeUnit<span class="token punctuation">.</span>MINUTES<span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
    <span class="token keyword">public</span> CaseGraphDTO <span class="token function">getMetaNode</span><span class="token punctuation">(</span><span class="token keyword">long</span> caseId<span class="token punctuation">)</span> <span class="token keyword">throws</span> ExecutionException <span class="token punctuation">{</span>
        <span class="token comment">//metaNodeCache的get方法,如果缓存中已有数据,直接返回数据,如果没有则通过Callable方法获取存入缓存再返回数据</span>
        CaseGraphDTO caseGraphDTO <span class="token operator">=</span> metaNodeCache<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>caseId<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">&gt;</span> caseGraphService<span class="token punctuation">.</span>getCaseGraph
                <span class="token punctuation">(</span>caseId<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">//getIfPresent方法,如果存在数据即返回</span>
        CaseGraphDTO caseGraphDTO1 <span class="token operator">=</span> metaNodeCache<span class="token punctuation">.</span><span class="token function">getIfPresent</span><span class="token punctuation">(</span>caseId<span class="token punctuation">)</span><span class="token punctuation">;</span>
        System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>caseGraphDTO1<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> caseGraphDTO<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">removeMetaNodeByKey</span><span class="token punctuation">(</span><span class="token keyword">long</span> caseId<span class="token punctuation">)</span><span class="token punctuation">{</span>
        metaNodeCache<span class="token punctuation">.</span><span class="token function">invalidate</span><span class="token punctuation">(</span>caseId<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">removeMetaNodeAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
        metaNodeCache<span class="token punctuation">.</span><span class="token function">invalidateAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    

    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    再进行调试:
    在这里插入图片描述
    这时我们看到caseGraphDTO1中有了数据,并且metaNodeCache中localCache的大小也变成了1.

    那么直接初始化成员变量和使用postConstruct来初始化二者的区别是什么呢?

    要将对象p注入到对象a,那么首先就必须得生成对象p与对象a,才能执行注入。所以,如果一个类A中有个成员变量p被@Autowired注解,那么@Autowired注入是发生在A的构造方法执行完之后的。
    如果想在生成对象时候完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么就无法在构造函数中实现。为此,可以使用@PostConstruct注解一个方法来完成初始化,@PostConstruct注解的方法将会在依赖注入完成后被自动调用。

    事情似乎明朗起来了,虽然我们在初始化metaNodeCache时没有使用autowired进来的caseGraphService,但我们使用了@Value来注入缓存过期时间(配置中这个值为60)。让我们再来断点调试一下,当使用直接初始化成员变量的方式时,@Value("${cache.expire.duration}")注入的过期时间是多少,如下:
    在这里插入图片描述
    我们看到expireDuration的值为0,而不是配置中的60,对于cache的含义即为缓存中的数据被读取后0分钟使其失效,等同于立即失效。所以导致上文我们的缓存中始终没有数据。

    让我们再来看看使用PostConstruct初始化时,这个过期时间的值:
    在这里插入图片描述
    此时看到expireDuration的值为60.

    因此我们需要记住,构造函数,Autowired(Value),PostConstruct的执行顺序为:

    Constructor >> Autowired >> PostConstruct
    如果初始化成员变量需要使用注入进来的对象或者值,那么应该放在被PostConstruct注解的方法中去做

                                    </div>
                <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-095d4a0b23.css" rel="stylesheet">
    

    原文地址:https://blog.csdn.net/yyysylvia/article/details/83177345

  • 相关阅读:
    【博弈论】取火柴游戏
    【贪心】马拉松接力赛
    【贪心】0/1背包
    【贪心】赶作业
    C# 设计模式 (一)
    C# 反射入门
    C# WinForm 访问webService天气预报
    WebService 测试地址
    C# Windows服务相关
    CSS 案例学习
  • 原文地址:https://www.cnblogs.com/jpfss/p/11533597.html
Copyright © 2020-2023  润新知