Uncategorized

BEAN INITIALIZATION AND DESTRUCTION USING ANNOTATIONS IN SPRING

Spring provides options to call methods before bean construction and before destruction.Spring provides “initMethod” and “destroyMethod” attributes which we can apply in @Bean annotation if you have used java based configuration to create beans.

And if you have defined your beans outside a Java config class (e.g., with the @Component annotation), then you have to use the @PostConstruct and @PreDestroy which are java based annotations to achieve this.

Let’s implement now the bean initialization and destruction for both the use-cases.

  • Case 1: You have created beans using @Bean annotation in a Java based config class.

Phone.java


package com.codegeekslab.device;

public interface Phone {

public void openApp(int number);

}

BasicPhone.java


package com.codegeekslab.type;

import com.codegeekslab.device.Phone;

public class BasicPhone implements Phone {

public void openApp(int number) {
System.out.println("calling via simcard... " + number);
}

public void checkNetwork() {
System.out.println("network found");
}

public void checkDuration() {
System.out.println("you talked for 10 min");
}
}

 

CallingApp.java


package com.codegeekslab.app;

import com.codegeekslab.device.Phone;

public class CallingApp {

private Phone phone;

public CallingApp(Phone phone) {
this.phone = phone;
}

public void makeCall(int number) {
phone.openApp(number);
}

}

CellPhoneConfiguration.java


package com.codegeekslab.configuration;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

import com.codegeekslab.app.CallingApp;
import com.codegeekslab.device.Phone;
import com.codegeekslab.type.BasicPhone;
import com.codegeekslab.type.SmartPhone;

@Configuration
public class CellPhoneConfiguration {
@Bean(initMethod = "checkNetwork", destroyMethod = "checkDuration")
public Phone basic() {
Phone phone = new BasicPhone();
return phone;

}

 

@Bean
public CallingApp callingApp(Phone phone) {

return new CallingApp(phone);
}

}

Test.java


package com.codegeekslab.test;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.codegeekslab.app.CallingApp;

public class Test {

public static void main(String[] args) {

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.codegeekslab.configuration");
context.refresh();
CallingApp callingApp = context.getBean("callingApp", CallingApp.class);
callingApp.makeCall(99999);
context.close();

}
}

Output


Apr 26, 2017 12:08:21 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@378fd1ac: startup date [Wed Apr 26 12:08:21 IST 2017]; root of context hierarchy
network found
calling via simcard... 99999
Apr 26, 2017 12:08:22 PM org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
INFO: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@378fd1ac: startup date [Wed Apr 26 12:08:21 IST 2017]; root of context hierarchy
you talked for 10 min

As you can see in the Java configuration class named as CellPhoneConfiguration I have defined a bean named as “basic” using @Bean annotation and added the attributes initMethod and destroyMethod  like this @Bean(initMethod = “checkNetwork”, destroyMethod = “checkDuration”).Both of these attributes requires the method name which we want to call before bean creation and after bean destruction which in this case are checkNetwork and checkDuration.

I have defined both these methods in the BasicPhone class.So when spring tries to load all the beans when we call the context.scan(“com.codegeekslab.configuration”); context.refresh(); it finds out that “basic” has initMethod and destroyMethod attribute defined So spring understands that it has to call the defined method checkNework before bean creation and checkDuration before bean destruction when we close the ApplicationContext using context.close();.

So by the output, you can understand that it has called all the methods sequentially as we have defined.

  1. network found – when spring loads all the beans when we do context.scan(“com.codegeekslab.configuration”); context.refresh();
  2. calling via simcard… 99999 – when we call the makeCall() method;
  3. you talked for 10 min – when we close the ApplicationContext using context.close();
  • Case 2: You have defined your beans outside a Java config class (e.g., with the @Component annotation).

Phone.java


package com.codegeekslab.device;

public interface Phone {

public void openApp(int number);

}

BasicPhone.java


package com.codegeekslab.type;

import org.springframework.stereotype.Component;

