ApplicationEventPublisher is not being autowired into Component - spring

I have a Spring Boot app and #Component class which looks like:
#Component
public class CustomEvent {
#Autowired
ApplicationEventPublisher publisher;
#PreRemove
public void onItemDelete(Object entity) {
System.out.println(" =======PUBLISH====== " + entity);
publisher.publishEvent(new EntityDeleteEvent<>(entity));
}
}
When it goes to run above method the first line is printed with proper entity but the publisher.publishEvent line throws a NullPointerException. I suppose it that the ApplicationEventPublisher is not being #Autowired but couldn't find why. Other #Components which are in the app are found by #ComponentScanner.
Of course in my entity this CustomEvent is registered:
#Entity
#EntityListeners(
CustomEvent.class
)
#Data
#AllArgsConstructor
public class Item
The exact error which is thrown looks like:
2017-10-26 16:46:06.190 ERROR 10176 --- [io-8091-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
at com.inventory.events.CustomEvent.onItemDelete(CustomEvent.java:19)
Do you have any suggestion why publisher is null?

The initialisation of ApplicationEventPublisher doesn't happen OR will remain null, if you have created the CustomeEvent without the help of Bean (like CustomEvent event = new CustomEvent().
Instead, declare the CustomEvent as bean in your configuration (Spring) and get the CustomEvent using application context.

If CustomEvent is in the Spring's package scan, then I don't know.
But, there is an additional solution.
Create a class to instantiate spring managed class, but by ApplicationContext.
1 - Create the class below:
public class AppContextUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext appContext) throws BeansException {
context = appContext;
}
public static ApplicationContext getApplicationContext() {
return context;
}
public static <T> T getBean(Class<T> classe) {
return context.getBean(classe);
}
}
2 - Instance class as below:
public class CustomEvent {
private ApplicationEventPublisher publisher;
#PreRemove
public void onItemDelete(Object entity) {
System.out.println(" =======PUBLISH====== " + entity);
getApplicationEventPublisher().publishEvent(new EntityDeleteEvent<>(entity));
}
private ApplicationEventPublisher getApplicationEventPublisher() {
return AppContextUtil.getBean(ApplicationEventPublisher.class);
}
}

Related

How to get ApplicationContext inside a unit test class of non-spring managed class

