SpringBoot bean lifecycle management - spring-boot

I am learning the initialization and destruction callbacks for a bean managed by ApplicationContext in a springboot application. I have a bean which implements InitializingBeans and DisposableBeans interfaces.I do have a #PostConstruct which gets invoked. However I am not seeing the init method invoked when i remove the implementation.What am i missing?
#Component
public class LifeCycleBean implements InitializingBean, DisposableBeans{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public LifeCycleBean() {
// TODO Auto-generated constructor stub
System.out.println("Learning lifecycle - COnstructor invoked"+name);
}
#Override
public void destroy() throws Exception {
System.out.println("Learning lifecycle - Calling Destroy Method");
}
#Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("Learning lifecycle - afterPropertiesSet
invoked"+name);
}
//This never got executed
public void init() {
System.out.println("Learning lifecycle - initMethod invoked"+name);
}
#PostConstruct
public void postConstructMethod() {
System.out.println("Learning lifecycle - postConstructMethod
invoked"+name);
}
#PreDestroy
public void preDestroyMethod() {
System.out.println("Learning lifecycle - preDestroyMethod invoked"+name);
}
}
SpringBootApplication
#SpringBootApplication
public class LifeCycleApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(LifeCycleApplication.class, args);
System.out.println("going to get bean definition names");
ctx.getBeanDefinitionNames();
LifeCycleBean bean = ctx.getBean(LifeCycleBean.class);
System.out.println("before setting name");
bean.setName("bean");
System.out.println("after setting name");
}
}
How and when do i see the init method invoked in springboot application?

Related

Autowiring not working in springboot application