import com.codegeekslab.device.Phone;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
public class BasicPhone implements Phone {

public void openApp(int number) {
System.out.println("calling via simcard... " + number);
}

@PostConstruct
public void checkNetwork() {
System.out.println("network found");
}

@PreDestroy
public void checkDuration() {
System.out.println("you talked for 10 min");
}
}

 

CallingApp.java


package com.codegeekslab.app;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import com.codegeekslab.device.Phone;

@Component
public class CallingApp {
@Autowired
private Phone phone;

public void makeCall(int number) {
phone.openApp(number);
}

}

 

Test.java


package com.codegeekslab.test;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.codegeekslab.app.CallingApp;

public class Test {

public static void main(String[] args) {

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.codegeekslab.configuration");
context.refresh();
CallingApp callingApp = context.getBean("callingApp", CallingApp.class);
callingApp.makeCall(99999);
context.close();

}
}

Output


Apr 28, 2017 9:59:04 AM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@378fd1ac: startup date [Fri Apr 28 09:59:04 IST 2017]; root of context hierarchy
network found
calling via simcard... 99999
Apr 28, 2017 9:59:04 AM org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
INFO: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@378fd1ac: startup date [Fri Apr 28 09:59:04 IST 2017]; root of context hierarchy
you talked for 10 min

As said before when you define your beans using @Component or any other bean creation annotation you have to use @PostConstruct and @PreDestroy which are java based annotations to achieve the pre-processing and post-processing tasks.

As you can see in the BasicPhone class I have marked checkNetwork method with @PostConstruct which will get called when spring container will load all the beans when we do a context.scan(“com.codegeekslab.app”,”com.codegeekslab.type”);
context.refresh();

And another method checkDuration which I have marked with @PreDestroy which will get called before bean destruction when we close the Application context using context.close();

Lazy Loading in Spring

By default Eager loading is enabled in Spring, by that I mean Spring loads all the beans at the time of the Spring container initialization or start or spring application for the first time.So when we do context.scan(“package name”); context.refresh(); it loads all the beans into the spring container.

But there will be the times when you don’t want to load your beans at time of Spring container initialization or when your application starts and you want those beans to get loaded only when they are requested, like for an example when you are requesting a bean using context.getBean(“bean name”, bean class.class); method.

To achieve this Spring provides @Lazy(value=true) annotation which you can put on top of your bean method if you have defined your beans using java based configuration and also you can put this annotation on top of your bean class if you have defined your beans using @Component annotation.let’s apply this annotation on both the use-cases.

  • Case 1: You have created beans using @Bean annotation in a Java based config class.

We will take the same example application of Case 1.

Let’s now add one more bean named as “smart” just like basic bean into our java cofiguration class

SmartPhone.java


package com.codegeekslab.type;

import com.codegeekslab.device.Phone;

public class SmartPhone implements Phone {

public void openApp(int number) {
System.out.println("calling via whatsapp..." + number);
}

public void checkNetwork() {
System.out.println("network found");
}

public void checkDuration() {
System.out.println("you have talked for 10 min");
}
}

Updated CellPhoneConfiguration.java


package com.codegeekslab.configuration;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

import com.codegeekslab.app.CallingApp;
import com.codegeekslab.device.Phone;
import com.codegeekslab.type.BasicPhone;
import com.codegeekslab.type.SmartPhone;

@Configuration
public class CellPhoneConfiguration {
@Bean(initMethod = "checkNetwork", destroyMethod = "checkDuration")
public Phone basic() {
Phone phone = new BasicPhone();
return phone;

}

@Bean(initMethod = "checkNetwork", destroyMethod = "checkDuration")
public Phone smart() {
Phone phone = new SmartPhone();
return phone;

}

@Bean
public CallingApp callingApp( Phone phone) {

return new CallingApp(phone);
}

}

Note:Don’t forget to add @Qualifier(“smart”) in callingApp bean as there are two beans which are qualified to get injected into callingApp bean.

Output


Apr 28, 2017 7:36:37 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@378fd1ac: startup date [Fri Apr 28 19:36:37 IST 2017]; root of context hierarchy
network found
network found
calling via whatsapp...99999
Apr 28, 2017 7:36:37 PM org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
INFO: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@378fd1ac: startup date [Fri Apr 28 19:36:37 IST 2017]; root of context hierarchy
you have talked for 10 min
you talked for 10 min

