"Step" or "Job" Scope for Spring-Batch beans? - java

I'm using Spring-Batch v3.0.0 for batch imports. There is a StepScope and a JobScope. How can I know which of them is appropriate?
For example, if I define a custom ItemReader or ItemWriter that should use a specific EntityManager, it could look like this:
#Bean
#Scope("step") //#Scope("job") //custom scope required to inject #jobParameters
public JpaItemWriter<T> jpaItemWriter(EntityManagerFactory emf) {
JpaItemWriter<T> writer = new JpaItemWriter<T>();
writer.setEntityManagerFactory(emf);
return writer;
}
But which scope is right here? And why?
Execution with step scope works, but I feel the itemWriters should maybe be of job scope so that they are not recreated on every step.
I tried switching step to job, but that throws following error:
Exception in thread "main" java.lang.IllegalStateException: No Scope registered for scope 'job'

Since Spring-Batch v3.0.1 you can use #JobScope
Marking a #Bean as #JobScope is equivalent to marking it as #Scope(value="job", proxyMode=TARGET_CLASS)

Got it: one has to provide the scope as a bean explicit within the #Configuration file.
#Bean
public JobScope jobScope() {
return new JobScope();
}

Related

converting java variable to spring beans

I am working on spring boot application. I want to define a variable in a class file as bean so that it is being initialised only once. Below is the source code.
Configuration cfg = new Configuration();
//Load template from source folder
Template template = cfg.getTemplate("src/helloworld.ftl");
// Build the data-model
Map<String, Object> data = new HashMap<String, Object>();
data.put("message", "Hello World!");
// Console output
Writer out = new OutputStreamWriter(System.out);
template.process(data, out);
out.flush();
I want to initialise the configuration object cfg & the template (starting 2 lines of the code) only once during application start and later use them wherever required in application classes.
How can I achieve this with bean?
Or is there any other better way to achieve this?
Create a new class and use the #Configuration annotation to load the beans when the application start up.
#Configuration
public class SpringConfiguration{
#Bean
public Configuration getConfigurationBean(){
return new Configuration();
}
}
One thing you can do is put the instantiation and initialization of the variables in their own method and annotate every method with the #Bean annotation.
If you do this, you should annotate the class with #Configuration.
The 2 methods of yours would look something like this:
#Configuration
public class ConfigurationBean {
#Bean
public Configuration configuration() {
return new Configuration();
}
#Bean
public Template template() {
return configuration().getTemplate("...");
}
}
Additionally, if you don't want to instantiate the Configuration as a separate bean, you can merge the 2 methods above.
======= UPDATE to answer question in the comment =======
If you want to use the newly defined Template bean for example in other classes, you need to annotate those classes with #Component, so that they become beans as well.
An example:
#Component
public class OtherClassBean {
private final Template template;
public OtherClassBean(Template template) {
this.template = template;
}
}
please be careful here, because from now on, you can't manually instantiate OtherClassBean, meaning that a line like new OtherClassBean(...) is wrong in 99% of the cases and might result in unexpected behavior.
This is because all beans are meant to be managed by Spring, which can only be achieved if Spring instantiates those classes.
The only exception from this rule of thumb is in my initial answer, where you instantiate a bean in a method that is annotated with #Bean.
I know this is a confusing topic for most people trying to learn this, so don't be shy to ask more questions.

What does the spring annotation #ConditionalOnMissingBean do?

