Spring: Publishing event from InitializingBean's afterPropertiesSet method does NOT work - spring

Recently I have found that when I publish an event from org.springframework.beans.factory.InitializingBean.afterPropertiesSet(), then it is unable to publish that event!
However this very same event trigger gets invoked if I invoke it from #Controller or any other class class (the event invocation mechanism remains same for both places).
I have put a print statement after publishing event in InitBean ('Trigger done') and that is successfully printed too.
If you have any idea about this behaviour then please let me know.
Thanks very much
//Sample code for InitializingBean:
#Component
public class InitBean implements InitializingBean {
private final ApplicationEventPublisher publisher;
public InitBean(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
#Override
public void afterPropertiesSet() throws Exception {
this.publisher.publishEvent(new TriggerEvent());
System.out.println("Trigger done");
}
}
// Sample code for trigger event:
public class TriggerEvent extends ApplicationEvent {
public TriggerEvent() {
super("source");
}
}
// Sample code for listener:
#Component
public class TriggerListener {
#EventListener(TriggerEvent.class)
public void trigger(TriggerEvent triggerEvent) {
System.out.println("Trigger event has come");
}
}

Without testing, I think the problem is that afterPropertiesSet is just too early in the Spring Bean live cycle.
Try firing the event a little later.
Rather in a #PostConstruct, an init-method, or when the application context refresh event was catched.
#PostConstruct:
#Component
public class InitBean {
...
#PostConstruct
public void fire() throws Exception {
this.publisher.publishEvent(new TriggerEvent());
System.out.println("Trigger done");
}
}
init-method:
#Configuration
public class AppConfig {
#Bean(initMethod="fire")
public InitBean initBean (ApplicationEventPublisher publisher) {
return new InitBean (publisher);
}
}
public class InitBean {
...
#PostConstruct
public void fire() throws Exception {
this.publisher.publishEvent(new TriggerEvent());
System.out.println("Trigger done");
}
}
context refresh way
#Component
public class InitBean {
...
#EventListener
public void onApplicationEvent(ContextRefreshedEvent event) {
fire();
}
public void fire() throws Exception {
this.publisher.publishEvent(new TriggerEvent());
System.out.println("Trigger done");
}
}
I don't know if the first two approaches solve the suspected problem, but the last one should work.

Related

How to trigger listener after 1 particular bean initialized

Does anybody know if it is possible to create Spring Boot listener that would be called once only 1 particular bean has been initialized?
I only know how to create listener that is triggered once all beans have been initialized:
#EventListener(ContextRefreshedEvent.class)
public void myListener(ContextRefreshedEvent event) {...}
But that listener will be triggered for every single bean in the app instead of 1 particular bean I am looking for.
Any ideas?
Here's how this could be done.
First make your bean publish an event when it's initialized by implementing InitializingBean or having a #PostConstruct method:
public class SomeBeanInitializedEvent extends ApplicationEvent {
...
public SomeBeanInitializedEvent(Object source) {
super(source);
}
}
#RequiredArgsConstructor
public class SomeBean implements InitializingBean {
private final ApplicationEventPublisher applicationEventPublisher;
#Override
public void afterPropertiesSet() {
applicationEventPublisher.publishEvent(new SomeBeanInitializedEvent(this));
}
}
Or as a #Bean method use the standard ApplicationEventPublisher available in the context:
#Bean
public SomeBean someBean(ApplicationEventPublisher publisher) {
SomeBean someBean = ...
publisher.publishEvent(new SomeBeanInitializedEvent(someBean));
return someBean;
}
Then create an event listener for your event:
private class SomeBeanInitializedEventApplicationListener implements ApplicationListener<SomeBeanInitializedEvent> {
#Override
public void onApplicationEvent(SomeBeanInitializedEvent event) {
log.info("Got SomeBeanInitializedEvent: {}", event);
}
}
Then register this application event listener via spring.factories or the setter method on SpringApplication/SpringApplicationBuilder:
#SpringBootApplication
public class SomeApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(SomeApplication.class)
.listeners(new SomeBeanInitializedEventApplicationListener())
.run(args);
}
...
You cannot use an #EventListener annotated method in this case because it'll be registered as a listener too late, after an event from your bean has already been fired.