I am trying to create a Spring boot application with JFrame. I can see my beans in applicationContext but they are not getting autowired. I am unable to find the reason for this issue. Can someone help me with this?
Here is the code:
JavauiApplication - it is showing both userManager and userNameRepository is beans
#SpringBootApplication
public class JavauiApplication implements CommandLineRunner {
#Autowired
private ApplicationContext appContext;
public static void main(String[] args) {
new SpringApplicationBuilder(JavauiApplication.class).headless(false).run(args);
java.awt.EventQueue.invokeLater(() -> new InputNameForm().setVisible(true));
}
#Override
public void run(String... args) throws Exception {
String[] beans = appContext.getBeanDefinitionNames();
Arrays.sort(beans);
for (String bean : beans) {
System.out.println(bean);
}
}
}
InputNameForm.java -> userManager coming null
#Component
public class InputNameForm extends javax.swing.JFrame {
/**
* Creates new form InputNameForm
*/
public InputNameForm() {
initComponents();
}
#Autowired
UserManager userManager;
private void submitButtonActionPerformed(java.awt.event.ActionEvent evt) {
userManager.setName(firstName.getText(), lastName.getText());
}
/**
* #param args the command line arguments
*/
public static void main(String args[]) {
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(InputNameForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new InputNameForm().setVisible(true);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextField firstName;
private javax.swing.JLabel firstNameLabel;
private javax.swing.JTextField lastName;
private javax.swing.JLabel lastNameLabel;
private javax.swing.JButton submitButton;
// End of variables declaration//GEN-END:variables
}
UserManager.java -> userNameRepository is coming null
#Component
public class UserManager {
#Autowired
UserNameRepository userNameRepository;
public void setName(String firstName, String lastName) {
userNameRepository.save(new UserName(firstName, lastName));
System.out.println(userNameRepository.findAllByFirstName(firstName));
}
}
It's a very common problem and it occurs because newcomers don't understand how the IoC container works.
Firstly, BeanDefinitionReader reads metadata about your beans from XML, Annotations(#Component, #Service etc), JavaConfig or Groovy script.
There are several BeanPostProcessor's which is responsible for reading all of these Spring annotation you're writing(#Autowired etc).
BeanFactory creates all BeanPostProcessor's then it creates all of your beans.
What happen if you create your bean with #Autowired dependencies via new operator? Nothing, because it isn't actually a bean. The object you created isn't related to IoC container. You may have the bean already in your ApplicationContext if you marked it with #Component(for example) but the object which was created via new operator wont be processed by Spring(annotations won't work).
Hope this helps.
PS: The lifecycle is simplified.
I had the same problem few days ago. What I undertood was that GUI builders like the one that comes with netbeans will automatically create components using new keyword. This means that those components won't be manage by spring. The code usually loks like this:
private void initComponents() {
jPanel1 = new javax.swing.JPanel(); //This component will not be managed by spring.
//...
}
You could use the following class provided here, to make it work.
#Component
public class BeanProvider {
private static ApplicationContext applicationContext;
// Autowires the specified object in the spring context
public static void autowire(Object object) {
applicationContext.getAutowireCapableBeanFactory().autowireBean(object);
}
#Autowired
private void setApplicationContext(ApplicationContext applicationContext) {
BeanProvider.applicationContext = applicationContext;
}
}
The top level SwingApp class:
#SpringBootApplication
public class SwingApp implements CommandLineRunner {
public static void main(String[] args) {
new SpringApplicationBuilder(SwingApp.class)
.headless(false).bannerMode(Banner.Mode.OFF).run(args);
}
#Override
public void run(String... args) throws Exception {
SwingUtilities.invokeLater(() -> {
MainFrame frame = new MainFrame();
frame.setVisible(true);
});
}
}
The MainFrame class:
public class MainFrame extends javax.swing.JFrame {
public MainFrame() {
initComponents();
}
private void initComponents() {
//Gui Builder generated code. Bean not managed by spring.
//Thus, autowired inside CustomPanel won't work if you rely on ComponentScan.
jPanel1 = new CustomJPanel();
//...
}
private CustomJPanel jPanel1;
}
The panel class where you want to autowire things:
//#Component //not needed since it wont work with gui generated code.
public class CustomJPanel extends javax.swing.JPanel{
#Autowired
private SomeRepository someRepository
public CustomJPanel(){
BeanProvider.autowire(this); //use someRepository somewhere after this line.
}
}
I have the same problem in a JavaFx project. Service and Component annotated classes were null in UI controllers even if it was shown in context that it was created. Below code worked for me
#Component
public class FxmlLoaderWithContext {
private final ApplicationContext context;
#Autowired
public FxmlLoaderWithContext(ApplicationContext context) {
this.context = context;
FXMLLoader fxmlloader = new FXMLLoader();
fxmlloader.setControllerFactory(context::getBean); //this row ensure services and components to be autowired
}
}
I think it returns null because you using command new to create object, such as new InputNameForm(). When creating object like that, the object isn't managed by Spring. That's why autowired not working.
The solution is registering your class as a bean.
You can use a class like in here.
#Component
public class BeanProvider {
private static ApplicationContext applicationContext;
public static void autowire(Object object) {
applicationContext.getAutowireCapableBeanFactory().autowireBean(object);
}
#Autowired
private void setApplicationContext(ApplicationContext applicationContext) {
BeanProvider.applicationContext = applicationContext;
}
}
And then, in your class InputNameForm constructor, call this:
class InputNameForm() {
BeanProvider.autowire(this);
...
}
And that's it. Spring will take care the rest.

Spring not injecting a bean into thread

1.How to inject a spring bean into thread
2.How to start a thread inside spring bean.
here is my code.
MyThread.java
#Component
public class MyThread implements Runnable {
#Autowired
ApplicationContext applicationContext;
#Autowired
SessionFactory sessionFactory;
public void run() {
while (true) {
System.out.println("Inside run()");
try {
System.out.println("SessionFactory : " + sessionFactory);
} catch (Exception e) {
e.printStackTrace();
}
try {
Thread.sleep(10000);
System.out.println(Arrays.asList(applicationContext.getBeanDefinitionNames()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
i am calling run method from below class like (Please suggest if i am following wrong appraoch for calling a thread inside spring bean )
#Component
public class MyServiceCreationListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getApplicationContext().getParent() == null) {
System.out.println("\nThread Started");
Thread t = new Thread(new MyThread());
t.start();
}
}
}
spring is not performing dependency injection on MyThread class
There are a couple of things wrong with your setup.
You shouldn't be creating and managing threads yourself, Java has nice features for that use those.
You are creating new bean instances yourself and expect Spring to know about them and inject dependencies, that isn't going to work.
Spring provides an abstraction to execute tasks, the TaskExecutor. You should configure one and use that to execute your task not create a thread yourself.
Add this to your #Configuration class.
#Bean
public ThreadPoolTaskExecutor taskExecutor() {
return new ThreadPoolTaskExecutor();
}
Your MyThread should be annotated with #Scope("prototype").
#Component
#Scope("prototype")
public class MyThread implements Runnable { ... }
Now you can inject these beans and an ApplicationContext into your MyServiceCreationListener
#Component
public class MyServiceCreationListener implements ApplicationListener<ContextRefreshedEvent> {
#Autowired
private ApplicationContext ctx;
#Autowired
private TaskExecutor taskExecutor;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getApplicationContext().getParent() == null) {
System.out.println("\nThread Started");
taskExecutor.execute(ctx.getBean(MyThread.class));
}
}
}
This will give you a pre-configured, fresh instance of MyThread and execute it on a Thread selected by the TaskExecutor at hand.
Your MyThread is created manually rather than via spring context new Thread(new MyThread()); so no chance for spring to inject a bean.
Instead you can add a trick with static access to spring context where you can get a necessary bean from the context (see here or here).
Alternatively you can use ThreadLocal or InheritableThreadLocal to store necessary objects to be used in the thread.
You are creating Thread t = new Thread(new MyThread());.Spring container will not inject the dependency and also not maintain the life cycle of bean.
Example :
#Component
#Scope("prototype")
public class PrintThread extends Thread{
#Override
public void run() {
System.out.println(getName() + " is running");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + " is running");
}
}
to access the thread object from spring context.
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext ctx;
private static final String USER_THREAD = "printThread";
#Override
public void setApplicationContext(ApplicationContext appContext)
throws BeansException {
ctx = appContext;
}
public static ApplicationContext getApplicationContext() {
return ctx;
}
public static UserService getUserService(){return ctx.getBean(USER_THREAD );}
}

#Before #PostConstruct Spring AOP ineterception

I'm trying to write an aspect that can intercept PostConstruct methods. I've looked at related questions on SO and others, and following them, this is what I have so far:
The Spring configuration
#Configuration
#EnableAspectJAutoProxy
#EnableLoadTimeWeaving
#...//other config annotations
public class WebConfiguration {
#Bean
public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
return new CommonAnnotationBeanPostProcessor();
}
... // etc
}
The annotation:
#Retention(RetentionPolicy.RUNTIME)
public #interface Secured {
Permission[] permissions() default {};
}
The bean
#Component
#Scope("request")
public class SomeWebBean {
#Secured(permissions = Permission.SOME_PERMISSION)
#PostConstruct
public void secure() {
... // some stuff
}
}
The aspect
#Component
#Aspect
public class SecuredAspect {
#Before("#annotation(secured)")
public void doAccessCheck(Secured secured) {
... // actually do the access check
}
}
If I call someWebBean.secure() from a page, then the aspect is invoked. However, it is not invoked on bean creation.
So as a note to future me - this absolutely cannot be done in this way using Spring AOP.
However, the same effect can be achieved by implementing a BeanPostProcessor as below:
public class SecureBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Secured secured = bean.getClass().getAnnotation(Secured.class);
if (secured != null) {
// do your security test here, throw an exception, return false, however you like
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
You can extend CommonAnnotationBeanPostProcessor and override postProcessBeforeInitialization(Object bean, String beanName)
Then register replace the original CommonAnnotationBeanPostProcessor with a BeanFactoryPostProcessor .
public class InitCommonAnnotationBeanPostProcessor extends CommonAnnotationBeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return super.postProcessBeforeInitialization(bean, beanName);
}
}
public class InitBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
RootBeanDefinition def = new RootBeanDefinition(InitCommonAnnotationBeanPostProcessor.class);
def.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME, def);
}
}
#Configuration
public class InitialisationMonitoringConfig {
public static final String BEAN_INIT_MONITOR = "BEAN_INIT_MONITOR";
#Bean
public static InitBeanFactoryPostProcessor initBeanFactoryPostProcessor() {
return new InitBeanFactoryPostProcessor();
}
}
This is ugly, but I had to do that to analyse startup times in dev environment.
Maybe it's enough to just declare InitCommonAnnotationBeanPostProcessor as a bean, I didn't tried.

