An #Async method in a #Service-annotated class is not being called asynchronously - it's blocking the thread.
I've got <task: annotation-driven /> in my config, and the call to the method is coming from outside of the class so the proxy should be being hit. When I step through the code, the proxy is indeed hit, but it doesn't seem to go anywhere near any classes related to running in a task executor.
I've put breakpoints in AsyncExecutionInterceptor and they never get hit. I've debugged into AsyncAnnotationBeanPostProcessor and can see advice getting applied.
The service is defined as an interface (with the method annotated #Async there for good measure) with the implementation's method annotated #Async too. Neither are marked #Transactional.
Any ideas what may have gone wrong?
-=UPDATE=-
Curiously, it works only when I have my task XML elements in my app-servlet.xml file, and not in my app-services.xml file, and if I do my component scanning over services from there too. Normally I have one XML file with only controllers in it (and restrict the component-scan accordingly), and another with services in it (again with a component-scan restricted such that it doesn't re-scan the controllers loaded in the other file).
app-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:webflow="http://www.springframework.org/schema/webflow-config"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd"
>
<task:annotation-driven executor="executor" />
<task:executor id="executor" pool-size="7"/>
<!-- Enable controller annotations -->
<context:component-scan base-package="com.package.store">
<!-- <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> -->
</context:component-scan>
<tx:annotation-driven/>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<mvc:annotation-driven conversion-service="conversionService" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
app-services.xml (doesn't work when specified here)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<!-- Set up Spring to scan through various packages to find annotated classes -->
<context:component-scan base-package="com.package.store">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<task:annotation-driven executor="han" />
<task:executor id="han" pool-size="6"/>
...
Am I missing something glaringly obvious in my configuration, or is there some subtle interplay between config elements going on?
For me the solution was to add #EnableAsync on my #Configuration annotated class:
#Configuration
#ComponentScan("bla.package")
#EnableAsync
public class BlaConfiguration {
}
Now the class in package bla.package which has #Async annotated methods can really have them called asynchronously.
With the help of this excellent answer by Ryan Stewart, I was able to figure this out (at least for my specific problem).
In short, the context loaded by the ContextLoaderListener (generally from applicationContext.xml) is the parent of the context loaded by the DispatcherServlet (generally from *-servlet.xml). If you have the bean with the #Async method declared/component-scanned in both contexts, the version from the child context (DispatcherServlet) will override the one in the parent context (ContextLoaderListener). I verified this by excluding that component from component scanning in the *-servlet.xml -- it now works as expected.
Jiří Vypědřík's answer solved my problem. Specifically,
Check if your method annotated with #Async is public.
Another useful information from Spring tutorials https://spring.io/guides/gs/async-method/:
Creating a local instance of the FacebookLookupService class does NOT
allow the findPage method to run asynchronously. It must be created inside
a #Configuration class or picked up by #ComponentScan.
What this means is that if you had a static method Foo.bar(), calling it in that manner wouldn't execute it in async, even if it was annotated with #Async. You'll have to annotate Foo with #Component, and in the calling class get an #Autowired instance of Foo.
Ie, if you have a annotated method bar in class Foo:
#Component
class Foo {
#Async
public static void bar(){ /* ... */ }
#Async
public void bar2(){ /* ... */ }
}
An in your caller class:
class Test {
#Autowired Foo foo;
public test(){
Foo.bar(); // Not async
foo.bar(); // Not async
foo.bar2(); // Async
}
}
Edit: Seems like calling it statically also doesn't execute it in async.
Hope this helps.
Try adding proxy-target-class="true" to all <*:annotation-driven/> elements that support this attribute.
Check if your method annotated with #Async is public.
In my case the #Async method was defined in same class as the sync method that used it, and that apparently caused all jobs to hang on current thread.
Bad
#Component
#EnableAsync
public class TranslationGapiReader {
#Async
public CompletableFuture<GapiFile> readFile(String fileId) {
try { Thread.sleep(2000); } catch (Exception exc) { throw new RuntimeException("ololo", exc); }
return CompletableFuture.completedFuture(null);
}
public Stream<GapiFile> readFiles(Iterable<String> fileIds) {
List<CompletableFuture<GapiFile>> futures = new ArrayList<>();
for (String fileId: fileIds) {
futures.add(readFile(fileId));
}
return Stream.empty();
}
}
Good
#Component
#EnableAsync
public class AsyncGapiFileReader {
#Async
public CompletableFuture<TranslationGapiReader.GapiFile> readFile(String fileId) {
try { Thread.sleep(2000); } catch (Exception exc) { throw new RuntimeException("ololo", exc); }
return CompletableFuture.completedFuture(null);
}
}
#Component
#EnableAsync
public class TranslationGapiReader {
#Autowired
AsyncGapiFileReader asyncGapiFileReader;
public Stream<GapiFile> readFiles(Iterable<String> fileIds) {
List<CompletableFuture<GapiFile>> futures = new ArrayList<>();
for (String fileId: fileIds) {
futures.add(asyncGapiFileReader.readFile(fileId));
}
return Stream.empty();
}
}
I'm not Spring guru enough to understand why does it only work when the #Async method is in a different class, but that's what fixes the issue from my observations.
Firstly make your .xml config looks like:
<task:scheduler id="myScheduler" pool-size="10" />
<task:executor id="myExecutor" pool-size="10" />
<task:annotation-driven executor="myExecutor" scheduler="myScheduler" proxy-target-class="true" />
(Yes, scheduler count and executor thread pool size is configurable)
Or just use default:
<!-- enable task annotation to support #Async, #Scheduled, ... -->
<task:annotation-driven />
Secondly make sure #Async methods are public.
#Async can not be used in conjunction with lifecycle callbacks such as #PostConstruct. To asynchonously initialize Spring beans you currently have to use a separate initializing Spring bean that invokes the #Async annotated method on the target then.
public class SampleBeanImpl implements SampleBean {
#Async
void doSomething() { … }
}
public class SampleBeanInititalizer {
private final SampleBean bean;
public SampleBeanInitializer(SampleBean bean) {
this.bean = bean;
}
#PostConstruct
public void initialize() {
bean.doSomething();
}
}
source
I realized following the tutorial async-method tutorial code that my issue source was: the bean with the annotated #Async method was not being created wrapped in a proxy.
I started digging and realized that there was a message saying
Bean 'NameOfTheBean' is not eligible for getting processed by all
BeanPostProcessors (for example: not eligible for auto-proxying)
You can see here responses about this issue and its basically that BeanPostProcessors are required by every Bean, so every bean injected here and its dependencies will be excluded to be processed later by other BeanPostProcessors, because it corrupted the life cycle of beans. So identify which is the BeanPostProcessor that is causing this and dont use or create beans inside of it.
In my case i had this configuration
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
#Autowired
private Wss4jSecurityInterceptor securityInterceptor;
#Autowired
private DefaultPayloadLoggingInterceptor payloadLoggingInterceptor;
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(securityInterceptor);
interceptors.add(payloadLoggingInterceptor);
}
}
WsConfigurerAdapter is actually a BeanPostProcessor and you realize it because there is always a pattern: #Configuration that extends classes and override some of it functions to install or tweak beans involved in some non functional features, like web service or security.
In the aforementioned example you have to override the addInterceptors and added interceptors beans, so if you are using some annotation like #Async inside DefaultPayloadLoggingInterceptor it wont work. What is the solution? Get ride of WsConfigurerAdapter to start.
After digging a bit i realized a class named PayloadRootAnnotationMethodEndpointMapping at the end was which had all valid interceptors, so i did it manually insted of overriding a function.
#EnableWs
#Configuration
public class WebServiceConfig {
#Autowired
private Wss4jSecurityInterceptor securityInterceptor;
#Autowired
private DefaultPayloadLoggingInterceptor payloadLoggingInterceptor;
#Autowired
public void setupInterceptors(PayloadRootAnnotationMethodEndpointMapping endpointMapping) {
EndpointInterceptor[] interceptors = {
securityInterceptor,
payloadLoggingInterceptor
};
endpointMapping.setInterceptors(interceptors);
}
}
So this will be run after all BeanPostProcessor have done their job. The setupInterceptors function will run when that party is over and install the interceptors beans. This use case may be extrapolated to cases like Security.
Conclusions:
If you are using a #Configuration extending from some class that runs some given functions automatically and you override them, you are probably inside of a BeanPostProcessor, so dont inject beans there and try to use AOP behaviour, because it wont work, and you will see Spring tells it to you with the beforementioned message in the console. In those cases dont use beans but objects (using the new clause).
If you need to use beans digg about which class is carrying the beans you want to setup at the end, #Autowired it and add those beans like i did before.
I hope this may save some time for you.
You need 3 lines of code for Async to work
in applicationContext.xml
At class level #EnableAsync
#Async at method level
#Service
#EnableAsync
public myClass {
#Async
public void myMethod(){
}
write a independent Spring configuration for asynchronous bean.
for example:
#Configuration
#ComponentScan(basePackages="xxxxxxxxxxxxxxxxxxxxx")
#EnableAsync
public class AsyncConfig {
/**
* used by asynchronous event listener.
* #return
*/
#Bean(name = "asynchronousListenerExecutor")
public Executor createAsynchronousListenerExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(100);
executor.initialize();
return executor;
}
}
I overcome this problem with this situation.
Try below:
1. In config create bean for ThreadPoolTaskExecutor
#Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
2. In service method where #Async is used add
#Async("threadPoolTaskExecutor")
public void asyncMethod(){
//do something
}
This should get #Async working.
Related
We are using Spring MCV and i am trying to use spring auto wiring to decouple my code. however, autowiring is not happening at all. Can you please suggest any issue in following code/ dispatcher
dispatcher-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd"
xmlns:context="http://www.springframework.org/schema/context">
<context:component-scan base-package="com.eos.accounts" />
</beans>
User.java
package com.eos.accounts.data;
#Service
public class User {
.......
#Autowired
public UserMilesHelper userMilesHelper ;
.....
public static setUserPoints(User user){
user.setPoints(user.userMilesHelper.getUserPoints(user.getUserId()));
}
IUserMilesHelper.java
package com.eos.accounts.data;
public interface IUserMilesHelper {
public int getUserPoints(int userId);
}
UserMilesHelper.java
package com.eos.accounts.data;
import org.springframework.stereotype.Component;
//I have used #Repository or Qualifier etc, no avail
#Component
public class UserMilesHelper implements IUserMilesHelper {
#Override
public int getUserPoints(int userId) {
return 10;
}
}
Web.xml
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>50</load-on-startup>
</servlet>
your User.java class have to be annotated with #Component too or any of its children for ex.
#Controller
#Service
#RestController
Spring initiliaze component in his context by scanning packages and looking for class annoted with stereotypes (#Controller, #Service, #Repository....) or by explicit instantiation with manually adding a bean. If you use a the new it won't call Spring initilization mecanism and won't inject the dependancy.
I suppose your User class is not a singleton class so you should add #Scope("prototype") and use applicationContext.getBean(User.class) to instantiate with dependancy.
But to be honest i would instead refactor the code to avoid public members and static method to set variable and have class like that:
#Service
public class UserService{
private IUserMilesHelper userHelper;
#Autowired
public UserService(IUserMilesHelper userHelper){
this.userHelper = userHelper:
}
public setUserPoints(User user){
user.setPoints(userHelper.getUserPoints(user.getUserId()));
}
}
By default, the name of the dispatcher servlet is xxx-servlet.xml where xxx is the servlet name. Which means that the spring is looking for dispatcher-servlet.xml, which is not the name of your XML config.
Thus the context itself is not loaded for you. Change it and test.
Apart from that, make sure that you follow best practises. Autowire on interfaces rather than on concrete class. Quick link for you - Spring: Why do we autowire the interface and not the implemented class?
pleae change <context:component-scan base-package="com.eos.accounts" /> to
<context:component-scan base-package="com.eos.accounts.*" />
give it a try and let me know the result.
hope it helps.
I've got a class that Spring finds via component scan and that has a method annotated with #Scheduled:
#Component
public class Foo {
...
#Scheduled(fixedDelay = 60000)
public void update() {
...
The value 60000 is ok for production, but in my tests I want it to be 1000.
How can I achieve that? E.g., can I combine #Scheduled with profiles somehow?
Make delay as property:
#Component
public class Foo {
...
#Scheduled(fixedDelay = ${delay})
public void update() {
You may keep 2 property files. For example dev.properties and prod.properties
Spring will load one of it.
<context:property-placeholder
location="classpath:${spring.profiles.active}.properties" />
Create two beans, one for production and one for testing and annotate both with #Profile accordingly like below
#Bean
#Scheduled(fixedDelay = 1000)
#Profile("test")
public void update() {
}
#Bean
#Scheduled(fixedDelay = 60000)
#Profile("dev")
public void update() {
}
In your unit test class you can switch between them by activating the relevant profile like below
#RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from "classpath:/app-config.xml"
#ContextConfiguration("/app-config.xml")
#ActiveProfiles("dev") //or switch to #ActiveProfiles("test") when testing
public class TransferServiceTest {
#Autowired
private TransferService transferService;
#Test
public void testTransferService() {
// test the transferService
}
}
If #ActiveProfiles("dev") is activated only the dev #scheduled bean will be created otherwise test if the test profile is activated.
I solved this issue like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans ... xmlns:task="http://www.springframework.org/schema/task"
... xsi:schemaLocation="... http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.2.xsd ...>
<!-- Everything for "default" profile, including the bean with "#Scheduled(fixedDelay = 60000)" on UpdaterTracker.update() and the "taskScheduler" bean -->
...
<!-- Activate this profile in Arquillian tests -->
<beans profile="arquillian">
<!-- Update more frequently -->
<bean id="updaterTracker" class="com.foo.UpdaterTracker"/>
<task:scheduled-tasks scheduler="taskScheduler">
<task:scheduled ref="updaterTracker" method="update" fixed-delay="1000"/>
</task:scheduled-tasks>
</beans>
</beans>
The first part defines the beans as usual, including an instance of the UpdaterTracker-bean that performs update() every 60 seconds. The last part is only activated in case "arquillian" profile is active, defining another instance of the UpdaterTracker-bean and a scheduled task that executes update() every second.
The solution is not perfect, because it produces 2 instances of UpdaterTracker and 3 scheduled tasks. It could be optimized by directly referencing the first UpdaterTracker instance in so that we get 1 instance and 2 scheduled tasks.
However, this works for me and the solution has advantages: It does not require additional beans to be coded and can cope with multiple profiles.
I am trying to autowire a class into a WebSocketServlet in the following way:
#Configurable(autowire=Autowire.BY_TYPE)
public class MyServlet extends WebSocketServlet {
#Autowired
public MyClass field;
// etc...
}
Here's what my configuration looks like:
<context:annotation-config />
<context:component-scan base-package="org.*" />
<bean id="config" class="org.*.MyClass">
<!-- a bunch of properties -->
</bean>
Note that autowire used to work just fine as long as I was in a Spring #Controller. I had to step out of that because i don't know how to map a WebSocketsServlet to a method of the #Controller as you do with regular servlets.
Any idea what I might be missing?
In order to use #Configurable, you need to have these line in tour context:
<context:load-time-weaver aspectj-weaving="true"/>
<context:spring-configured/>
<context:annotation-config />
<context:component-scan base-package="org.*" />
In addition, I think you must reference spring-aspect in the Import-Library section of your Manifest.
I didn't succeed to make it work, there is a post on this in the Virgo forum at Eclipse.
If you succeed, let me know how ;)
Getting rid of #Configurable and doing the following in the servlet init method does the trick:
#Override
public void init() throws ServletException {
super.init();
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
As per the spring documentation
Externalized values may be looked up by injecting the Spring Environment into a #Configuration class using the #Autowired or the #Inject annotation:
#Configuration
public class AppConfig {
#Inject Environment env;
#Bean
public MyBean myBean() {
MyBean myBean = new MyBean();
myBean.setName(env.getProperty("bean.name"));
return myBean;
}
}
Before certain methods (or as of now all the methods) I have to call the method of an Aspect to log some messages. My application is functioning correctly otherwise but none of the methods of the Aspect class are called.
I have tried the same cutpoint in same folder structure in my local application but when I try to include it with ZK i am having issues. I have also modified my application-context.xml to support AOP.
This is my aspect class :
package com.mypckg.services.impl;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class MyIntercpeter {
#Pointcut("execution(* com.mypckg.services.impl.MyService.getStudents(..))")
public void performance() {
}
#Before("performance()")
public void doSomethingBeforeExecution() {
System.out.println("Before execution method called...");
}
#AfterReturning("performance()")
public void doSomethingAfterExecution() {
System.out.println("After execution method called...");
}
}
The modifications I made in the application-context.xml are
<beans .........
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
..........
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
.....
<aop:aspectj-autoproxy />
<context:annotation-config />
Am I missing something? Thanks in advance.
Looks like you missed an obvious thing : you forgot to declare beans in spring config?
From Spring docs:
Spring AOP only supports method execution join points for Spring beans, so you can think of a pointcut as matching the execution of methods on Spring beans.
http://static.springsource.org/spring/docs/2.0.x/reference/aop.html
You can declare your beans with annotations or by config.
Also would be better to put a version of spring you use (I supposed it was 2.0.x).
I created a custom annotation for logging following the example in this blog post almost exactly. The main difference that I can see is my LoggerInjector is annotated with #Component.
The annotation works great and I get a non-null Logger instance everywhere except in one case: when I try to log in a method annotated with #Autowired.
For example:
#Repository
public class MyDao
{
#AutowiredLogger
private Logger _logger;
private JdbcTemplate _jt;
#Autowired
public void setDatasource(DataSource ds)
{
_logger.debug("Entering setDs")
_jt = new JdbcTemplate(ds);
_logger.debut("Exiting setDs);
}
}
A NullPointerException is thrown on the first _logger.debug() line.
A snippet from applicationContext.xml:
<mvc:annotation-driven />
<context:component-scan base-package="my.package" />
A snippet from dispatch-servlet.xml:
<mvc:annotation-driven />
<context:component-scan base-package="my.package">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Repository" />
</context:component-scan>
How can I have my #AutowiredLogger injected before the #Autowired methods are run?
I don't think you can control which component gets autowired first. An alternate solution is to use InitializingBean to configure the class after everything has been set.
You could something like
#Override
void afterPropertiesSet() {
_logger.debug("Entering setDs")
_jt = new JdbcTemplate(ds);
_logger.debut("Exiting setDs);
}