I am encountering a NullPointerException in my Spring application when calling a method on an autowired object. The class in question looks like the following:
#Component
public class Listener {
#Autowired
TemplateService templateService;
#Async
#EventListener
private Future<String> listener1(Event1 event) {
System.out.println(templateService);
return new AsyncResult<>(null);
}
#Async
#EventListener
public Future<String> listener2(Event2 event) {
System.out.println(templateService);
return new AsyncResult<>(null);
}
}
When I publish an event that triggers listener1, a null value is printed, but when I publish an event that triggers listener2, the toString() method of TemplateService is called (as I would expect). I'm probably misunderstanding some aspect of how #Async affects #Autowired objects, but I haven't been able to determine what that would be. Am I misusing #Async? Am I misunderstanding how to use #Autowired objects in a multithreaded environment?
Change the visibility of your listener1 method to be at least protected (package visibility , protected or public). This is because Spring creates a proxy which is a subclass of your component. It overrides your #Async annotated methods in order to add new logic to execute your code in a separate thread. However because it uses inheritance it can only override methods which are visible to the subclass.
This explains why listener2 method which is public works.
Change your method to
#Async
#EventListener
public Future<String> listener1(Event1 event) {
System.out.println(templateService);
return new AsyncResult<>(null);
}
Spring needs an interface to create a proxy class. It's this proxy class that gets called every time you call the method, and it's through this method that the whole asynchronous execution happens. Without an interface Spring can't autowire, scan or make methods execute asynchronously.
public interface Listener {
public Future<String> listener1(Event1 event);
public Future<String> listener2(Event2 event);
}
#Component
public class ListenerImpl {
#Autowired
private TemplateService templateService;
#Async
#Override
public Future<String> listener1(Event1 event) {
System.out.println(templateService);
return new AsyncResult<>(null);
}
#Async
#Override
public Future<String> listener2(Event2 event) {
System.out.println(templateService);
return new AsyncResult<>(null);
}
}
It's also worth noting that Spring can't run private methods asynchronously.
Related
I have a method that does something like this
class EmployeeService {
#Transactional
public void perform() {
// perform DB operations()
createEmployee()
// call some async service that relies on the persisted data
runEmployeeBGV();
}
}
runEmployeeBGV() crates a separate thread which does bgv operations.
I want data to be persisted as soon as the createEmployee() method ends. But due to limitations on #Transactional(and my knowldge), I am not sure on how to exclude runEmployeeBGV() out of the scope of transaction.
Please note that these two operations need to reside within perform() method which is being called by another service class where EmployeeService is injected.
Is there any way we can achieve this without calling these methods separately from the client code?
You can move those methods to another service and move #Transaction to createEmployee()
something like that
class EmployeeService {
private EmployeeRepo repo;
private EmployeeBGV bgv;
public void perform() {
repo.createEmployee()
bgv.runEmployeeBGV();
}
}
class EmployeeRepo {
#Transactional
public void perform() {
//code
}
}
I try to understand why this code doesn't work
In component:
#PostConstruct
public void runAtStart(){
testStream();
}
#Transactional(readOnly = true)
public void testStream(){
try(Stream<Person> top10ByFirstName = personRepository.findTop10ByFirstName("Tom")){
top10ByFirstName.forEach(System.out::println);
}
}
And repository :
public interface PersonRepository extends JpaRepository<Person, Long> {
Stream<Person> findTop10ByFirstName(String firstName);
}
I get:
org.springframework.dao.InvalidDataAccessApiUsageException: You're trying to execute a streaming query method without a surrounding transaction that keeps the connection open so that the Stream can actually be consumed. Make sure the code consuming the stream uses #Transactional or any other way of declaring a (read-only) transaction.
One key thing about Spring is that many annotated features use proxies to provide the annotation functionality. That is #Transactional, #Cacheable and #Async all rely on Spring detecting those annotations and wrapping those beans in a proxy bean.
That being the case, a proxied method can only be used when invoked on the class and not from within the class. See this about the topic.
Try:
Refactoring and call this #Transactional method from another class in your context, or
By self-autowiring the class into itself and calling the #Transactional method that way.
To demonstrate (1):
public class MyOtherClass {
#Autowired
private MyTestStreamClass myTestStreamClass;
#PostConstruct
public void runAtStart(){
// This will invoke the proxied interceptors for `#Transactional`
myTestStreamClass.testStream();
}
}
To demonstrate (2):
#Component
public class MyTestStreamClass {
#Autowired
private MyTestStreamClass myTestStreamClass;
#PostConstruct
public void runAtStart(){
// This will invoke the proxied interceptors for `#Transactional` since it's self-autowired
myTestStreamClass.testStream();
}
#Transactional(readOnly = true)
public void testStream(){
try(Stream<Person> top10ByFirstName = personRepository.findTop10ByFirstName("Tom")){
top10ByFirstName.forEach(System.out::println);
}
}
}
I'm using Spring 4.3.8.RELEASE with Hibernate 5.1.5.Final. I want to have a method executed after another another transaction completes. That transaction is defined below
#Service("organizationService")
#Transactional
public class OrganizationServiceImpl implements OrganizationService, ApplicationEventPublisherAware
{
private ApplicationEventPublisher publisher;
#Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher)
{
this.publisher = publisher;
}
#Override
public void save(Organization organization)
{
...
// sync data with ThirdParty but only if something has definitelychanged on the SB
// side, in which case we want to send ThirdParty an update.
if (!hasSameAttributes)
{
publisher.publishEvent(new ThirdPartyOrganizationEvent(organization.getId()));
} // if
} // save
So here is the method that I want executed after the above transaction completes ...
#Service
public class ThirdPartyAPIServiceImpl implements ThirdPartyAPIService
{
#Override
#TransactionalEventListener
public boolean updateOrg(final ThirdPartyOrganizationEvent thirdPartyOrgEvent)
{
...
}
But when I load my application context I get this error
Caused by: java.lang.IllegalStateException: No TransactionalEventListener annotation found on method: public abstract boolean org.mainco.subco.myproject.service.ThirdPartyAPIService.updateOrg(org.mainco.subco.myproject.domain.ThirdPartyOrganizationEvent)
at org.springframework.transaction.event.ApplicationListenerMethodTransactionalAdapter.<init>(ApplicationListenerMethodTransactionalAdapter.java:55)
at org.springframework.transaction.event.TransactionalEventListenerFactory.createApplicationListener(TransactionalEventListenerFactory.java:55)
at org.springframework.context.event.EventListenerMethodProcessor.processBean(EventListenerMethodProcessor.java:159)
at org.springframework.context.event.EventListenerMethodProcessor.afterSingletonsInstantiated(EventListenerMethodProcessor.java:104)
... 34 more
Wbat do I need to do to get this configured properly?
Defining #TransactionalEventListener on interface method rather then on method implementing interface worked for me.
Is there a way to intercept calls of bean methods annotated with #RabbitListener without using AspectJ.
The code is something like this
#OtherAnnotation
#RabbitListener
public void do(Message message)
I need to intercept all calls to #RabbitListener method, if the method has #OtherAnnotation annotation.
UPDATE:
I managed to make it work using Gary Russell solution.
public class CustomRabbitListenerAnnotationBeanPostProcessor extends RabbitListenerAnnotationBeanPostProcessor {
#Override
protected void processAmqpListener(RabbitListener rabbitListener, final Method method, Object bean, String beanName) {
if (method.isAnnotationPresent(OtherAnnotation.class)) {
ProxyFactory proxyFactory = new ProxyFactory(bean);
proxyFactory.addAdvisor(new StaticMethodMatcherPointcutAdvisor(new OtherAnnotationInterceptor()) {
#Override
public boolean matches(Method advisorMethod, Class<?> targetClass) {
return advisorMethod.equals(method);
}
});
Object proxiedBean = proxyFactory.getProxy();
super.processAmqpListener(rabbitListener, method, proxiedBean, beanName);
} else {
super.processAmqpListener(rabbitListener, method, bean, beanName);
}
}
}
The bean definition is like:
#Bean(name = RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
public CustomRabbitListenerAnnotationBeanPostProcessor customRabbitListenerAnnotationBeanPostProcessor() {
return new CustomRabbitListenerAnnotationBeanPostProcessor();
}
It's a bit ugly, but it works. If anyone has better solution, please share it.
You can subclass RabbitListenerAnnotationBeanPostProcessor and override the processListener method and modify the bean before invoking the super version.
Then, replace the RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME bean registered by the #EnableRabbit with your subclass.
Or, simply add your advice to the container factory's advice chain and all listeners will be advised. You can then do a runtime check to see if the other annotation is present.
i think what you are looking for is a bean-post-processor
heres a simple example:
https://www.tutorialspoint.com/spring/spring_bean_post_processors.htm
if you need to intercept calls you can wrap a proxy over the returned instance. a good example is the org.springframework.validation.beanvalidation.MethodValidationPostProcessor. you can probably extend org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor as well.
EDIT i am just learning this myself so i hope that is the right way to do this but this worked for me when experimenting with it
#Component
public class MyInterceptAnnotationBeanPostProcessor
extends AbstractBeanFactoryAwareAdvisingPostProcessor
implements InitializingBean {
public void afterPropertiesSet() {
AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(
null,
MyIntercept.class);
this.advisor = new DefaultPointcutAdvisor(pointcut, this.createAdvice());
}
protected Advice createAdvice() {
return new MethodInterceptor() {
#Override
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("advice");
return arg0.proceed();
}
};
}
}
I'm toying with asynchronous calls to reduce some waiting time on client side when calling a page.
As a experiement i have a controller that calls a pojo with a method annotated with #Async. In that menthod i sleep for 10000 ms to simulation operation to test whether my theory works, and it seems not to. Code below and further information can be found after it:
Test Controller
#Conroller
public class TestController {
#RequestMapping("/test")
public String testAsyncCall() {
new AsyncTestClass().asyncOpOne();
return "secondpage";
}
}
Asynchronous Class containing the #Async annotated method
public class AsyncTestClass {
#Async
public void asyncOpOne() {
try {
Thread.sleep(10000);
System.out.println("done working");
} catch (InterruptedException e) {
//
}
}
}
Now from my understanding when the client makes the call to "/test" in their browser the controller should return call the asynchronous method and instantly return "secondpage" to be rendered.
What is happening is that the controller doesn't return the second page until the 10000 ms in the asynchronous call has finished, only then it returns the secondpage.
FYI
#EnableAsync is added to one of my config files (using Java Configuration).
What am i doing wrong here that is causing the controller to wait for the async to finish its sleep before continuing?
Spring uses AOP to apply #Async behavior to your beans (the same goes for #Transactional for instance).
Spring will only apply AOP to beans it knows, as you are constructing a new instance outside of the scope of Spring the #Async does nothing. Simply add it as a bean to your configuration and inject it into your controller.
#Bean
public AsyncTestClass asyncTestClass() {
return new AsyncTestClass():
}
Then in your calling class.
#Conroller
public class TestController {
#Autowired
private AsyncTestClass asyncTestClass;
#RequestMapping("/test")
public String testAsyncCall() {
asyncTestClass.asyncOpOne();
return "secondpage";
}
}