1.StepSope是一个作用域在这之前先说一下IOC容器中的几个bean的作用域:singleton单例模式——全局且只有一个实例原型原型模式——每次获取一个bean,都会有一个新的实例请求——请求的意思每次HTTP请求都会生成一个新的bean,bean只在当前HTTP请求会话内有效-sessionscope表示每次HTTP请求都会生成一个新的bean,bean只在当前HTTP请求内有效sessionglobalsession——全局会话作用域类似于标准的HTTPSession作用域,但它只在基于portlet的web应用程序中可用意义2.StepSope是自定义步骤。目的是每次启动Job实例时,底层单元(RPW或step)的参数可以灵活修改或可变,因为一个系统中可能存在多个Job实例。每个作业实例对应的参数是不同的。在step运行过程中需要获取job参数/stepContext/jobContext,所以需要自定义一个scope,使其与Step的生命周期保持一致。使用注释。那么stepScope修饰必须是一个@Bean3。如何使用它。@Value支持spel表达式3.1大多数场景都是Spel表达式。在底层reader/process/writer中使用@Value获取jobParamter/stepContext/jobContextjob参数:\#{jobParameters[xy]}job运行上下文:#{jobExecutionContext[xy]}step运行上下文:#{stepExecutionContext[xy]}3.2SpEL引用bean对象:\#{car}bean对象属性:\#{car.brand}bean对象方法:\#{car.toString()}静态方法属性:\#{T(java.lang.Math).PI}3.3系统属性系统变量:systemProperties环境变量:#{systemEnvironment['HOME']}3.4运算符符号if-else运算符(三元运算符?:(temary),?:(Elvis))比较运算符(<,>,==,>=,<=,lt,gt,eg,le,ge)逻辑运算符(and,or,not,|)正则表达式(#{admin.email匹配'[a-zA-Z0-9._%±]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,4}'})算术运算符(+、-、*、/、%、^(加号符号也可以用作字符串连接))4.你可能会遇到问题问题:Scope'step'isnotactiveforthecurrentthread;创建名为“scopedTarget.demo01StepScopeStep”的bean时出错:范围“step”对于当前线程不活动;如果您打算从单例中引用它,请考虑为该bean定义一个作用域代理;嵌套异常是java.lang.IllegalStateException:没有可用于步骤范围的上下文持有者原因:请参阅代码@BeanpublicJobdemo01StepScopeJob(){returnjobBuilderFactory.get("demo01StepScopeJob").start(demo01StepScopeStepError(null)).build();}/****此时添加注解stepscope**Scope'inStep(demo01StepScopeStep)step'对于当前线程是不活跃的。这个也很清楚了,step还没有加载和初始化。**所以@Value只能在step激活后,即加载成功后才能获取到。我们可以将taskle定义为一个bean来获取**/@Bean@StepScopepublicStepdemo01StepScopeStepError(@Value("${demo01.param.name}")StringparamName){returnstepBuilderFactory.get("demo01StepScopeStep").tasklet(newTasklet(){@OverridepublicRepeatStatusexecute(StepContributioncontribution,ChunkContextchunkContext)throwsException{logger.info("=======demo01StepScopeStep======paramName:{}",paramName);返回空;}})。建造();}//转换如下@BeanpublicJobdemo01StepScopeJob(){returnjobBuilderFactory.get("demo01StepScopeJob").start(demo01StepScopeStep()).build();}//这个时间步上面的bean是可选的。因为stepScope只需要定义需要获取@Value的公共Step}@Bean@StepScope//这里的@StepScope可以是也可以不是。因为@value只是application.properties中的内容)抛出异常{logger.info("=======demo01StepScopeStep======paramName:{}",paramName);返回空值;}};}这是配置文件的内容。步骤内没有具体内容。所以可以直接在参数bean中使用@Value。也可以直接在配置里。在jobParam参数中使用@Value怎么样?那必须使@StepScope@Bean@StepScopepublicTaskletdemo01StepScopeTasklet(@Value("#{jobParameters[rnd]}")Stringrnd){returnnewTasklet(){@OverridepublicRepeatStatusexecute(StepContributioncontribution,ChunkContextchunkContext)throwsException{logger.info("=======demo01StepScopeTasklet======rnd:{}",rnd);返回空值;}};}5.StepScope原理org.springframework.batch.core.scope。StepScope.java看到stepScope实现了BeanFactoryPostProcessor。bpp的引入意味着在实例化之前进行扩展。这里是在spring容器中注册stepScope获取bean时的AbstractBeanFactory#doGetBean。判断bean不是mbd.isSingleton(),不是mbd.isPrototype(),如果是custom的话就是mbd.getScope()。如何获得?StepScope#get(String,ObjectFactory)->这将获取StepContext内容。stepContext=getContext()->这个其实是直接从ThreadLocal获取内容。获取stepContext(这里有jobParam、stepContext),可以看到StepSynchronizationManager已经获取到了。你什么时候注册的?AbstractStep#execute在step的抽象类中。执行doExecute(stepExecution);前面的方法doExecutionRegistration在ThreadLocal中添加了stepExecution。这个可以认为是threadlocal(其实一个栈可以存放多个stepExecution!!!,兼容step和step。)其实这里已经可以给出答案了。但是还有一个问题。@Value如何从stepExecution中获取额外的jobParm/stepContext/jobContext(@value如何获取那个阶段的值):在springboot启动过程中,有两个比较重要的过程,如下:1扫描并解析容器bean中的bean注册到beanFactory中,就像信息注册一样。2实例化并初始化这些扫描的bean。@Value的解析在第二阶段。BeanPostProcessor定义了bean初始化前后用户可以对bean进行操作的接口方法。正如javadoc所说,它的一个重要实现类之一AutowiredAnnotationBeanPostProcessor支持在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方法。6.自定义一个作用域定义JakcssybinScope/***在同一个线程中定义,多次获取同一个bean,获取的都是同一个。*如果不使用该注解获取的是不同的bean*/publicclassJackssybinScopeimplementsScope{privatefinalThreadLocal