As you can see from output all the beans get initialized at the time of application startup because Eager loading is enabled by default, Because of which checkNetwork and checkDuration method of SmartPhone class gets called even though it was not requested.

Now suppose I want to make my “basic” bean to get load only when it’s requested or in other terms I want to enable lazy loading in this bean I have to add Lazy(value=true) on top of this bean method like below.

CellPhoneConfiguration.java


package com.codegeekslab.configuration;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

import com.codegeekslab.app.CallingApp;
import com.codegeekslab.device.Phone;
import com.codegeekslab.type.BasicPhone;
import com.codegeekslab.type.SmartPhone;

@Configuration
public class CellPhoneConfiguration {
@Bean(initMethod = "checkNetwork", destroyMethod = "checkDuration")
@Lazy(value=true)
public Phone basic() {
Phone phone = new BasicPhone();
return phone;

}

@Bean(initMethod = "checkNetwork", destroyMethod = "checkDuration")
public Phone smart() {
Phone phone = new SmartPhone();
return phone;

}

@Bean
public CallingApp callingApp(@Qualifier("smart")Phone phone) {

return new CallingApp(phone);
}

}

And now if you run this application “basic” bean don’t get loads up.

Output


Apr 30, 2017 8:50:21 AM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@378fd1ac: startup date [Sun Apr 30 08:50:21 IST 2017]; root of context hierarchy
network found
calling via whatsapp...99999
Apr 30, 2017 8:50:22 AM org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
INFO: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@378fd1ac: startup date [Sun Apr 30 08:50:21 IST 2017]; root of context hierarchy
you have talked for 10 min

 

  • Case 2: You have defined your beans outside a Java config class (e.g., with the @Component annotation).

Same like above example we will add one more bean named as “smart” into our application but this time we will create this bean using @Component instead of @Bean.

 

package com.codegeekslab.type;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import com.codegeekslab.device.Phone;
@Component
public class SmartPhone implements Phone {

public void openApp(int number) {
System.out.println(“calling via whatsapp…” + number);
}
@PostConstruct
public void checkNetwork() {
System.out.println(“network found”);
}
@PreDestroy
public void checkDuration() {
System.out.println(“you have talked for 10 min”);
}
}

Output without @Lazy(value=true)


Apr 30, 2017 10:35:56 AM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@378fd1ac: startup date [Sun Apr 30 10:35:55 IST 2017]; root of context hierarchy
network found
network found
calling via simcard... 99999
Apr 30, 2017 10:35:56 AM org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
INFO: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@378fd1ac: startup date [Sun Apr 30 10:35:55 IST 2017]; root of context hierarchy
you have talked for 10 min
you talked for 10 min

just like our previous example all beans get loaded at the time of application startup.Because of that methods checkNetwork and checkDuration gets called from both the beans “smart” and “basic”.

To make our beans lazy load, just like our previous example we will add @Lazy(value=true),But this time we will add this on top of our bean class which we want to lazy load.

I want my “basic” bean to lazy load so I will add this annotation @Lazy(value=true) on top of this class.

BasicPhone.java


package com.codegeekslab.type;

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import com.codegeekslab.device.Phone;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
@Lazy(value=true)
public class BasicPhone implements Phone {

public void openApp(int number) {
System.out.println("calling via simcard... " + number);
}

@PostConstruct
public void checkNetwork() {
System.out.println("network found");
}

@PreDestroy
public void checkDuration() {
System.out.println("you talked for 10 min");
}
}

Output with @Lazy(value=true)


Apr 30, 2017 11:01:07 AM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@378fd1ac: startup date [Sun Apr 30 11:01:07 IST 2017]; root of context hierarchy
network found
calling via whatsapp...99999
Apr 30, 2017 11:01:07 AM org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
INFO: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@378fd1ac: startup date [Sun Apr 30 11:01:07 IST 2017]; root of context hierarchy
you have talked for 10 min

Leave a Reply

Your email address will not be published. Required fields are marked *