Thursday, 3 June 2021

Spring

Topics covered in this page:
A) Spring Inversion of Control (IOC) 
B) Dependency injection
C) XML Configuration using p-namespace
D) Autowiring Dependencies
E) Lazy Initialization



Create Sample Project Structure from https://start.spring.io/


A) Spring Inversion of Control (IOC) - Transfers the control of objects to a container or framework
        Example: ApplicationContext

package com.sample.Spring;
public class MessagePojo {
    private String message;
    public String getMessage() { return message; }
    public void setMessage(String message) { this.message = message; }
}

package com.sample.Spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyMainClass {
    public static void main(String[] args) {
        ApplicationContext appContext = new ClassPathXmlApplicationContext("mybean.xml");
        MessagePojo pojo = (MessagePojo) appContext.getBean("messageBean");
        System.out.println(pojo.getMessage());
    }
}

xml file under /src/main/resources
<beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id="messageBean" class="com.sample.Spring.MessagePojo">
        <property name="message" value="This is predefined value in xml"></property>
    </bean>
</beans>


B) Dependency injection: A pattern we can use to implement IoC
    1) Constructor-Based Dependency Injection
    2) Setter-Based Dependency Injection
    3) Field-Based Dependency Injection


1) Constructor-Based Dependency Injection

package com.sample.Spring;
public class SpellChecker {
    public SpellChecker() { System.out.println("Inside SpellChecker constructor."); }
    public void checkSpelling() { System.out.println("Inside checkSpelling method."); }
}


package com.sample.Spring;
public class TextEditor {
private SpellChecker spellChecker;
    public TextEditor(SpellChecker spellChecker) {
        System.out.println("Inside TextEditor constructor.");
        this.spellChecker = spellChecker;
    }
    public void spellCheck() { spellChecker.checkSpelling(); }
}


package com.sample.Spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("mybean.xml");
        TextEditor te = (TextEditor) context.getBean("textEditor");
        te.spellCheck();
    }
}


<beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id = "textEditor" class = "com.sample.Spring.TextEditor">
        <constructor-arg ref = "spellChecker"/>
    </bean>
    <bean id = "spellChecker" class = "com.sample.Spring.SpellChecker"></bean>
</beans>

Output:
Inside SpellChecker constructor.
Inside TextEditor constructor.
Inside checkSpelling.

2) Setter-Based Dependency Injection

package com.sample.Spring;
public class TextEditor {
    private SpellChecker spellChecker;
    // Setter
    public void setSpellChecker(SpellChecker spellChecker) {
        System.out.println("Inside setSpellChecker.");
        this.spellChecker = spellChecker;
    }
    // Getter
    public SpellChecker getSpellChecker() {
        return spellChecker;
    }
    public void spellCheck() {
        spellChecker.checkSpelling();
    }
}

<beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id = "textEditor" class = "com.sample.Spring.TextEditor">
        <property name = "spellChecker" ref = "spellChecker"/>
    </bean>
    <bean id = "spellChecker" class = "com.sample.Spring.SpellChecker"></bean>
</beans>

Output:
Inside SpellChecker constructor.
Inside setSpellChecker.
Inside checkSpelling.

3) Field-Based Dependency Injection

package com.sample.Spring;
public class OrderService  {
public String getOrderDetails(String orderId) {
      return "Order details for order id=" + orderId;
  }
}

package com.sample.Spring;
import org.springframework.beans.factory.annotation.Autowired;
public class OrderServiceClient {
@Autowired
private OrderService orderService;

public void showPendingOrderDetails() {
System.out.println(orderService.getOrderDetails("300"));
}
}

package com.sample.Spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MainApp {

    @Bean
    public OrderService orderService() {
        return new OrderService();
    }

    @Bean
    public OrderServiceClient orderServiceClient() {
        return new OrderServiceClient();
    }

    public static void main(String... strings) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainApp.class);
        OrderServiceClient bean = context.getBean(OrderServiceClient.class);
        bean.showPendingOrderDetails();
    }
}