Injecting dependency inside AbstractWebSocketHandler

How to inject a dependency inside a Web Socket handler:
public class WebsocketHandler extends AbstractWebSocketHandler {
#Autowired
GreetingMap greetingMap;
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
// NullPointerException here
String greeting = greetingMap.getSampleGreetings().get("hello") + " " + message.getPayload();
session.sendMessage(new TextMessage(greeting));
}
}
The code above throws NullPointerException
What could be missing here?
Try using dependency injection with constructor instead #Autowired:
private GreetingMap greetingMap;
public WebsocketHandler(GreetingMap greetingMap){
this.greetingMap = greetingMap
}
I think the problem is that SocketHanler is not a spring bean, but is created by "new" operator:
#Configuration
#EnableWebSocket
public class WebSocketsConfiguration implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new SocketHandler(), "/socket")
.setAllowedOrigins("*");
}
}
What you need to do in this case, is to inject your dependency into WebSocketConfiguration and pass it manually to SocketHandler constructor:
#Configuration
#EnableWebSocket
public class WebSocketsConfiguration implements WebSocketConfigurer {
#Autowired
MyDependency myDependency;
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new SocketHandler(myDependency), "/socket")
.setAllowedOrigins("*");
}
}
And in the handler, you need to add constructor that receives the dependency
public class SocketHandler extends AbstractWebSocketHandler {
private MyDependency myDependency;
public SocketHandler(MyDependency myDependency) {
this.myDependency = myDependency;
}
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
System.out.println(String.format("Message from client: %s", message));
}
}

Different ways to run custom code before the application starts

Could you describe different ways to run custom code before the application starts for data initialization or something else?
(like ApplicationListener, CommandLineRunner etc.)
What is the difference between all of them? Which cases is better to use each of them in?
I want to know not only one way to do that but an understanding when and what I need to use.
Here is enough old question with too many options to do that: Running code after Spring Boot starts
If it is a wrong place to ask this question, please, point me to the right one.
What options I know:
CommandLineRunner - receive command-line arguments as String
#Slf4j
#Component
public class DemoCommandLineRunner implements CommandLineRunner {
#Override
public void run(String... args) {
log.info("[CommandLineRunner] Args: " + Arrays.toString(args));
}
}
ApplicationRunner - receive command-line arguments with names
#Slf4j
#Component
public class DemoApplicationRunner implements ApplicationRunner {
#Override
public void run(ApplicationArguments args) {
log.info("[ApplicationRunner] Args: ");
nonOptionArgs(args);
optionArgs(args);
}
private void nonOptionArgs(ApplicationArguments args) {
args.getNonOptionArgs().forEach(log::info);
}
private void optionArgs(ApplicationArguments args) {
args.getOptionNames().stream()
.map(args::getOptionValues)
.map(Objects::toString)
.forEach(log::info);
}
}
ApplicationListener - listener for different events (for each event own class)
#Slf4j
#Component
public class DemoApplicationListener implements ApplicationListener<ApplicationEvent> {
#Override
public void onApplicationEvent(ApplicationEvent event) {
logEvent(event);
}
private void logEvent(ApplicationEvent event) {
log.info("[DemoApplicationListener] Event: " + event);
}
}
#EventListener - listener for different events (several events in one bean)
#Slf4j
#Component
public class DemoEventApplicationListener {
#EventListener
public void handleContextRefreshedEvent(ContextRefreshedEvent event) {
logEvent(event);
}
#EventListener
public void handleApplicationReadyEvent(ApplicationReadyEvent event) {
logEvent(event);
}
private void logEvent(ApplicationEvent event) {
log.info("[DemoEventApplicationListener] Event: " + event);
}
}
SmartLifecycle - configure bean lifecycle
#Slf4j
#Component
public class DemoSmartLifecycle implements SmartLifecycle {
private boolean isRunning;
#Override
public void start() {
isRunning = true;
log.info("[DemoSmartLifecycle]: Start");
}
#Override
public void stop() {
isRunning = false;
log.info("[DemoSmartLifecycle]: Stop");
}
#Override
public boolean isRunning() {
return isRunning;
}
}
SmartInitializingSingleton - triggered at the end of the singleton pre-instantiation phase
#Slf4j
#Component
public class DemoSmartInitializingSingleton implements SmartInitializingSingleton {
#Override
public void afterSingletonsInstantiated() {
log.info("[SmartInitializingSingleton] afterSingletonsInstantiated");
}
}
Github repo: https://github.com/venkaDaria/demo-bootstrap-spring
If you need to run some code "once the SpringApplication has started" you should use ApplicationRunner or CommandLineRunner - they work the same way.
ApplicationListener, or #EventListener with ApplicationReadyEvent do the same as well.
See my example.
The option you choose is up to you.