I have a requirement where I had to use Spring beans inside a POJO class to retrieve a MyDomainClass object. Since Autowiring the beans inside the class which is not managed by Spring isn't possible, I had to explicitly get the ApplicationContext from another util class and retrieve the bean from applicationContext object. Please find the code below
#Component
public class ApplicationContextUtils implements ApplicationContextAware{
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext appContext)
throws BeansException {
applicationContext = appContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
The code for using the applicationContext and retrieving the bean is below
public class MyNonBeanClass{
public MyDomainClass retrieveMyDomainObj(){
ApplicationContext applicationContext=ApplicationContextUtils.getApplicationContext();
TestBean testBean=(TestBean)applicationContext.getBean("testBean");
return testBean.retrieve(param1,param2);
}
}
This code is working as expected and I am able to fetch the beans from the application context successfully. But writing the test case has been challenging to say the least. I am not even sure if we will be able to write the test case for my non bean class but I would still like to try. I am trying to use PowerMock to mock the static invocation of getApplicationContext() in ApplicationContextUtils. But I am unable to get the applicationContext necessary for getting the TestBean object. And a NullPointerException is thrown when the getBean() is called on applicationContext. Please help with this.
#RunWith(PowerMockRunner.class)
#PrepareForTest(ApplicationContextUtils.class)
#ContextConfiguration(classes= { BaseTestContext.class})
public class MyNonBeanClassTest implements ApplicationContextAware{
MyNonBeanClass myNonBeanClass;
ApplicationContext applicationContext;
#Test
public void test_retrieveMyDomainObj(){
myNonBeanClass=new MyNonBeanClass();
PowerMockito.mockStatic(ApplicationContextUtils.class);
when(ApplicationContextUtils.getApplicationContext()).thenReturn(applicationContext);
assertNotNull(myNonBeanClass.retrieveMyDomainObj(param1, param2));
}
#Override
public void setApplicationContext(ApplicationContext appContext) throws BeansException {
// TODO Auto-generated method stub
this.applicationContext=appContext;
}
public ApplicationContext getApplicationContext(){
return this.applicationContext;
}
}

SingletonBean with PrototypeBean dependency

I am learning the scope dependency of injected beans in Spring framework. I was learning to solve the narrow scope bean dependency from websites. But I am not able to see the resolution as explained in the websites. I have tried using the method of Inject ApplicationContext bean into MySingletonBean to get instance of MyPrototypeBean. However I do not see any difference in the bean creation.
#SpringBootApplication
#ComponentScan("com.learning.spring.basics.scope.proxy")
public class SpringScopeProxyApplication {
public static void main(String...strings) throws InterruptedException {
ApplicationContext ctx = SpringApplication.run(SpringScopeProxyApplication.class, strings);
SingletonBean bean1=ctx.getBean(SingletonBean.class);
/**
* Singleton bean is wired with a proototypeBean. But since a singleton bean is created only once by the container
* even the autowired proptotyeBean is also created once.
*/
bean1.display();
SingletonBean bean2=ctx.getBean(SingletonBean.class);
bean2.display();
}
}
#Component
public class SingletonBean {
#Autowired
// prototypeBean is of scope prototype injected into singleton bean
private PrototypeBean prototypeBean;
//Fix 1 - Inject ApplicationContext and make the singletonbean applicationcontextaware
#Autowired
private ApplicationContext applicationContext;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public PrototypeBean getPrototypeBean() {
return prototypeBean;
}
public void setPrototypeBean(PrototypeBean prototypeBean) {
this.prototypeBean = prototypeBean;
}
// Fix -1 - get the prototypeBean object from applicationContext everytime the method is called
public void display() {
applicationContext.getBean(PrototypeBean.class).showTime();
//prototypeBean.showTime();
}
PrototypeBean
package com.learning.spring.basics.scope.proxy;
import java.time.LocalDateTime;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
#Component
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBean {
public void showTime() {
System.out.println("Time is "+LocalDateTime.now().toString());
}
}
Expected Results:The time stamp should be different as the prototype scoped bean should be a new instance
Actual results: The timestamps are same with no difference
This is not right way to validate SCOPE_PROTOTYPE bean because showTime is an instance methods that gets executed when you invoke it through object so you will always get the different timestamp.But i'm not sure how you are getting same timestamp, but below is an example
#SpringBootApplication
#ComponentScan("com.learning.spring.basics.scope.proxy")
public class SpringScopeProxyApplication {
public static void main(String...strings) throws InterruptedException {
ApplicationContext ctx = SpringApplication.run(SpringScopeProxyApplication.class, strings);
SingletonBean bean1=ctx.getBean(PrototypeBean.class);
System.out.println(bean1);
SingletonBean bean2=ctx.getBean(PrototypeBean.class);
System.out.println(bean2);
}
}
You will get the two different object references
com.learning.spring.basics.scope.proxy.PrototypeBean#48976e6d
com.learning.spring.basics.scope.proxy.PrototypeBean#2a367e93

Spring: Register a component within a test class

I am registering an ErrorHandler for my Spring Scheduler and would like to test that is is correctly registered in a SpringTest
So far I have tried:
Handler
#Component
public class ScheduledErrorHandler implements ErrorHandler {
#Autowired
private ErrorService errorService;
#Override
public void handleError(final Throwable t) {
errorService.handle(t);
}
}
Registering the Handler
#EnableScheduling
#Configuration
public class SchedulingConfiguration implements SchedulingConfigurer {
#Autowired
private ScheduledErrorHandler handler;
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(1);
scheduler.setErrorHandler(handler);
scheduler.initialize();
taskRegistrar.setScheduler(scheduler);
}
//...
}
Testing it's registered
#ContextConfiguration(classes = {
SchedulerConfiguration.class,
SchedulerErrorHandler.class
})
#RunWith(SpringRunner.class)
public class SchedulerErrorHandlerTest {
#MockBean
private ErrorService service;
#Autowired
private ExampleScheduledJob job;
#Test
public void verifyHandlerGetsCalled() {
// Wait until the job runs
if(!job.latch.await(5, SECONDS)) {
fail("Job never ran");
}
verify(service).handle(any(RuntimeException.class));
}
#Component
public static class ExampleScheduledJob {
private final CountDownLatch latch = new CountDownLatch(1);
#Scheduled(fixedRate=1000)
public void run() {
latch.countDown();
throw new RuntimeException("error");
}
}
}
However when I do this I get a DependencyNotFound error saying Spring cannot create my test class as no Bean named ExampleScheduledJob can be found. How can I register it only for the sake of this test?
Error creating bean with name
'com.example.demo.SchedulerErrorHandlerTest': Unsatisfied dependency
expressed through field 'job'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'com.example.demo.SchedulerErrorHandlerTest$ExampleScheduledJob'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
This should work
#ContextConfiguration(classes = {
SchedulingConfiguration.class,
SchedulerErrorHandlerTest.ExampleScheduledJob.class,
ScheduledErrorHandler.class
})
#RunWith(SpringRunner.class)
You can register your test configuration class (ExampleScheduledJob) as indicated above. Since it is a static inner class, you need to use it like SchedulerErrorHandlerTest.ExampleScheduledJob