Class Cast Exception in while mocking bean

I am trying to create test cases in my application using powermock and TestNG. This is a web application and spring context xml is loaded through web.xml. I want to mock the spring context xml with my own test xml and access beans from there.
ContextLoader Code
public class AppContextLoader implements ApplicationContextAware {
private static ApplicationContext context;
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
public static Object getBean(String beanName) {
return context.getBean(beanName);
}
}
I am trying to access a bean later in my code like below
XMLConverter converter = (XMLConverter) AppContextLoader.getBean("XMLConverter");
Below is my Test Code
#PrepareForTest(AppContextLoader.class)
#PowerMockIgnore({"javax.management.*", "javax.xml.*","org.xml.*","org.w3c.dom.*"})
public class ImplTest {
Impl impl;
ApplicationContext context;
#ObjectFactory
/**
* Configure TestNG to use the PowerMock object factory.
*/
public IObjectFactory getObjectFactory() {
return new org.powermock.modules.testng.PowerMockObjectFactory();
}
#BeforeClass
public void setUp() throws Exception {
impl = new Impl();
context = new ClassPathXmlApplicationContext("Test_config.xml");
}
#Test
public void execute() {
try {
PowerMock.mockStatic(AppContextLoader.class);
expect(AppContextLoader.getBean("XMLConverter")).andReturn(context.getBean("XMLConverter"));
PowerMock.replay(AppContextLoader.class);
actualResponse = impl.execute(request, "");
//PowerMock.verify(AppContextLoader.class);
Assert.assertEquals("", actualResponse);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
When I am executing this code I am getting below error:
java.lang.ClassCastException: com.xxx.xxx.xxx.xxx.bean.XMLConverter cannot be cast to com.xxx.xxx.xxx.xxx.bean.XMLConverter
I am new to unit testing, and not sure what is causing this issue where same class are not able to cast each other. Any help will be appreciated

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