Programming‎ > ‎Java‎ > ‎15. Spring‎ > ‎

2. Apply IC and DI



What is a Spring Bean

  • A "Spring Bean" is simply a Java object. 
  • When Java objects are created by the Spring Container, then Spring refers to them as "Spring Beans". 
  • Spring Beans are created from normal Java classes .... just like Java objects.
  • In summary, whenever you see "Spring Bean", just think Java object. :-)

Spring Inversion of Control - XML Configuration


What is Inversion of Control

  • Outsource to an Object Factory
  • The approach of outsourcing the construction and management of objects
  • (My App) -> getDailyWorkout -> BaseballCoach
        - App should be configurable
        - Easily change the coach for another sport 

Code Demo


- MyApp.java : main method
- BaseballCoach.java
- Coach.java : interface after refactoring
- TrackCoach.java

  • Right Click at src directory in Eclipse and Create Package - com.deepagar.springdemo
  • Right Click on package - com.deepagar.springdemo
  • Create New Class - BaseballCoach
  • Write code for new method - getDailyWorkout
  • BaseballCoach class is complete
package com.deepagar.springdemo;

public class BaseballCoach {
public String getDailyWorkout() {
return "Spend 30 minutes on batting";
}
}

  • Right Click on package - com.deepagar.springdemo
  • Create New Class - MyApp and include main method check-box
  • In MyApp main, create the BaseballCoach object and use the object
  • Right Click and Run as Java Application
  • Console as print : Spend 30 minutes on batting
package com.deepagar.springdemo;

public class MyApp {

public static void main(String[] args) {
// TODO Auto-generated method stub
// create the object
BaseballCoach theCoach = new BaseballCoach();
// use the object
System.out.println(theCoach.getDailyWorkout());
}
}

Now, let's Code to an interface to support multiple sports
  • Work on requirement that App should on any type of Coach
  • Refactoring Code: Creating an Interface
  • Right Click on package - com.deepagar.springdemo
  • Create new Interface - Coach
  • Add method declaration getDailyWorkout() to interface 
  • Interface only say what is available not how its implemented
  • Now do - BaseballCoach implements Coach
  • Add @Override to getDailyWorkout() in BaseballCoach class, this is the method override from Coach interface
  • In MyApp change variable type from BaseballCoach to Coach
Coach.Java
package com.deepagar.springdemo;

public interface Coach {
public String getDailyWorkout();
}

BaseballCoach.java
package com.deepagar.springdemo;

public class BaseballCoach implements Coach{
@Override
public String getDailyWorkout() {
return "Spend 30 minutes on batting";
}
}

MyApp.java
package com.deepagar.springdemo;

public class MyApp {

public static void main(String[] args) {
// TODO Auto-generated method stub
// create the object
Coach theCoach = new BaseballCoach();
// use the object
System.out.println(theCoach.getDailyWorkout());
}
}


  • Change creation to new TrackCoach instead of BaseballCoack in MyApp
  • TrackCoach doesn't exist, just hover over it and it would give an option to create TrackCoach class
  • Create TrackCoach class using this option - Eclipse has done it for us including the method and override tag
  • Run the main as Java Application, it runs as expected - "Run a marathon"
  • Easy to change coach is achieved, however App is not configurable but hardcoded
  • It would be great if we could read implementation name from config file and use
  • Spring is designed to solve this problem of configuration based object fetch
TrackCoach.java
package com.deepagar.springdemo;

public class TrackCoach implements Coach {

@Override
public String getDailyWorkout() {
// TODO Auto-generated method stub
return "Run a marathon";
}
}

MyApp.java
package com.deepagar.springdemo;

public class MyApp {

public static void main(String[] args) {
// TODO Auto-generated method stub
// create the object
Coach theCoach = new TrackCoach();
// use the object
System.out.println(theCoach.getDailyWorkout());
}
}


Spring Inversion of Control


  • Spring provides Object Factory that gives an object based on the configuration
  • MyApp would as give me a Coach object from Spring Object Factory

Spring Container

  • There are 2 Primary functions
  • Create and manage objects (Inversion of Control)
  • Inject object's dependencies (Dependency Injection)

Configuring Spring Container

  • XML configuration file (legacy, but most legacy apps still use it)
  • Java Annotations (modern)
  • Java Source Code (modern)

Spring Development Process

  • Configure your Spring Beans
  • Create a Spring Container
  • Retrieve Beans from Spring Container

Configure your Spring Beans

  • Here we are using the xml configuration file
  • File: applicationContext.xml
  • The id is alias to retrieve a bean from spring container
  • fully qualified class name of implementation class
applicationCOntext.xml
<beans>
    <bean id="myCoach"
        class="com.deepagar.springdemo.BaseballCoach">         
    </bean>
    
</beans>

Create a Spring Container

  • Spring Container is generally known as ApplicationContext
  • They have Specialized implementations
         a. ClassPathXmlApplicationContext : One for reading xml from classpath
         b. AnnotationConfigApplicationContext 
         c. GenericWebApplicationContext
         d. Others

  • Here creating a context object using name of config file
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml")

Retrieve Beans from Spring Container


// create a spring container
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml")

// retrieve bean from spring container
Coach theCoach = context.getBean("myCoach", Coach.class);

