I have defined the following Spring boot application:
#SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(WebApplicationType.NONE).run(args);
}
public void run(String... args) throws Exception {
Thread.currentThread().join();
}
}
I also have a package of workers (i.e. could be classes which implements Runnable). They are supposed to run indefinitely.
What is the "Spring way" to run them? (and doing so automatically, without explicitly knowing them)
Thanks
and doing so automatically, without explicitly knowing them
There's no mechanism to automatically run some Runnables from a certain place. You need to find a way to inform Spring about these classes.
Three common scenarios:
Execute some code during the application startup: ApplicationRunner and CommandLineRunner.
You either congregate Runnables and wrap them up into an [Application|CommandLine]Runner which should be a bean (e.g. #Bean, #Component, etc) or make each Runnable a separate [Application|CommandLine]Runner.
Execute some code at some point of time: TaskExecutor.
You inject a TaskExecutor and give it previously gathered Runnables.
Execute some code repeatedly: TaskScheduler.
You inject a TaskScheduler and give it previously gathered Runnables, plus a trigger.
More details: Task Execution and Scheduling
You could (1) have your classes that implement Runnable be annotated with #Component, so that Spring can find them. You can then (2) write a WorkManager, annotated with #Service, with an #Autowired List - which Spring would initialize with a list instances of all your classes from (1). And could (3) write a #PostConstruct method in your WorkManager that would iterate over that list of Runnables, and pass each one to a TaskExecutor to run...
Related
Maybe I have some outdated knowledge but it is the same as described here
https://stackoverflow.com/a/2657465/2674303
But now I noticed that this example works without any exceptions:
#Service
#EnableScheduling
public final class MyService {
#PostConstruct
public void init(){
System.out.println("MyService started");
}
#Scheduled(fixedDelay= 1000)
public void scheduleCall() {
System.out.println("scheduleCall");
}
}
Could you pease provide how does it work ?
#Scheduled annotation does not require proxy creation. The mechanism is different. After bean initialization Spring called post-processor ScheduledAnnotationBeanPostProcessor. Post processor searches for all methods annotated with #Scheduled and registers them to TaskScheduller for execution. Method execution will be performed via reflection.
See ScheduledAnnotationBeanPostProcessor source code.
#Scheduled
Processing of #Scheduled annotations is performed by registering a
ScheduledAnnotationBeanPostProcessor. This can be done manually or,
more conveniently, through the task:annotation-driven/ XML element
or #EnableScheduling annotation.
ScheduledAnnotationBeanPostProcessor
Bean post-processor that registers methods annotated with #Scheduled
to be invoked by a TaskScheduler according to the "fixedRate",
"fixedDelay", or "cron" expression provided via the annotation. This
post-processor is automatically registered by Spring's
task:annotation-driven XML element, and also by the
#EnableScheduling annotation.
Autodetects any SchedulingConfigurer instances in the container,
allowing for customization of the scheduler to be used or for
fine-grained control over task registration (e.g. registration of
Trigger tasks). See the #EnableScheduling javadocs for complete usage
details.
#PostConstruct also implemented via post-processor InitDestroyAnnotationBeanPostProcessor when dependency injection performed for bean, method which marked #PostConstruct will be executed thru reflection without proxy.
See InitDestroyAnnotationBeanPostProcessor source code
Summary:
In your example, Spring will create bean without proxy.
In case you will add a proxy-specific annotation, for example, #Transactional you will get an exception that proxy can not be created due to final class java.lang.IllegalArgumentException: Cannot subclass final class com.test.services.MyService
#Service
#EnableScheduling
public final class MyService {
#PostConstruct
public void init(){
System.out.println("MyService started");
}
#Scheduled(fixedDelay= 1000)
#Transactional
public void scheduleCall() {
System.out.println("scheduleCall");
}
}
But the current problem you also can solve to force use JDK dynamic proxy. We need to create an interface for class and set property spring.aop.proxy-target-class = false according to Proxying mechanisms
I'm using spring boot. I was new to spring and started a spring project. So I didn't know about pre defined repositories (JPA, CRUD) which can be easily implemented. In case, I wanted to save a bulk data, so I use for loop and save one by one, Its taking more time. So I tried to use #Async. But it doesn't also work, is my concept wrong?
#Async has two limitation
it must be applied to public methods only
self-invocation – calling the async method from within the same class won’t work
1) Controller
for(i=0;i < array.length();i++){
// Other codes
gaugeCategoryService.saveOrUpdate(getEditCategory);
}
2) Dao implementation
#Repository
public class GaugeCategoryDaoImpl implements GaugeCategoryDao {
// Other codings
#Async
#Override
public void saveOrUpdate(GaugeCategory GaugeCategory) {
sessionFactory.getCurrentSession().saveOrUpdate(GaugeCategory);
}
}
After removing #Async , it working normally. But with that annotation it doesn't work. Is there any alternative method for time consuming? Thanks in advance.
the #Async annotation creates a thread for every time you call that method. but you need to enable it in your class using this annotation #EnableAsync
You also need to configure the asyncExecutor Bean.
You can find more details here : https://spring.io/guides/gs/async-method/
In my opinion, there are several issues with your code:
You overwrite the saveOrUpdate() method without any need to do so. A simple call to "super()" should have been enough to make #Async work.
I guess that you somewhere (within your controller class?) declare a transactional context. That one usually applies to the current thread. By using #Async, you might leave this transaction context as (because of the async DAO execution), the main thread may already be finished when saveOrUpdate() is called. And even though I currently don't know it exactly, there is a good change that the declared transaction is only valid for the current thread.
One possble fix: create an additional component like AsyncGaugeCategoryService or so like this:
#Component
public class AsyncGaugeCategoryService {
private final GaugeCategoryDao gaugeCategoryDao;
#Autowired
public AsyncGaugeCategoryService(GaugeCategoryDao gaugeCategoryDao) {
this.gaugeCategoryDao = gaugeCategoryDao;
}
#Async
#Transactional
public void saveOrUpdate(GaugeCategory gaugeCategory) {
gaugeCategoryDao.saveOrUpdate(gaugeCategory);
}
}
Then inject the service instead of the DAO into your controller class. This way, you don't need to overwrite any methods, and you should have a valid transactional context within your async thread.
But be warned that your execution flow won't give you any hint if something goes wrong while storing into the database. You'll have to check the log files to detect any problems.
I am using Spring #Async on two classes. Both are ultimately implementing an interface. I am creating two separate ThreadPoolTaskExecutor so each class has its own ThreadPool to work off of. However due to I think something with proxy and how Spring implements Async classes, I have to put the #Async annotation on the base interface. Because of this, both classes end up using the same ThreadPoolTaskExecutor.
Is it possible to tell Spring that for this Bean (in this case I am calling the classes that implement that interface a Service), use this ThreadPoolTaskExecutor.
By default when specifying #Async on a method, the executor that will be used is the one supplied to the 'annotation-driven' element as described here.
However, the value attribute of the #Async annotation can be used when needing to indicate that an executor other than the default should be used when executing a given method.
#Async("otherExecutor")
void doSomething(String s) {
// this will be executed asynchronously by "otherExecutor"
}
In this case, "otherExecutor" may be the name of any Executor bean in the Spring container, or may be the name of a qualifier associated with any Executor, e.g. as specified with the element or Spring’s #Qualifier annotation
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html
And probably you need to specify the otherExecutor bean in you app with the pool settings you wish.
#Bean
public TaskExecutor otherExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
return executor;
}
I have created a bean of a class with default (Singleton) scope. Within the class I have a method which is scheduled to be run every hour.
public class TaskService implements InitializingBean {
#Scheduled(cron="0 0 */1 * * ?")
public void hourlyReportTask()
{
... code here ...
}
public void performAllTasks()
{
hourlyReportTask();
...
...
}
}
My application config looks something like this,
<bean id="reportService"
class="com.tasks.TaskService" />
I am assuming the Thread running the scheduled task will be using the same TaskService bean since its created in singleton scope. What shall happen if the application is currently running hourlyReportTask() and the Spring container kicks off a background scheduled thread to run hourlyReportTask() at the same time. Will it wait for the to get access of the TaskService instance?
The exact same instance is used by both your application and the scheduling service. There is no synchronization so the scheduling service may run that method while your application invokes it.
Pretty much the same way as you would have injected TaskService in something that can be accessed by multiple threads at the same time and those threads call that method concurrently.
There's no black magic behind #Scheduled: it invokes your method the same way as you would manually. If that method is not thread-safe you need to fallback on regular synchronization mechanism in Java (for instance by adding the synchronized keyword to your method declaration).
Spring Singleton, does not mean what you expect from Design Patterns Singleton. In Spring, Singleton means that a bean only has created only one instance (without meaning that another cannot be created) and that instance is used whenever Spring needs that type.
In your case your hourlyReportTask() method would execute twice.
I am extracting some functionality from an existing program into a separate library.
This program uses Spring for dependency injection and other tasks and I'd like to keep using it in the library as well.
This library needs to monitor the filesystem for changes, so it will kick off some kind of separate thread to do this.
I don't really know what my options are for initialisation of the library:
How do I initialise the library's context? I cannot assume that library users will make use of Spring too, but I can distribute Spring with the library.
How do I manage my filesystem monitoring thread? Is it good design to expect the program to instantiate a main class of the library and the call init or something like that?
How do I initialise the library's context? I cannot assume that
library users will make use of Spring too, but I can distribute Spring
with the library.
I am writing a library using Spring context as well and I did something like that, assuming your library is called FooLib, has two services called FooService and BarService and a class called SpringContext that configures your spring context through java config:
public final class FooLib {
private static ApplicationContext applicationContext;
private FooLib() {
}
public static FooService getFooService() {
return getApplicationContext().getBean(FooService.class);
}
public static BarService getBarService() {
return getApplicationContext().getBean(BarService.class);
}
private static ApplicationContext getApplicationContext() {
if (applicationContext == null) {
applicationContext = new AnnotationConfigApplicationContext(SpringContext.class);
}
return applicationContext;
}
}
Then a client can use BarService this way:
BarService barService = FooLib.getBarService();
How do I manage my filesystem monitoring thread? Is it good design to
expect the program to instantiate a main class of the library and the
call init or something like that?
You can start your monitoring subsystem statically within Spring context, inside the SpringContext class, for example.
#Configuration
#ComponentScan(basePackages = "com.yourgroupid.foolib")
public class SpringContext {
#Bean
public MonitoringSystem monitoringSystem() {
MonitoringSystem monitoringSystem = new MonitoringSystem();
monitoringSystem.start();
return monitoringSystem;
}
}
That should be enough because Spring creates its beans eagerly by default.
Cheers
How do I initialise the library's context? I cannot assume that library users will make use of Spring too, but I can distribute Spring with the library.
It's up to your library to instantiate spring the way you need it. This is typically done in your interface entrypoint which delegates to a routine using e.g., ClassPathXmlApplicationContext to configure spring. A sample could be
public class SpringContextLoader {
private static ApplicationContext ctx = null;
public static void init() {
if (ctx == null) {
ctx = ClassPathXmlApplicationContext("classpath:/applicatonContext.xml");
}
}
}
How do I manage my filesystem monitoring thread? Is it good design to expect the program to instantiate a main class of the library and the call init or something like that?
In this case you will probably provide a non-daemon thread, e.g., a thread which must be terminated manually for the application to exit cleanly. Hence you should provide start and stop mechanisms. In your case these probably better be called registerEventListener and unregisterAllEventListener (since I'm guessing you want to pass filesystem events to the client ... ). Another alternative could be to use quartz scheduling with spring.
How do I initialise the library's context? I cannot assume that library users will make use of Spring too, but I can distribute Spring with the library.
You can use a PropertyPlaceholderConfigurer to read configuration settings from a (possibly external) property file, which can be edited by the users. This way users aren't exposed to the details of Spring, not even to XML config files.
How do I manage my filesystem monitoring thread? Is it good design to expect the program to instantiate a main class of the library and the call init or something like that?
I recommend using the Java concurrency framework, specifically a ScheduledExecutorService to run monitoring task(s) at specified intervals. However, you may want to hide this implementation detail from the users of your library, by only exposing some init method to pass in the desired timer interval.
As far as I know, it is not possible to configure your library to start a thread automatically, you have to define a class as starting point. Using Maven you can create an executable jar:
http://maven.apache.org/plugins/maven-shade-plugin/examples/executable-jar.html
In your main class, simple use:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:myspring-context.xml");
context.registerShutdownHook();
For threads, you can try either implementing the runnable interface, and initialize a bean which starts the threads using spring task executors. A more elegant solution that I can suggest is however creating your thread as a pojo and then using spring task scheduler, as follows:
<bean id="threadPojo" class="com.mydomain.ThreadPojo">
</bean>
<task:scheduled-tasks scheduler="mydomainTaskScheduler">
<task:scheduled ref="threadPojo" method="process" fixed-delay="${delay-pool}"/>
</task:scheduled-tasks>
<task:scheduler id="mydomainTaskScheduler" pool-size="${my-pool-size}" />
I hope it will be helpful.