I am trying to start a springboot application where this annotation has been used. When I try to start the application it gives me the following error:
org.springframework.boot.autoconfigure.condition.OnBeanCondition$BeanTypeDeductionException Failed to deduce bean type for com.shutterfly.sbs.platform.SbsPlatformConfigurationClientConfig.getRestTemplate
Code:
#ConditionalOnMissingBean
#Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
The #ConditionalOnMissingBean annotation is used to load a bean only if a given bean is missing:
#Bean
#ConditionalOnMissingBean(SomeBean.class)
public SomeBean otherBean(){
return new SomeBean();
}
The above bean will get loaded by Spring only if there is no other bean of this type present in the context. On the other hand, if there is already a bean of the type SomeBean present in the application context, the above bean will not be created.
Some use cases where this annotation comes in handy are:
Specifying a fallback bean which gets only loaded as a backup if there is no bean of the same type present (for example: using an in-memory database if there is no real database configured)
Specifying a default bean which allows being overridden in the case that a more specific bean of the same type is present in the context (for example: using a default authentication mechanism unless someone decides to replace it with his own custom authentication)
Reference: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html
The #ConditionalOnMissingBean annotation is a spring conditional annotation for registering beans only when they are not already in the application context.
See the documentation: https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/api/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBean.html
We use #ConditionalOnMissingBean if we want to include a bean only if a specified bean is not present. For ex.
Let's configure a transactionManager bean that will only be loaded if a bean of type JpaTransactionManager is not already defined:
#Bean
#ConditionalOnMissingBean(type = "JpaTransactionManager")
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
To understand more consider this scenario as well.
Let's say in my project I configured a bean videoDecoderService
#Bean
#ConditionalOnMissingBean(VideoDecoderService.class)
public videoDecoderService videoDecoderService(){
return new VideoDecoderService;
}
What it will do is whoever is using my project would be able to override the videoDecoderService with the videoDecoderService of their own. If they are not writing their own videoDecoderService then this default one will be provided.
You should not skip the part of the documentation that clearly says:
The condition can only match the bean definitions that have been processed by the application context so far and, as such, it is strongly recommended to use this condition on auto-configuration classes only.
It goes on later to say:
If a candidate bean may be created by another auto-configuration, make sure that the one using this condition runs after
In rather simple words it might mean that the #Bean brought in by the some other auto-configuration should take precedence over the one that you defined. For that to work, you need to properly set-up the order of those configurations via #AutoConfigureBefore.

Purpose of Bean annotations for non-Job methods in trivial Spring Batch example

I am getting familiarized with the entire Spring stack.
I am referring here to a trivial Spring Batch example posted on spring.io: https://spring.io/guides/gs/batch-processing/
Every method in the Job Configuration class BatchConfiguration is annotated using #Bean. Except for the job method importUserJob is there any point in annotating singleton-type Bean helper methods invoked by the singleton-type Bean method that creates a job?
To my mind by removing #Bean annotations from all methods except importUserJob all this code is going to be invoked only once per Spring instance and only from within the importUserJob method.
All Spring Batch examples are shown with the #Bean annotation around the non-job methods so there must be something to it that eludes me.
Pruned excerpt below:
#Configuration
#EnableBatchProcessing
public class BatchConfiguration {
#Autowired public JobBuilderFactory jobBuilderFactory;
#Autowired public StepBuilderFactory stepBuilderFactory;
#Autowired public DataSource dataSource;
#Bean
public FlatFileItemReader<Person> reader() ...
#Bean
public PersonItemProcessor processor() ...
#Bean
public JdbcBatchItemWriter<Person> writer() ...
#Bean
public Job importUserJob(JobCompletionNotificationListener listener) {
return jobBuilderFactory.get("importUserJob")
.incrementer(new RunIdIncrementer())
.listener(listener)
.flow(step1())
.end()
.build();
}
#Bean
public Step step1() ...
}
Thanks for reading and helping out.
Technically, you could write the batch without using #Bean for those function you mentioned and your assumptions are correct, as the beans are not used other places. (I have not tried it myself, but I think it will run - if Spring Batch doesn't use them for other stuff).
A #Bean annotated object gets its lifecycle handled by Spring so it can perform dependency injection properly. This way, you can seperate construction of objects from their usage (IoC). Especially, when you use autowiring, dependency injection becomes much easier.
You, could of course do this manually, but this will require a lot boilerplate code, and you'll be required to send your configured objects around. In your example, there may not be any benefits of creating beans for everything, but when the batch grows larger, I am sure you will miss the flexiblity of Springs bean handling. I have personally written a batch with some other framework without Springs dependency injection. First I had a compact model, but as thing grew, I really felt the pain of boilerplate code which effected the readablity of my code.
In addition Spring managed beans takes care of doing other stuff e.g. closing resources.
Of course, dependency injection is just a tool and can be an overkill for certain use cases. These articles may be of interest for you:
https://martinfowler.com/articles/dipInTheWild.html
https://sites.google.com/site/unclebobconsultingllc/blogs-by-robert-martin/dependency-injection-inversion
The #Bean annotation makes the methods bean creation methods and the resulting object will be registered in the Spring ApplicationContext as a bean.
As a result it will participate in the lifecycle of said context and as well receive callbacks like the #PostConstruct annotated methods are going to be called or interface callbacks like InitializingBean.
If you omit the #Bean this will not happen. For the FlatFileItemReader for instance this will lead to the afterPropertiesSet method not being called, which would lead to a not fully initialized bean.
So no omitting the #Bean isn't something you should do as the #Bean is the equivalent of a <bean /> in XML.