Why #Inject is not working when implement BeanFactoryPostProcessor

I have some web app with simple task:
public class CustomTask {
private final Logger logger = LoggerFactory.getLogger(getClass());
#Inject
private CustomDao customDao;
#Override
#Transactional(propagation = Propagation.REQUIRED, timeout = 600)
public int run() {
customDao.doSomething();
}
}
now it work with no problem. But now I want to add implementation of BeanFactoryPostProcessor (implements BeanFactoryPostProcessor)to this task and override this method:
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
logger.info(beanFactory.getClass() + "xxxxxxxxxx");
logger.info("The factory contains the followig beans:");
String[] beanNames = beanFactory.getBeanDefinitionNames();
for (int i = 0; i < beanNames.length; ++i)
logger.info(beanNames[i]);
}
but now then I want to do something with customDao it throw NullPointException and I dont know why because in logger I see that this bean is registred. Can you explain me why this exception occurs and how should I fix it ?
This will not work because #Inject (just like #Autowired) is detected by AutowiredAnnotationBeanPostProcessor which is a BeanPostProcessor. BeanFactoryPostProcessors are instantiated BEFORE any other bean in the application context. So, when your post processor is created, the infrastructure that detects #Inject annotation isn't even created and cannot act on your bean factory post processor.
See the api docs for Autowired annotation where there is a note about the restriction for BeanFactoryPostProcessors.
You could make your task to implement BeanFactoryAware and inject the bean yourself:
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.customDao = beanFactory.getBean(CustomDao.class);
}

Access spring component from servlet

I have a controller (for example. MyManager) where I invoke method (for example myMethod() ) of component class (bean), for example MyComponent. I have I servlet where I want to invoke myMethod() . In servlet I have annotated MyManager by #Autowired annotation , despite this I got NullPointerException. I saw this kind of topic but it is not useful for me. For imagination I write little code :
public class myClass extends HttpServlet {
#Autowired
private MyComponent component;
public void init(ServletConfig config) throws ServletException{
super.init(config);
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
protected void doGet(HttpServletRequest req,HttpServletResponse res) throws ... {
List<MyObject> objects =component.myMethod(); // Here is the problem, component is null
}
}
}
I make Spring configuration file "context.xml" and I got bean (component) object, but now I have problem with injected EntityManager in bean object. Now it is null , can anyone help me to solve this problem ? Also update init() method.
public void init(ServletConfig config) throws ServletException{
ApplicationContext con = new ClassPathXmlApplicationContext("context.xml");
component = (MyComponent) con.getBean("myBean");
}
You cannot autowire dependencies like that because Servlets are not Spring Beans. You need to do something like the following:
#Override
public void init() throws ServletException {
super.init();
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
component= applicationContext.getBean(MyComponent.class);
}
Also drop the #Autowired annotation from component

Resources