spring boot how to unregister guava eventbus listener?

there's a spring boot application. and I create an event like UserCreateEvent and have a listener UserCreateListener
event:
public class UserCreateEvent {
private Long userId;
}
listener:
#Component
public class UserCreateListener {
#Autowired
private Eventbus eventbus;
#PostConstruct
public void init() {
this.eventbus.register(this)
}
#Subscribe
public void onUserCreate(UserCreateEvent event) {
Long userId = event.getUserId();
// todo something necessary
}
}
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Application.class);
application.run(args);
}
}
now, I want to unregister the UserCreateListener after spring boot application startup. how can I make Eventbus unregister this event and listener??
Introduce an unregister() method in the UserCreateListener
#Component
public class UserCreateListener {
#Autowired
private Eventbus eventbus;
#PostConstruct
public void init() {
this.eventbus.register(this)
}
public void unregister() {
this.eventbus.unregister(this)
}
}
Then if you want to unregister get autowired UserCreateListener (or retrieve the bean from application context) and call listenerInstance.unregister()
UPDATE
Create your own component and call unregister there. Guess the listener and eventbus are singletons.
#Component
public class MyUnregisterService {
#Autowired
private Eventbus eventbus;
#Autowired
private UserCreateListener listener;
public void unregister() {
eventbus.unregister(listener)
}
}

How to go about Spring autowiring?

public class ProcessSchedulerServlet implements javax.servlet.Servlet {
Timer timer=new Timer();
#Override
public void init(ServletConfig arg0) throws ServletException {
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
LogProcessorService logProcessorService=new LogProcessorServiceImpl();
logProcessorService.processPageRequestsLogs();
}
}, 60*1000, 120*1000);
}
This is ugly and it doesn't work, anyway. The LogProcessorServiceImpl has properties with #Autowired annotation. These properties are not autowired when this code runs. This may be expected.
The real question is: how to make this run() method work. It seems to me that Spring wants the logProcessorService to be autowired to have properties within LogProcessorServiceImpl autowired, as well.
=== SCENARIO 1 ==============================================================
public void run() {
final LogProcessorService logProcessorService=null;
WebApplicationContextUtils.getWebApplicationContext(arg0.getServletContext()).getAutowireCapableBeanFactory().autowireBean(logProcessorService);
logProcessorService.processPageRequestsLogs();
}
Result: compile time error: Cannot refer to a non-final variable arg0 inside an inner class defined in a different method
=== SCENARIO 2 ==============================================================
#Autowired
LogProcessorService logProcessorService;
public void run() {
logProcessorService.processPageRequestsLogs();
}
Result: run time error: logProcessorService is null;
==== SOLUTION (from Boris) ======================================================
public class ProcessSchedulerServlet implements javax.servlet.Servlet {
Timer timer=new Timer();
#Autowired
LogProcessorService logProcessorService;
#Override
public void init(ServletConfig arg0) throws ServletException {
final AutowireCapableBeanFactory autowireCapableBeanFactory=WebApplicationContextUtils.getWebApplicationContext(arg0.getServletContext()).getAutowireCapableBeanFactory();
autowireCapableBeanFactory.autowireBean(this);
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
logProcessorService.processPageRequestsLogs();
}
}, 60*1000, 120*1000);
}
Why bother with servlets and Timer class if Spring has a built in scheduling support:
#Service
public class LogProcessorService {
#Scheduled(fixedRate=120*1000, initialDelay=60*1000)
public void processPageRequestsLogs() {
//...
}
}
That's it! No timers, runnables and servlets. Note: initialDelay was introduced in Spring 3.2 M1 (see SPR-7022).

Resources