Why is an #Autowired field within a #Configuration class null?

This is an example from the Spring documentation, section 6.12.5:
#Configuration
public class ServiceConfig {
#Autowired
private AccountRepository accountRepository;
#Bean
public TransferService transferService() {
return new TransferServiceImpl(accountRepository);
}
}
My question is: why must it happen that accountRepository is created before it's used by new TransferServiceImpl()? Offhand, I don't see how Spring could know that the second one depends on the first one being set up (unless it goes through the transferService() bytecode). Is it because something about the order in which Spring does things guarantees that the #Autowired variable is processed before the #Bean method could possibly be called? What is the processing order? What kinds of circumstances could cause Spring to process these out of order?
The reason I'm asking is that I have a case where something like this isn't working, i.e. the new is being executed with a null argument. Either the #Autowired variable is being set up too late, or it isn't set up at all (my guess is the latter, based on some log4j.logger.org.springframework.beans debugging output, but I'm not sure). The situation is of course much more complex--it's a largish application, and there are a few more #Autowired and #Bean definitions in the configuration class. Using #DependsOn hasn't helped. It will take a lot of time to narrow down the problem by deleting code until I can get a minimal example, but I wanted to see if I could get some insight into the problem by learning more details about how Spring processes things, before starting down the difficult code reduction path.
why must it happen that accountRepository is created before it's used
by new TransferServiceImpl()?
It doesn't. accountRepository may be seen to be null.
From the note in the documentation you linked (its more current version)
Make sure that the dependencies you inject that way are of the
simplest kind only. #Configuration classes are processed quite early
during the initialization of the context and forcing a dependency to
be injected this way may lead to unexpected early initialization.
Whenever possible, resort to parameter-based injection as in the
example above.
Also, be particularly careful with BeanPostProcessor and
BeanFactoryPostProcessor definitions via #Bean. Those should usually
be declared as static #Bean methods, not triggering the instantiation
of their containing configuration class. Otherwise, #Autowired and
#Value won’t work on the configuration class itself since it is being
created as a bean instance too early.
In summary, a Configuration class will end up being just another bean in the application context. As such, it will be processed by all registered BeanPostProcessor beans.
#Autowired is processed by AutowiredAnnotationBeanPostProcessor. Presumably, you're using AnnotationConfigApplicationContext which registers one automatically.
In your example, which is incomplete since
...but determining exactly where the autowired bean definitions are
declared is still somewhat ambiguous
However, we can assume some other configuration provided a bean definition for a AccountRepository bean. Once the application context instantiates the ServiceConfig bean, it can then post process it and inject #Autowired targets.
The only reason an #Autowired target could be null in a #Configuration bean instance is that you tried to read it before an AutowiredAnnotationBeanPostProcessor could process/inject it.
Consider a circular dependency. Take the #Configuration class in your snippet with an additional #ComponentScan of the following classes
#Component
class AccountRepository {
public AccountRepository(Foo foo) {}
}
#Component
class Foo {
public Foo(TransferService ts) {}
}
The #Configuration bean get initialized. AutowiredAnnotationBeanPostProcessor kicks off to process the accountRepository field. It looks for an AccountRepository bean and tries to initialize it. It needs a Foo bean to instantiate it (for constructor injection). It looks for a Foo bean and tries to initialize it. It needs a TransferService bean to instantiate it (for constructor injection). It looks for a TransferService bean and finds the #Bean factory method. It invokes it. The accountRepository hasn't been initialized yet, so remains null. You can verify this by putting a breakpoint in the #Bean method and browsing the stack trace.
Had you used a parameter injection as suggested in the quote above
Whenever possible, resort to parameter-based injection as in the example above.
Spring would've crashed and warned you
Caused by:
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'accountRepository': Requested bean is
currently in creation: Is there an unresolvable circular reference?
That's the workaround I ended up doing
I can't currently explain this.
I just move accountRepository to the method's param and annotated with #Autowired to solve this problem. But I don't know why. I think the reason is about Spring's init order.
#Configuration
public class ServiceConfig {
#Bean
public TransferService transferService(#Autowired AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}

Java based configuration for Ehcache based caching not working

I am using java annotation based configuration for initializing ehcache based caching, with Spring 3.1.
Here is the sample code...
#Configuration
#EnableCaching
public class EhcacheConfig implements CachingConfigurer {
.....
#Bean
public CacheManager cacheManager() {
.....
EhCacheManagerFactoryBean bean = new EhCacheManagerFactoryBean();
bean.setCacheManagerName(CACHE_MANAGER);
bean.setShared(Boolean.TRUE);
File file = new File(property + Constants.Slash + EHCACHE_XML);
bean.setConfigLocation(new FileSystemResource(file));
try {
bean.afterPropertiesSet();
} catch (Exception e) {
throw new RuntimeException(e);
}
EhCacheCacheManager cm = new EhCacheCacheManager();
cm.setCacheManager(bean.getObject());
return cm;
}
public KeyGenerator keyGenerator() {
return new DefaultKeyGenerator();
}
}
There is a valid ehcache.xml with 1 cache declared in it.
This is all the configuration that I have for initializing ehcache with Spring. There is no XML based initialization in the application.
At runtime, I have noticed that cacheManager() is initialized, as expected. After its successful execution, the code fails to complete the initialization by erring out in:
CachingInterceptor.afterPropertiesSet() ->
if (this.cacheManager == null) {
throw new IllegalStateException("'cacheManager' is required");
}
I have done some investigation.
It appears that the problem occurs when CachingInterceptor is being initialized by ProxyCachingConfiguration.
ProxyCachingConfiguration is derived from AbstractCachingConfiguration.
AbstractCachingConfiguration has a method called:
#PostConstruct
protected void reconcileCacheManager()
This method is not invoked. Had it been invoked, the cacheManager instantiated in EhcacheConfig.cacheManger() would have been setup correctly for used by the CacheInterceptor.afterPropertiesSet().
I do not understand the reason why reconcileCacheManager() is not invoked before CacheInterceptor.afterPropertiesSet() is invoked.
Am I missing something? Can some one help me with the problem that I am facing?
Thank you.
First, you might consider extracting the initialization of the EhCacheManagerFactoryBean to its own #Bean method.
This way you can simply instantiate, configure, and return the FactoryBean without having to invoke afterPropertiesSet() yourself. This ensures that the object is a properly-managed Spring bean and that it can receive any other callbacks in might register for (like DisposableBean#destroy()) in this particular case.
Assuming that new #Bean method is named "ecmfb", you can simply call ecmfb().getObject() from within your cacheManager() method, and you'll be guaranteed at that point that the FactoryBean contract (i.e. afterPropertiesSet()) has been satisfied.
Second, you might care to know that your #Bean methods can throw any exception you like. So for example if you did not choose to extract the FactoryBean as I suggest above, you could still simplify the situation by declaring a 'throws Exception' clause on your cacheManager #Bean method. This will save you the current try/catch noise.
Finally, to address why the #PostConstruct method is not being called, let me ask how you're bootstrapping the Spring container. If you're working with AnnotationConfig(Web)ApplicationContext, the CommonAnnotationBeanPostProcessor should be registered by default. The same is true if you're using or . CABPP is responsible for the detection and processing of annotations like #PostConstruct, #PreDestroy and others. Please provide a bit more information about your bootstrapping approach and we'll go from there.

Categories

Resources