Code Demo Continue

  • Create applicationContext.xml starter file
  • Copy file ApplicationContext.xml
  • Right Click on src folder in Eclipse project and paste ApplicationContext.xml
  • This file has lot of header information for xml namespace, that's what required for Spring to process this file using its grammer and xml schema
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- Define your beans here -->
    
</beans>

  • Let's define the bean now. This completes configuring Spring beans
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- Define your beans here -->
    <bean id="myCoach"
          class="com.deepagar.springdemo.TrackCoach">
    </bean>
</beans>

  • Right Click on package - com.deepagar.springdemo
  • Create New Class - Create HelloSpringApp class and include main method check-box
  • Add the following operations to main method
        a. load the spring configuration file
            ClassPathXmlApplicationContext context = 
new ClassPathXmlApplicationContext("ApplicationContext.xml"); 
            Hover over the error, and import the suggested class

b. retrieve the bean from spring container
c. call methods on the bean
d. close the application context

HelloSpringApp.java
package com.deepagar.springdemo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class HelloSpringApp {

public static void main(String[] args) {
// TODO Auto-generated method stub
// load the spring configuration file
ClassPathXmlApplicationContext context = 
new ClassPathXmlApplicationContext("ApplicationContext.xml"); 
// retrieve the bean from spring container
Coach theCoach = context.getBean("myCoach", Coach.class);
// call methods on the bean
System.out.println(theCoach.getDailyWorkout());
// close the application context
context.close();

}

}



  • Now App is configurable based on configuration file we built
  • Now Easily change the coach for another sport, by simply putting in different coach implementation

Validate the configuration file change

  • Change applicationConext.xml
  • <bean id="myCoach"
  •           class="com.deepagar.springdemo.BaseballCoach">
  • Run HelloApringApp
  • Worked! - Spend 30 minutes on batting

Why do we specify the Coach interface in getBean()?


Question

Why do we specify the Coach interface in getBean()?

For example:

Coach theCoach = context.getBean("myCoach", Coach.class); 



Answer

When we pass the interface to the method, behind the scenes Spring will cast the object for you.

context.getBean("myCoach", Coach.class)  

However, there are some slight differences than normal casting.

From the Spring docs:

Behaves the same as getBean(String), but provides a measure of type safety by throwing a BeanNotOfRequiredTypeException if the bean is not of the required type. This means that ClassCastException can't be thrown on casting the result correctly, as can happen with getBean(String).

Source:  http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/BeanFactory.html#getBean-java.lang.String-java.lang.Class-


Add Logging Messages in Spring 5.1


The Problem

In Spring 5.1, the Spring Development team changed the logging levels internally. As a result, by default you will no longer see the red logging messages at the INFO level. This is different than in the videos.


The Solution

If you would like to configure your app to show similar logging messages as in the video, you can make the following updates listed below. Note, you will not see the EXACT same messages, since the Spring team periodically changes the text of the internal logging messages. However, this should give you some additional logging data.


Overview of the steps

1. Create a bean to configure the parent logger and console handler

2. Configure the bean in the Spring XML config file

Detailed Steps

1. Create a bean to configure the parent logger and console handler

This class will set the parent logger level for the application context. It will also set the logging level for console handler. It sets the logger level to FINE. For more detailed logging info, you can set the logging level to level to FINEST.  You can read more about the logging levels at http://www.vogella.com/tutorials/Logging/article.html

This class also has an init method to handle the actual configuration. The init method is executed after the bean has been created and dependencies injected.

File: MyLoggerConfig.java

package com.luv2code.springdemo;
 
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
 
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
public class MyLoggerConfig {
 
private String rootLoggerLevel;
private String printedLoggerLevel;
public void setRootLoggerLevel(String rootLoggerLevel) {
this.rootLoggerLevel = rootLoggerLevel;
}
 
public void setPrintedLoggerLevel(String printedLoggerLevel) {
this.printedLoggerLevel = printedLoggerLevel;
}
 
public void initLogger() {
 
// parse levels
Level rootLevel = Level.parse(rootLoggerLevel);
Level printedLevel = Level.parse(printedLoggerLevel);
// get logger for app context
Logger applicationContextLogger = Logger.getLogger(AnnotationConfigApplicationContext.class.getName());
 
// get parent logger
Logger loggerParent = applicationContextLogger.getParent();
 
// set root logging level
loggerParent.setLevel(rootLevel);
// set up console handler
ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(printedLevel);
consoleHandler.setFormatter(new SimpleFormatter());
// add handler to the logger
loggerParent.addHandler(consoleHandler);
}
}

2. Configure the bean in the Spring XML config file

In your XML config file, add the following bean entry. Make sure to list this as the first bean so that it is initialized first. Since the bean is initialized first, then you will get all of the logging traffic. If you move it later in the config file after the other beans, then you will miss out on some of the initial logging messages.

File: applicationContext.xml (snippet)

<!-- 
Add a logger config to see logging messages.
- For more detailed logs, set values to "FINEST"
- For info on logging levels, see: http://www.vogella.com/tutorials/Logging/article.html
 -->
    <bean id="myLoggerConfig" class="com.luv2code.springdemo.MyLoggerConfig" init-method="initLogger">
    <property name="rootLoggerLevel" value="FINE" />
    <property name="printedLoggerLevel" value="FINE"/>
    </bean>


Once you make these updates, then you will be able to see additional logging data. :-)
























Comments