<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在此之前,先說一下IOC容器中幾種bean的作用範圍:
singleton單例模式 – 全域性有且僅有一個範例
prototype原型模式 – 每次獲取Bean的時候會有一個新的範例
request – request表示該針對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP request內有效
session – session作用域表示該針對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP session內有效
globalsession – global session作用域類似於標準的HTTP Session作用域,不過它僅僅在基於portlet的web應用中才有意義
目的是在每一次啟動Job範例時底層單位(RPW或者也可是step)的引數可以靈活修改或者達到可變,因為一個系統裡可能有多個job範例,每個job範例對應的引數是不一樣的。在step執行期間需要獲取的job引數/stepContext/jobContext,因此需要自定義一個作用域,令其與Step的生命週期一致。
使用註解了。那麼stepScope修飾的一定是一個@Bean
job引數:#{jobParameters[xy]}
job執行上下文:#{jobExecutionContext[xy]}
step執行上下文:#{stepExecutionContext[xy]}
bean物件:#{car}
bean物件屬性:#{car.brand}
bean物件方法:#{car.toString()}
靜態方法屬性:#{T(java.lang.Math).PI}
系統變數:systemProperties
環境變數:#{systemEnvironment['HOME']}
if-else 運運算元(三目運運算元 ?:(temary), ?:(Elvis))
比較運運算元(< , > , == , >= , <= , lt , gt , eg , le , ge)
邏輯運運算元(and , or , not , |)
正規表示式(#{admin.email matches ‘[a-zA-Z0-9._%±]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,4}’})
算術運運算元(+,-,*,/,%,^(加號還可以用作字串連線))
Error creating bean with name 'scopedTarget.demo01StepScopeStep': Scope 'step' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No context holder available for step scope
原因:看程式碼
@Bean public Job demo01StepScopeJob(){ return jobBuilderFactory.get("demo01StepScopeJob") .start(demo01StepScopeStepError(null)) .build(); } /** ** 這個時候在 Step (demo01StepScopeStep) 中新增註解stepscope。 ** Scope 'step' is not active for the current thread. 這個說的也很明白,step還沒有裝載初始化完成呢。 ** 所以只有在step啟用,即裝載成功之後才能獲取@Value 這種情況。我們可以把taskle 定義成個bean來獲取 **/ @Bean @StepScope public Step demo01StepScopeStepError(@Value("${demo01.param.name}") String paramName){ return stepBuilderFactory.get("demo01StepScopeStep") .tasklet(new Tasklet() { @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { logger.info("=======demo01StepScopeStep======paramName:{}",paramName); return null; } }).build(); } // 改造如下 @Bean public Job demo01StepScopeJob(){ return jobBuilderFactory.get("demo01StepScopeJob") .start(demo01StepScopeStep()) .build(); } //這個時候step上面的bean可以不要。因為stepScope只需要定義需要獲取@Value的 public Step demo01StepScopeStep(){ return stepBuilderFactory.get("demo01StepScopeStep") .tasklet(demo01StepScopeTasklet(null)) .build(); } @Bean @StepScope //這裡的@StepScope 可以有也可以不用。因為@value只是在application.properties中的內容 public Tasklet demo01StepScopeTasklet(@Value("${demo01.param.name}") String paramName){ return new Tasklet() { @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { logger.info("=======demo01StepScopeStep======paramName:{}",paramName); return null; } }; }
這裡面獲取的是組態檔的內容。不是step內的特定內容。所以可以直接使用@Value在引數bean裡。也可以直接在configuration內。
若使用jobParam引數內的@Value呢?那必須使@StepScope
@Bean @StepScope public Tasklet demo01StepScopeTasklet(@Value("#{jobParameters[rnd]} ") String rnd){ return new Tasklet() { @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { logger.info("=======demo01StepScopeTasklet======rnd:{}",rnd); return null; } }; }
org.springframework.batch.core.scope.StepScope.java
看出stepScope實現了BeanFactoryPostProcessor。引入了bpp即範例化之前做擴充套件。這裡是講stepScope給註冊到了spring容器中
獲取bean的時候AbstractBeanFactory#doGetBean。判斷bean不是mbd.isSingleton(),不是mbd.isPrototype(),若是自定義的即
mbd.getScope()。
那是如何獲取的呢StepScope#get(String, ObjectFactory) ->這裡面會獲取StepContext內容。
stepContext=getContext() ->這裡面其實是直接從ThreadLocal中獲取內容。獲取stepContext(這裡面從有jobParam,stepContext)
那我們可以看到這個StepSynchronizationManager已經有獲取的。那什麼時候註冊進去的呢?
AbstractStep#execute中在step的抽象類裡面。執行doExecute(stepExecution);之前的方法doExecutionRegistration
這裡面將stepExecution新增進ThreadLocal中。這裡面可以認為是threadlocal(其實裡面是一個棧可以存多個stepExecution!!!,相容step套step那種的。)
其實這裡已經能解答了。但還有個問題。@Value是如何從stepExecution中獲取jobParm/stepContext/jobContext的
額外(通過@value是如何在那個階段獲取值的呢):
springboot啟動過程中,有兩個比較重要的過程,如下: 1 掃描,解析容器中的bean註冊到beanFactory上去,就像是資訊登記一樣。 2 範例化、初始化這些掃描到的bean。
@Value
的解析就是在第二個階段。BeanPostProcessor
定義了bean初始化前後使用者可以對bean進行操作的介面方法,它的一個重要實現類AutowiredAnnotationBeanPostProcessor
正如javadoc所說的那樣,為bean中的@Autowired
和@Value
註解的注入功能提供支援。下面是呼叫鏈
這裡先簡單介紹一下圖上的幾個類的作用。
AbstractAutowireCapableBeanFactory: 提供了bean建立,屬性填充,自動裝配,初始胡。支援自動裝配建構函式,屬性按名稱和型別裝配。實現了AutowireCapableBeanFactory
介面定義的createBean
方法。
AutowiredAnnotationBeanPostProcessor: 裝配bean中使用註解標註的成員變數,setter方法, 任意的設定方法。比較典型的是@Autowired
註解和@Value
註解。
InjectionMetadata: 類的注入後設資料,可能是類的方法或屬性等,在AutowiredAnnotationBeanPostProcessor類中被使用。
AutowiredFieldElement: 是AutowiredAnnotationBeanPostProcessor
的一個私有內部類,繼承InjectionMetadata.InjectedElement
,描述註解的欄位。
StringValueResolver: 一個定義了處置字串值的介面,只有一個介面方法resolveStringValue
,可以用來解決預留位置字串。本文中的主要實現類在PropertySourcesPlaceholderConfigurer#processProperties
方法中通過lamda表示式定義的。供ConfigurableBeanFactory
類使用。
PropertySourcesPropertyResolver: 屬性資源處理器,主要功能是獲取PropertySources
屬性資源中的設定鍵值對。
PropertyPlaceholderHelper: 一個工具類,用來處理帶有預留位置的字串。形如${name}的字串在該工具類的幫助下,可以被使用者提供的值所替代。替代途經可能通過Properties
範例或者PlaceholderResolver
(內部定義的介面)。
PropertyPlaceholderConfigurerResolver: 上一行所說的PlaceholderResolver
介面的一個實現類,是PropertyPlaceholderConfigurer
類的一個私有內部類。實現方法resolvePlaceholder
中呼叫了外部類的resolvePlaceholder
方法。
定義JakcssybinScope
/** * 定義在同一個執行緒內,多次獲取同一個bean 獲取的是同一個。 * 如果不用該註解 獲取的是不同的bean */ public class JackssybinScope implements Scope { private final ThreadLocal<Map<String, Object>> threadLoacal = new ThreadLocal<Map<String, Object>>() { @Override protected Map<String, Object> initialValue() { return new HashMap<String, Object>(); } }; public Object get(String name, ObjectFactory<?> objectFactory) { Map<String, Object> scope = threadLoacal.get(); Object obj = scope.get(name); // 不存在則放入ThreadLocal if (obj == null) { obj = objectFactory.getObject(); scope.put(name, obj); System.out.println("Not exists " + name + "; hashCode: " + obj.hashCode()); } else { System.out.println("Exists " + name + "; hashCode: " + obj.hashCode()); } return obj; } public Object remove(String name) { Map<String, Object> scope = threadLoacal.get(); return scope.remove(name); } public String getConversationId() { return null; } public void registerDestructionCallback(String arg0, Runnable arg1) { } public Object resolveContextualObject(String arg0) { return null; } }
注入jackssybinScope
@Component public class JackssybinBPP implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("==============註冊JackssybinBPP========="); beanFactory.registerScope("jackssybinScope", new JackssybinScope()); } }
參照
@Service @Scope("jackssybinScope") public class JackssybinHaveScopeService { public String getMessage() { return "Hello World!"+this.hashCode(); } }
未參照
@Service public class JackssybinNoScopeService { public String getMessage() { return "Hello World!"+this.hashCode(); } }
測試
@SpringBootTest(classes = {Demo01StepScopeApplication.class}) public class JackssybinScopeTest { @Autowired ApplicationContext ctx; @Test public void jackssybinScopetest2(){ JackssybinHaveScopeService service = ctx.getBean(JackssybinHaveScopeService.class); System.out.println(service.getMessage()+"="+service.hashCode()); JackssybinHaveScopeService service2= ctx.getBean(JackssybinHaveScopeService.class); System.out.println(service2.getMessage()+"="+service2.hashCode()); System.out.println("======================"); JackssybinNoScopeService service3 = ctx.getBean(JackssybinNoScopeService.class); System.out.println(service3.getMessage()+"="+service3.hashCode()); JackssybinNoScopeService service4= ctx.getBean(JackssybinNoScopeService.class); System.out.println(service4.getMessage()+"="+service4.hashCode()); } }
結果
Not exists jackssybinHaveScopeService; hashCode: 1842102517
Hello World!1842102517=1842102517
Exists jackssybinHaveScopeService; hashCode: 1842102517
Hello World!1842102517=1842102517
======================
Hello World!728236551=728236551
Hello World!728236551=728236551
結論:
程式碼位置: github.com/jackssybin/…
到此這篇關於SpringBatch從入門到精通之StepScope作用域和用法詳解的文章就介紹到這了,更多相關SpringBatch StepScope作用域和用法內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45