Output
Order details for order id=300


C) XML Configuration using p-namespace

THIS
<?xml version = "1.0" encoding = "UTF-8"?>
<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   <bean id = "john-classic" class = "com.example.Person">
      <property name = "name" value = "John Doe"/>
      <property name = "spouse" ref = "jane"/>
   </bean>
   <?xml version = "1.0" encoding = "UTF-8"?>

CAN BE REPLACED BY THIS
<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns:p = "http://www.springframework.org/schema/p"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   <bean id = "john-classic" class = "com.example.Person"
      p:name = "John Doe"
      p:spouse-ref = "jane"/>
   </bean>
</beans>

D) Autowiring Dependencies

1) The Spring framework enables automatic dependency injection. In other words, by declaring all the bean dependencies in a Spring configuration file, Spring container can autowire relationships between collaborating beans. This is called Spring bean autowiring.

2) When a bean is being constructed, the @Autowired dependencies should be available. Otherwise, if Spring cannot resolve a bean for wiring, it will throw an exception. To fix this, we need to declare a bean of the required type: @Autowired(required = false)

3) We can use @Autowired on Properties, Setters and Constructors

4) By default, Spring resolves @Autowired entries by type. If more than one bean of the same type is available in the container, the framework will throw a fatal exception. To resolve this conflict, we need to tell Spring explicitly which bean we want to inject using @Qualifier annotation

@Component("fooFormatter")
public class FooFormatter implements Formatter {
    public String format() {
        return "foo";
    }
}
@Component("barFormatter")
public class BarFormatter implements Formatter {
    public String format() {
        return "bar";
    }
}
public class FooService {
    @Autowired
    @Qualifier("fooFormatter")
    private Formatter formatter;
}

5) Autowiring can be done by Custom Qualifier

@Qualifier
@Target({
  ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface FormatterType {  
    String value();
}

@FormatterType("Foo")
@Component
public class FooFormatter implements Formatter {
    public String format() {
        return "foo";
    }
}
@FormatterType("Bar")
@Component
public class BarFormatter implements Formatter {
    public String format() {
        return "bar";
    }
}
@Component
public class FooService {  
    @Autowired
    @FormatterType("Foo")
    private Formatter formatter;
}

E) Lazy Initialization

When we configure a bean with lazy initialization, the bean will only be created, and its dependencies injected, once they're needed.

Enable Lazy Initialization: It can be enabled in any of the below following ways

1) In application.yml add
spring:
  main:
lazy-initialization: true
Note: All the defined beans will use lazy initialization, except for those that we explicitly configure with @Lazy(false).

2) In application.properties add
spring.main.lazy-initialization=true
Note: All the defined beans will use lazy initialization, except for those that we explicitly configure with @Lazy(false).

3) Use the SpringApplicationBuilder method
SpringApplicationBuilder(Application.class)
  .lazyInitialization(true)
  .build(args)
  .run();

4) Use the SpringApplication class
SpringApplication app = new SpringApplication(Application.class);
app.setLazyInitialization(true);
app.run(args);

5) Use with Example

public class Writer {
    private final String writerId;
    public Writer(String writerId) {
        this.writerId = writerId;
        System.out.println(writerId + " initialized!!!");
    }
    public void write(String message) {
        System.out.println(writerId + ": " + message);
    }
}

@SpringBootApplication
public class Application {
    @Bean("writer1")
    public Writer getWriter1() {
        return new Writer("Writer 1");
    }
    @Bean("writer2")
    public Writer getWriter2() {
        return new Writer("Writer 2");
    }
    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(Application.class, args);
ctx.setLazyInitialization(true);
ctx.run(args);
        System.out.println("Application context initialized!!!");

        Writer writer1 = ctx.getBean("writer1", Writer.class);
        writer1.write("First message");

        Writer writer2 = ctx.getBean("writer2", Writer.class);
        writer2.write("Second message");
    }
}

No comments:

Post a Comment