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. :-)