Spring
Spring
Spring
Coach.java
package com.sayantan.springdemo;
FootballCoach.java
package com.sayantan.springdemo;
@Override
public String getDailyWorkout() {
return "Spend 30 mins on passing";
}
}
BaseballCoach.java
package com.sayantan.springdemo;
@Override
public String getDailyWorkout() {
return "Spend 30 mins on shot practice";
}
}
MyApp.java
package com.sayantan.springdemo;
The above app is now able to change coach easily but the code is hardcoded. That means if we need
to change the coach, we need to change the source code. Thus, it is not configurable.
Spring Container
Primary functions
o Create and manage objects (Inversion of Control)
o Inject object’s dependencies (Dependency Injection)
XML configuration file (legacy, but most legacy apps still use this)
Java annotations (modern)
Java source code (modern)
NOTE
FAQ: What is a Spring Bean?
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.
---
Source: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-
introduction
---
In the early days, there was a term called "Java Beans". Spring Beans have a similar concept but Spring
Beans do not follow all of the rigorous requirements of Java Beans.
---
In summary, whenever you see "Spring Bean", just think Java object. :-)
ApplicationContext.xml
MySpringApp.java
package com.sayantan.springdemo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
myCoach is the Bean ID we used in ApplicationContext.xml and the Coach.class specifies the interface
we created earlier.
Explanation
Say for example, I'm going to buy a car and this car is
built at the factory on demand. So, there's nothing in
the car lot. You have to actually talk to the factory
and put in a request and they'll build a car for you; So,
at the factory, you have all the different parts for the
car. You have the car chassis, you have the engine,
the tires, the seats, the electronics, the exhaust, and
so on. And the mechanics or the assemblers there or
the technicians, they'll actually assemble the car for
you and then deliver to you the final car. So, you don't have to actually build the car. The cars already
built for you at the factory. So, they actually inject all of the dependencies for the car. So, they inject the
engine, they inject the tires, the seats and so on. So that's basically what you have here with
dependency injection. So, you simply outsource the construction and injection of your object to an
external entity. In this case, that's the car factory.
Injection Types
1. Constructor Injection
2. Setter Injection
Although, there are many types of injection with Spring
b. Class
package com.sayantan.springdemo;
@Override
public String getFortuneService() {
return "Today? your lucky day??";
}
//create constructor
public BaseballCoach(Fortune theFortuneService) {
fortuneService = theFortuneService;
}
public BaseballCoach() {
@Override
public String getDailyWorkout() {
return "Spend 30 mins on shot practice";
}
@Override
public String getFortuneService() {
return fortuneService.getFortuneService();
}
EXPLANTAION
At first, we create the dependency interface and define the method in a class.
In the next step, we create a private instance of the interface in the class where we want to inject
the dependency. After creating the instance, we call for the constructor to load the dependency
into the earlier instance of the interface by passing it via an argument (here, theFortuneService).
NOTE –
If you want to add another dependency in the same
project, make sure you call one constructor and define
it for the same class. DO NOT DEFINE SEPARATE
CONSTRUCTORS FOR THE SAME CLASS.
//create pvt field for dependency
private Fortune fortuneService;
private Salary sal;
//create constructor
public FootballCoach(Fortune theFortuneService, Salary theSalaryService) {
fortuneService = theFortuneService;
sal = theSalaryService;
}
public FootballCoach() {
Setter Injection
Inject dependencies by calling setter methods on our class.
Development Process
Before creating the setter methods, we need to create the private instances of the dependencies
Fortune Service and Salary Services (as a whole). Also, we need to call the no-arg constructor so just that
we know we are inside the constructor to know the behind scenes (However, this part is completely
optional).
After the above methods, we need to create setter methods. And then we will call the setter methods by
passing a parameter in order to load the setter method. After the loading has been done, we will
actually call the method through it.
package com.sayantan.springdemo;
//create a constructor
// public CricketCoach() {
// System.out.println("CricketCoach: inside no-arg constructor");
// }
//
// Create setter methods
@Override
public String getDailyWorkout() {
// TODO Auto-generated method stub
return "Practice Sprinting";
}
@Override
public String getFortuneService() {
// TODO Auto-generated method stub
return fortuneService.getFortuneService();
}
@Override
public String getSalaryService() {
// TODO Auto-generated method stub
return sal.getSalaryService();
}
}
2. Configure the dependency injection in spring config file
<bean id="myFortune"
class="com.sayantan.springdemo.HappyFortuneService"></bean>
<bean id="mySalary" class="com.sayantan.springdemo.GetSalary"></bean>
<bean id="myCricketCoach"
class="com.sayantan.springdemo.CricketCoach">
</bean>
</beans>
The ref is used in the property is used for referencing objects or dependencies.
Creating setter methods ( create private fields and then call setter methods)
package com.sayantan.springdemo;
//create a constructor
// public CricketCoach() {
// System.out.println("CricketCoach: inside no-arg constructor");
// }
//
// Create setter methods
//calling the getter methods to print some lines to test our app
public String getEmailAddress() {
return emailAddress;
}
@Override
public String getDailyWorkout() {
// TODO Auto-generated method stub
return "Practice Sprinting";
}
@Override
public String getFortuneService() {
// TODO Auto-generated method stub
return fortuneService.getFortuneService();
}
@Override
public String getSalaryService() {
// TODO Auto-generated method stub
return sal.getSalaryService();
}
}
Configuring the injection in config file.
<?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">
<bean id="myFortune"
class="com.sayantan.springdemo.HappyFortuneService"></bean>
<bean id="mySalary" class="com.sayantan.springdemo.GetSalary"></bean>
<bean id="myCricketCoach"
class="com.sayantan.springdemo.CricketCoach">
</bean>
</beans>
We injected literal values but those literal values were hard coded. What we want to do is to inject
those values from an external properties file.
Development process
<bean id="myFortune"
class="com.sayantan.springdemo.HappyFortuneService"></bean>
<bean id="mySalary" class="com.sayantan.springdemo.GetSalary"></bean>
<bean id="myCricketCoach"
class="com.sayantan.springdemo.CricketCoach">
</bean>
</beans>
Bean Scope
Scope refers to the lifecycle of a bean.
Duration of a bean.
No of instances created
How the bean is shared?
What is Singleton?
Scope Description
Singleton Create a single shared instance of the bean.
Default scope
prototype Creates a new bean instance for each container
request
request Scoped to an HTTP web request. Only used for
web apps
session Scoped to an HTTP web session. Only used for
web apps.
global-session Scoped to a global HTTP web session. Only used
for web apps.
Code to check
package com.sayantan.springdemo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
//print result
System.out.println("Pointing to the same object: "+result);
System.out.println("\n Memory location for theCoach: "+theCoach);
System.out.println("\n Memory location for alphaCoach: "+alphaCoach);
//close context
context.close();
}
<?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">
</beans>
BEAN LIFECYCLE
Development process
Access modifier
The method can have any access modifier (public, protected, private)
Return type
The method can have any return type. However, "void' is most commonly used. If you give a return type
just note that you will not be able to capture the return value. As a result, "void" is commonly used.
Method name
The method can have any method name.
Arguments
The method cannot accept any arguments. The method should be no-arg.
package com.sayantan.springdemo;
//create constructor
public BaseballCoach(Fortune theFortuneService, Salary theSalaryService)
{
fortuneService = theFortuneService;
sal = theSalaryService;
}
public BaseballCoach() {
@Override
public String getDailyWorkout() {
return "Spend 30 mins on shot practice";
}
@Override
public String getFortuneService() {
return fortuneService.getFortuneService();
}
@Override
public String getSalaryService() {
return sal.getSalaryService();
}
}
beanLifeCycle-applicationContext.xml
<?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">
</beans>
beanLifeCycleDemoApp.java
package com.sayantan.springdemo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
System.out.println(theCoach.getDailyWorkout());
//close context
context.close();
}
Java Annotations
Special labels/markers added to Java classes
Provide meta-data about the class
Processed at compile time or run time for special processing
Development Process.
applicationContext.xml
<context:component-scan base-package="com.sayantan"/>
</beans>
Coach.java
package com.sayantan;
TennisCoach.java
Syntax: @component(“bean-id”)
package com.sayantan;
import org.springframework.stereotype.Component;
@Component(“thatTennisCoach”)
public class TennisCoach implements Coach {
@Override
public String getDailyWorkout() {
return "Practice your backhand volley";
}
import org.springframework.context.support.ClassPathXmlApplicationContext;
}
Spring also supports Default Bean IDs
Code Example
@Component
public class TrackCoach implements Coach {
Autowiring Example
Constructor Injection
Setter Injection
Field Injection
When using autowiring, what if there are multiple FortuneService implementations? Like in the
image below?
Spring has special support to handle this case. Use the @Qualifier annotation.
1. Define the dependency interface and class
Example: HappyFortuneService meets the requirement and the dependency is being injected into the
TennisCoach automatically by using autowiring.
FortuneService.java
package com.sayantan;
HappyFortuneService.java
package com.sayantan;
import org.springframework.stereotype.Component;
@Component
public class HappyFortuneService implements FortuneService {
@Override
public String getFortune() {
return "Today is your lucky day";
}
So, the component searches for a Fortune Service class or interface that matches the type and once it
founds its match, it will automatically wire the components.
package com.sayantan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("thatTennisCoach")
@Autowired
fortuneService = theFortuneService;
@Override
@Override
return fortuneService.getFortune();
Autowired (OPTIONAL)
package com.sayantan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("thatTennisCoach")
public TennisCoach() {
}
//Define a setter method
@Autowired
fortuneService = theFortuneService;
@Autowired
salaryService = theSalaryService;
@Override
@Override
return fortuneService.getFortune();
@Override
return salaryService.getSalary();
}
Method Injection
Inject dependencies by calling ANY method on our class by simply giving: @Autowired
FIELD INJECTION
Inject dependencies by setting field values on our class directly (even private
fields) [Accomplished by using JAVA Reflection]
Development Process
Configure the dependency injection with Autowired Annotation
o Applied directly to the field
o No need for setter methods
Note -
Reflection in Java
Reflection is an API which is used to examine or modify the behavior of methods, classes, interfaces
at runtime.
The required classes for reflection are provided under java.lang.reflect package.
Reflection gives us information about the class to which an object belongs and also the
methods of that class which can be executed by using the object.
Through reflection we can invoke methods at runtime irrespective of the access specifier
used with them.
Link: https://www.geeksforgeeks.org/reflection-in-java/
package com.sayantan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("thatTennisCoach")
@Autowired
public TennisCoach() {
@Autowired
fortuneService = theFortuneService;
@Override
@Override
@Override
return salaryService.getSalary();
There arose a problem, when there were multiple implementations of an interface. How would Spring
understand which one to choose? And there comes an error caused by
“NoUniqueBeanDefinitionFound”.
Constructor Injection
Setter Injection methods
Field Injection.
Use the annotation @Qualifier with the desired implementation with a bean ID (preferably
defaultBeanID).
import org.springframework.stereotype.Component;
@Component
public class HappyFortuneService implements FortuneService {
@Override
public String getFortune() {
return "Today is your lucky day";
}
DatabaseFortuneService.java
package com.sayantan;
import org.springframework.stereotype.Component;
@Component
public class DatabaseFortuneService implements FortuneService {
@Override
public String getFortune() {
// TODO Auto-generated method stub
return null;
}
RESTFortuneService.java
package com.sayantan;
import org.springframework.stereotype.Component;
@Component
public class RESTFortuneService implements FortuneService {
@Override
public String getFortune() {
// TODO Auto-generated method stub
return null;
}
RandomFortuneService.java
package com.sayantan;
import org.springframework.stereotype.Component;
@Component
public class RandomFortuneService implements FortuneService {
@Override
public String getFortune() {
// TODO Auto-generated method stub
return null;
}
The class file from where spring will choose which one to pick from the multiple implementations. So,
spring picks up the one with “@Qualifier” annotation.
package com.sayantan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component("thatTennisCoach")
@Autowired
@Autowired
@Override
@Override
return fortuneService.getFortune();
@Override
return salaryService.getSalary();
}
Annotations - Default Bean Names - The Special Case
In general, when using Annotations, for the default bean name, Spring uses the following rule.
If the annotation's value doesn't indicate a bean name, an appropriate name will be built based on the
short name of the class (with the first letter lower-cased).
For example:
However, for the special case of when BOTH the first and second characters of the class name are
upper case, then the name is NOT converted.
Behind the scenes, Spring uses the Java Beans Introspector to generate the default bean name. Here's a
screenshot of the documentation for the key method.
https://docs.oracle.com/javase/8/docs/api/java/beans/Introspector.html#decapitalize(java.lang.String)
The syntax is much different from other examples and not exactly intuitive. Consider this the "deep end
of the pool" when it comes to Spring configuration LOL :-)
You have to place the @Qualifier annotation inside of the constructor arguments.
Here's an example from our classroom example. I updated it to make use of constructor injection, with
@Autowired and @Qualifier. Make note of the code in bold below:
package com.luv2code.springdemo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class TennisCoach implements Coach {
private FortuneService fortuneService;
// define a default constructor
public TennisCoach() {
System.out.println(">> TennisCoach: inside default constructor");
}
@Autowired
public TennisCoach(@Qualifier("randomFortuneService") FortuneService theFortuneService) {
System.out.println(">> TennisCoach: inside constructor using @autowired and @qualifier");
fortuneService = theFortuneService;
}
@Override
public String getDailyWorkout() {
return "Practice your backhand volley";
}
@Override
public String getDailyFortune() {
return fortuneService.getFortune();
}
}
For detailed documentation on using @Qualified with Constructors, see this link in the Spring Reference
Manual
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-autowired-
annotation-qualifiers
_____________________________________________________________________________________
Answer:
This solution will show you how inject values from a properties file using annotatons. The values will no
longer be hard coded in the Java code.
1. Create a properties file to hold your properties. It will be a name value pair.
1. foo.email=myeasycoach@luv2code.com
2. foo.team=Silly Java Coders
Note the location of the properties file is very important. It must be stored in src/sport.properties
File: applicationContext.xml
Use the “@Scope” annotation to change the scope. Place it with the component. Default scope is always
singleton.
Development Process
Access modifier
The method can have any access modifier (public, protected, private)
Return type
The method can have any return type. However, "void' is most commonly used. If you give a return type
just note that you will not be able to capture the return value. As a result, "void" is commonly used.
Method name
The method can have any method name.
Arguments
The method cannot accept any arguments. The method should be no-arg.
These are the steps to resolve it. Come back to the lecture if you hit the error.
Error
When using Java 9 and higher, javax.annotation has been removed from its default classpath. That's why
we Eclipse can't find it.
Solution
10. Eclipse will perform a rebuild of your project and it will resolve the related build errors.
Development Process
@Component
public class TennisCoach implements Coach {
@Autowired
@Qualifier("randomFortuneService")
private FortuneService fortuneService;
@Autowired
private SalaryService salaryService;
public TennisCoach() {
System.out.println(">> TennisCoach : Inside default constructor -
optional step");
}
@PostConstruct
public void callInitMethod() {
System.out.println(">>TennisCoach: Inside of callInitMethod() ");
}
@Override
public String getDailyWorkout() {
return "Practice your backhand volley";
}
@Override
public String getDailyFortune() {
// TODO Auto-generated method stub
return fortuneService.getFortune();
}
@Override
public String getSalaryService() {
return salaryService.getSalary();
}
}
Run the driver app class to generate the output.
For "prototype" scoped beans, Spring does not call the @PreDestroy method.
Here is the answer from the Spring reference manual. Section 1.5.2
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-
scopes-prototype
In contrast to the other scopes, Spring does not manage the complete lifecycle of a
prototype bean: the container instantiates, configures, and otherwise assembles a
prototype object, and hands it to the client, with no further record of that prototype
instance.
Thus, although initialization lifecycle callback methods are called on all objects regardless of scope, in
the case of prototypes, configured destruction lifecycle callbacks are not called. The client code must
clean up prototype-scoped objects and release expensive resources that the prototype bean(s) are
holding.
To get the Spring container to release resources held by prototype-scoped beans, try using a custom
bean post-processor, which holds a reference to beans that need to be cleaned up.
QUESTION: How can I create code to call the destroy method on prototype scope beans
ANSWER:
You can destroy prototype beans but custom coding is required. This examples shows how to destroy
prototype scoped beans.
1. Create a custom bean processor. This bean processor will keep track of prototype scoped beans.
During shutdown it will call the destroy() method on the prototype scoped beans.
2. The prototype scoped beans MUST implement the DisposableBean interface. This interface defines a
"destroy()" method. This method should be used instead of the @PreDestroy annotation.
Development Process
1,2: Creating java class and annotating it as @Configuration; Adding Component Scanning support
SportConfig.java
package com.sayantan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.sayantan")
public class SportConfig {
Driver Class
package com.sayantan;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
1&2
package com.sayantan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
//@ComponentScan("com.sayantan")
@PropertySource("classpath:sport.properties")
@Bean
@Bean
During All Java Configuration, how does the @Bean annotation work in the background?
Answer - This is an advanced concept. But I'll walk through the code line-by-line.
1. @Bean
4. return mySwimCoach;
5. }
At a high-level, Spring creates a bean component manually. By default the scope is singleton. So any
request for a "swimCoach" bean, will get the same instance of the bean since singleton is the default
scope.
1. @Bean
The @Bean annotation tells Spring that we are creating a bean component manually. We didn't specify a
scope so the default scope is singleton.
This specifies that the bean will bean id of "swimCoach". The method name determines the bean id. The
return type is the Coach interface. This is useful for dependency injection. This can help Spring find any
dependencies that implement the Coach interface.
The @Bean annotation will intercept any requests for "swimCoach" bean. Since we didn't specify a
scope, the bean scope is singleton. As a result, it will give the same instance of the bean for any
requests.
1. return mySwimCoach;
Now let's step back and look at the method in it's entirety.
1. @Bean
4. return mySwimCoach;
5. }
It is important to note that this method has the @Bean annotation. The annotation will intercept ALL
calls to the method "swimCoach()". Since no scope is specified the @Bean annotation uses singleton
scope. Behind the scenes, during the @Bean interception, it will check in memory of the Spring
container (applicationContext) and see if this given bean has already been created.
If this is the first time the bean has been created then it will execute the method as normal. It will also
register the bean in the application context. So that is knows that the bean has already been created
before. Effectively setting a flag.
The next time this method is called, the @Bean annotation will check in memory of the Spring container
(applicationContext) and see if this given bean has already been created. Since the bean has already
been created (previous paragraph) then it will immediately return the instance from memory. It will not
execute the code inside of the method. Hence this is a singleton bean.
2. return mySwimCoach;
is not executed for subsequent requests to the method public Coach swimCoach() . This code is only
executed once during the initial bean creation since it is singleton scope.
That explains how @Bean annotation works for the swimCoach example.
>> Please explain in detail whats happening behind the scene for this statement.
2. @Bean
5. }
6.
8. @Bean
11.
13. }
The code
2. @Bean
5. }
In the code above, we define a bean for the sad fortune service. Since the bean scope is not specified, it
defaults to singleton.
Any calls for sadFortuneService, the @Bean annotation intercepts the call and checks to see if an
instance has been created. First time through, no instance is created so the code executes as desired.
For subsequent calls, the singleton has been created so @Bean will immediately return with the
singleton instance.
This is effectively dependency injection. It is accomplished using all Java configuration (no xml).
This concludes the line-by-line discussion of the source code. All of the behind the scenes work.
foo.email=abc@xyz.com
foo.team=Barcelona
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
//@ComponentScan("com.sayantan")
@PropertySource("classpath:sport.properties")
@Bean
@Bean
import org.springframework.beans.factory.annotation.Value;
@Value("${foo.email}")
private String email;
@Value("${foo.team}")
private String team;
…………..
.
.
.
.
.
.
.
.
.
.
.
}
SPRING MVC
Basically, we have an incoming request coming from the browser, it'll encounter the Spring MVC front
controller. This person will actually delegate the request off to a controller code. This controller code is
code that you create that contains your business logic. You basically create a model, and you send the
model back to the front controller, and then the front controller, will pass that model over to your view
template. So, your view template is basically like a html page, or a JSP page that will take that data, and
then render a response to the browser. So that's kind of the big picture of the MVC framework.
The MVC pattern results in separating the different aspects of the application (input logic, business logic,
and UI logic), while providing a loose coupling between these elements.
The Model encapsulates the application data and in general they will consist of POJO.
The View is responsible for rendering the model data and in general it generates HTML output
that the client's browser can interpret.
The Controller is responsible for processing user requests and building an appropriate model
and passes it to the view for rendering.
Controller
So, when the front controller has a request, it delegates the request to the controller. The
controller is the code that you will actually create. Basically, in this controller, this contains your
business logic. So, this is where you'll handle the request where you'll maybe read some form
data then you'll take this data and store it or retrieve it. You may store it into a database or
retrieve information from a web service. Basically, once you have your data and you're using it,
then you can take that data and place it into the model. So, the model is just a container for
your data and then you pass it back to the appropriate view template.
Created by developer
Model
The model contains your data. So, when your controller goes off and performs an operation to retrieve
data from a backend system, like a database or web service, or a spring bean, you can take that data and
place it into the model.so the model again is your container, like your suitcase or your luggage, for
shipping data between various parts of your spring mvc application.so that model data will actually get
passed over to the view template and they can actually handle that for displaying the data.
Contains data
View Template
The most common view template that we'll use is JSP and JSTL. Spring MVC is very flexible. There are
many different view template types. This model data comes over to your view template and then your
JSP page can read that model data and display it. So, say for example, we have a list of students, or list of
products, then JSP page can create a table to display that product list or that student list. Or, say for
example, somebody is signing up for an airline flight, or is signing up for a class, then your view
template, or your page can give them a confirmation, hey, you're registered for the class, here's your
confirmation number. So that's the idea of the view template. It's basically a JSP page that will provide
data to the user.
Details: www.luv2code.com/spring-mvc-views
Development Process
Basically, this annotation tells that it is a controller and since it is inherited from Component, so
when the Spring will do its component scanning, it will also pick up Controllers on its way.
package com.luv2code.springdemo.mvc;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HomeController { 1
@RequestMapping("/") 3
public String showPage() { 2
return "main-menu"; 4
}
This is however not the full name of the “main-menu” view page. it's not the complete name of the
page because we make use of information from the config file while they'll actually add a prefix and
they'll actually add the suffix. So, it'll be WEB-INF/view/main-menu.jsp. So, Spring will do this
automatically for you in the background.
<!DOCTYPE HTML>
<html>
<body>
<h2>Welcome to CGR</h2>
</body>
</html>
Development Process
1,2,3
package com.luv2code.springdemo.mvc;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/showForm")
return "helloworld-form";
@RequestMapping("/processForm")
return "helloworld";
<form action="processForm">
<input type="text" name="studentName" placeholder="What's your name?" />
<input type="submit" value="Submit" />
</form>
</body>
</html>
Development Process
1. Create a method to process the data or use existing method to process the data
2. Add the data to the model.
1,2
package com.luv2code.springdemo.mvc;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/showForm")
return "helloworld-form";
@RequestMapping("/processForm")
return "helloworld";
@RequestMapping("/processFormVersionTwo")
return "helloworld";
}
}
message NAME
result VALUE
View Page
<br><br>
Answer
Here are the steps on how to access static resources in a Spring MVC. For example, you can use this to
access images, css, JavaScript files etc.
Any static resource is processed as a URL Mapping in Spring MVC. You can configure references to static
resources in the spring-mvc-demo-servlet.xml.
I chose to put everything in the "resources" directory. But you can use any name for "resources", such as
"assets", "foobar" etc. Also, you can give any name that you want for the subdirectories under
"resources".
---
Step 1: Add the following entry to your Spring MVC configuration file: spring-mvc-demo-servlet.xml
You can place this entry anywhere in your Spring MVC config file.
Step 2: Now in your view pages, you can access the static files using this syntax:
<img src="${pageContext.request.contextPath}/resources/images/spring-logo.png">
You need to use the JSP expression ${pageContext.request.contextPath} to access the correct root
directory for your web application.
---
Here's a full example that reads CSS, JavaScript and images.
<head>
href="${pageContext.request.contextPath}/resources/css/my-test.css">
</head>
<body>
<br><br>
<br><br>
</body>
</html>
---
- https://gist.github.com/darbyluv2code/9a09543a226baeedc04b9a5037ca52ec
Deploying To Tomcat using WAR files
Bonus: Deploying your App to Tomcat as a Web Application Archive (WAR) file
When you deploy your Java web apps, you can make use of a Web Application Archive (WAR) file.
The Web Application Archive (WAR) file is a compressed version of your web application. It uses the zip
file format but the file has the .war extension.
If you are using Eclipse, then the best way to visualize it is think of your "WebContent" directory being
compressed as a zip file with the .war extension.
This includes all of your web pages, images, css etc. It also includes the WEB-INF directory which
includes your classes in WEB-INF/classes and supporting JAR files in WEB-INF/lib.
The WAR file format is part of the Java EE / Servlet specification. As a result, all Java EE servers support
this format (ie jboss, weblogic, websphere, glassfish and tomcat).
Below, I provide steps on how to create a WAR file in Eclipse. I also show how to deploy the WAR file on
Tomcat.
---
Give it about 10-15 seconds to make the deployment. You'll know the deployment is over because you'll
see a new folder created in webapps ... with your WAR file name.
7. Visit your new app. If your war file was: mycoolapp.war then you can access it with:
http://localhost:8080/mycoolapp/
Binding Request Params
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/showForm")
return "helloworld-form";
@RequestMapping("/processFormVersionThree")
theName = theName.toUpperCase();
model.addAttribute("message", result);
return "helloworld";
}
Controller Level Request Mapping
All of the method mappings are relative to the Controller Level Request Mapping. Also a nice way to
group the request mappings together, and a great way to resolve any conflicts that we may have with
other request mappings.
NewController.java
package com.luv2code.springdemo.mvc;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class NewController {
@RequestMapping("/showForm")
public String displayTheForm() {
return "silly";
}
}
HelloWorldController.java
package com.luv2code.springdemo.mvc;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/showForm")
return "helloworld-form";
So, both of the Controllers got same request mapping but different methods. So, there arises an
ambiguity while running it on the server. To resolve this, we need Controller Level Request Mapping.
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/hello")
@RequestMapping("/showForm")
return "helloworld-form";
So showForm is now relative to hello. And we need to update the links in the view pages too.
How /hello is getting appended to the jsp file action for "processform"?
Answer
You can use "processform" because it is a relative path to the controller "/hello" request mapping. Here
is how it works.
1. When you wish to view the form, the HTML link points to "hello/showform". This calls the controller
and it displays the form.
3. The HTML form uses "processform" for the form action. Notice that it does not have a forward slash,
as a result, this will be relative to the current browser URL. Since the current browser URL is
Http://localhost:8080/spring-mvc-demo/hello
Http://localhost:8080/spring-mvc-demo/hello/processform
The part in bold with map to the controller with top-level request mapping "/hello" and then map to
request mapping in that class "/processform"
The key here is relative path of showing the form and then submitting to relative path.
Spring MVC Form Tags
Spring MVC Form Tags are the building blocks for a web page.
Form Tags are configurable and reusable for a web page.
Data Binding
Web Structure
<html>
…..regular HTML
……More HTML
</html>
Showing Form
public Student() {
}
2,4 Create Student Controller Class and Processing Code
package com.luv2code.springdemo.mvc;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/student")
@RequestMapping("/showForm")
theModel.addAttribute("student",theStudent);
return "student-form";
@RequestMapping("/processForm")
return "student-confirmation";
}
2 Before showing the form, we added a parameter “theModel” for the Model. A student object
“theStudent” (containing the first name and last name from the Student class) is being created and
added to the model attribute. “student” is the name of the Model attribute and the value of the Model
attribute “student” is the object i.e. “theStudent”. And at last, the student-form is being returned.
4 We add the method processForm() and bind the model attribute “student” (same as the
modelAttribute in the JSP form) to the instance theStudent of the class Student.
</form:form>
</body>
</html>
The highlighted taglib uri is added for the Spring form tags. We used the <form:form></form:form> tag
with the action=”processForm” and modelAttribute=”student”. The model attribute is very much
important for our controller. It should same as the Model Attribute in our student controller.
When the form is loaded, getter methods are being called i.e. the Spring MVC will call
student.getFirstName() and student.getLastName(). A similar thing happens when we submit the form.
Spring MVC will call the student.setFirstName() and student.setLastName();
5 We are making the confirmation page that the processForm leads to. The model attribute helps to
retrieve the firstName and the lastName.
Answer:
This solution will show you how to place the country options in a properties file. The values will no
longer be hard coded in the Java code.
1. Create a properties file to hold the countries. It will be a name value pair. Country code is name.
Country name is the value.
1. BR=Brazil
2. FR=France
3. CO=Colombia
4. IN=India
Note the location of the properties file is very important. It must be stored in WEB-
INF/countries.properties
We are going to use a new set of Spring tags for <util>. As a result, you need to update the header
information in the Spring config file.
File: spring-mvc-dmo-servlet.xml
Remove the previous header and add this.
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:context="http://www.springframework.org/schema/context"
4. xmlns:mvc="http://www.springframework.org/schema/mvc"
5. xmlns:util="http://www.springframework.org/schema/util"
6. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
7. xsi:schemaLocation="
8. http://www.springframework.org/schema/beans
9. http://www.springframework.org/schema/beans/spring-beans.xsd
10. http://www.springframework.org/schema/context
11. http://www.springframework.org/schema/context/spring-context.xsd
12. http://www.springframework.org/schema/mvc
13. http://www.springframework.org/schema/mvc/spring-mvc.xsd
14. http://www.springframework.org/schema/util
15. http://www.springframework.org/schema/util/spring-util.xsd">
3. Load the country options properties file in the Spring config file. Bean id: countryOptions
File: spring-mvc-dmo-servlet.xml
1. import java.util.Map;
4.2 Inject the properties values into your Spring Controller: StudentController.java
1. @Value("#{countryOptions}")
2. private Map<String, String> countryOptions;
5. Add the country options to the Spring MVC model. Attribute name: theCountryOptions
1. @RequestMapping("/showForm")
3.
6.
8. theModel.addAttribute("student", theStudent);
9.
12.
14. }
6. Update the JSP page, student-form.jsp, to use the new model attribute for the drop-down list:
theCountryOptions
1. <form:select path="country">
3. </form:select>
---
- http://www.luv2code.com/downloads/spring-hibernate/spring-props-mvc-demo.zip
How to populate radiobuttons with items from Java class like we did with selectlist?
You can follow a similar approach that we used for the drop-down list.
In java code
Need to add support when user selects multiple options
Array of Strings
Add appropriate get/set methods
Use array field in class file if you want to print elements in list format in the JSP Page. The code
works fine without the String.
Required fields
Valid numbers in a range
Valid format (postal code)
Custom Business Rule.
required
validate length
validate numbers
validate with regular expressions
custom validation
Validation Annotations
Annotation Description
@NotNull Checks that the annotated value is not null
@Min Must be a number >= value
@Max Must be a number <= value
@Size Size must match the given size
@Pattern Must match a regular expression pattern
@Future/@Past Date must be in future or past of a given date
Others…. ….
RoadMap
Development Process
When performing Spring MVC validation, the location of the BindingResult parameter is very important.
In the method signature, the BindingResult parameter must appear immediately after the model
attribute.
If you place it in any other location, Spring MVC validation will not work as desired. In fact, your
validation rules will be ignored.
1. @RequestMapping("/processForm")
2. public String processForm(
3. @Valid @ModelAttribute("customer") Customer theCustomer,
4. BindingResult theBindingResult) {
5. ...
6. }
@RequestMapping handler methods have a flexible signature and can choose from a range of supported
controller method arguments and return values.
...
The Errors or BindingResult parameters have to follow the model object that is being
bound immediately ...
Source: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-
methods
package com.luv2code.springdemo.mvc;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Controller
@RequestMapping("/customer")
public class CustomerController {
@RequestMapping("/showForm")
public String showModel(Model theModel) {
theModel.addAttribute("customer",new Customer());
return "CustomerForm";
}
@RequestMapping("/processForm")
public String processForm(@Valid @ModelAttribute("customer") Customer theCus-
tomer,
BindingResult theBindingResult) {
if(theBindingResult.hasErrors()) {
return "CustomerForm";
}
else {
return "CustomerConfirmation";
}
}
}
@InitBinder
Reminder: @InitBinder will basically pre-process all web requests coming to the controller.
import javax.validation.Valid;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/customer")
public class CustomerController {
@InitBinder
public void initBinder(WebDataBinder dataBinder) {
StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true);
dataBinder.registerCustomEditor(String.class, stringTrimmerEditor);
}
@RequestMapping("/showForm")
public String showModel(Model theModel) {
theModel.addAttribute("customer",new Customer());
return "CustomerForm";
}
@RequestMapping("/processForm")
public String processForm(@Valid @ModelAttribute("customer") Customer
theCustomer,
BindingResult theBindingResult) {
if(theBindingResult.hasErrors()) {
return "CustomerForm";
}
else {
return "CustomerConfirmation";
}
}
}
Validate a Number Range
Input field where user can enter a range from 0 to 10 (as desired)
Development Process
@NotNull(message="is required")
@Size(min=1, message="is required")
private String lastName;
return firstName;
}
import javax.validation.Valid;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/customer")
public class CustomerController {
@InitBinder
dataBinder.registerCustomEditor(String.class, stringTrimmerEditor);
@RequestMapping("/showForm")
public String showModel(Model theModel) {
theModel.addAttribute("customer",new Customer());
return "CustomerForm";
}
@RequestMapping("/processForm")
public String processForm(@Valid @ModelAttribute("customer") Customer theCustomer,
BindingResult theBindingResult) {
if(theBindingResult.hasErrors()) {
return "CustomerForm";
}
else {
return "CustomerConfirmation";
}
}
}
A Regular Expression is a sequence of characters that constructs a search pattern. When you search
for data in a text, we can use this search pattern to describe what we are looking for.
^[a-z0-9_-]{3-15}$
^ Start of the line
The Java Regex is an API which is used to define a patten for searching or manipulating Strings. It is
widely used to define the constraint on Strings such as password and email validation.
Matcher Class
A compiled version of regular expression which is used to define the pattern of a regex engine.
Various methods
Static Pattern compile(String regex) It compiles the given regex and returns the
instance of a pattern
Matcher matcher(charSequence input) Used to create a matcher that matches the given
input with the pattern
Static boolean matches(String regex) Used to split the given String around matches of a
given pattern
String pattern() Helps to return the regex pattern
Int end() Returns the ending index
Character Class
Regex Quantifiers
Regex Metacharacters
Links –
1. https://www.javatpoint.com/java-regex
2. https://www.youtube.com/watch?v=f0lZbeueVzU&t=523s
Development Process
Problem: If we left the field “Free Passes” empty and submitted it even after adding
@NotNull(message="is required")
There will be an error showing that “failed to convert…” since the blank spaces are considered as a null
string
Solution:
Change the primitive data type to Wrapper class data type i.e. int to Integer in the class file.
Development Process
1.a
messages.properties
typeMismatch.customer.freePasses=Invalid number
2.a
<?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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- Step 4: Add support for conversion, formatting and validation support -->
<mvc:annotation-driven/>
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
</bean>
Explanation : https://www.udemy.com/course/spring-hibernate-
tutorial/learn/lecture/6846302#content
Question:
I am getting the following error when i submit the form with an empty value for customer "freePasses". I
am using @NotNull on the field "freePasses". It is not throwing "is required" after validation after
submit.
Also, how do I handle validation if the user enters String input for the integer field?
-----
Answer:
Great question!
The root cause is the freePasses field is using a primitive type: int. In order to check for null we must use
the appropriate wrapper class: Integer.
@NotNull(message="is required")
@Min(value=0, message="must be greater than or equal to zero")
@Max(value=10, message="must be less than or equal to 10")
private Integer freePasses;
=====
1. package com.luv2code.springdemo.mvc;
2. import javax.validation.constraints.Max;
3. import javax.validation.constraints.Min;
4. import javax.validation.constraints.NotNull;
5. import javax.validation.constraints.Pattern;
6. import javax.validation.constraints.Size;
7.
11.
15.
20.
23.
24.
27. }
28.
31. }
32.
33. public String getFirstName() {
35. }
36.
39. }
40.
43. }
44.
47. }
48.
51. }
52.
55. }
56.
57. }
====
If the user enters String input such as "abcde" for the Free Passes integer field, we'd like to give a
descriptive error message.
typeMismatch.customer.freePasses=Invalid number
5. Save file
---
This file contains key/value pairs for the error message type
typeMismatch.customer.freePasses=Invalid number
The format of the error key is: code + "." + object name + "." + field
To find out the given error code, in your Spring controller, you can log the details of the binding result
- http://docs.spring.io/spring/docs/current/javadoc-
api/org/springframework/validation/DefaultMessageCodesResolver.html
---
1. <bean id="messageSource"
2. class="org.springframework.context.support.ResourceBundleMessageSource">
3.
4. <property name="basenames" value="resources/messages" />
5.
6. </bean>
8. Run your app and add bad data for the "Free Passes" field. You will see the error message from our
properties file.
solution-code-spring-mvc-validation-handle-strings-for-integer-fields.zip
Spring MVC Form Validation – Creating CUSTOM Validation Rules
Custom Validation
So far, we have used predefined validation rules: @Min, @Max and others. For custom validation, we
will create a CUSTOM JAVA ANNOTATION such as @CourseCode
Development Process
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
@Constraint(validatedBy = CourseCodeConstraintValidator.class) //
CourseCodeConstraintValidator.class --> Helper Class that contains business rules/
validation logic
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
@Override
public void initialize(CourseCode theCourseCode) {
coursePrefix = theCourseCode.value(); //.value accesses the attribute
value for the given annotation
}
@Override
public boolean isValid(String theCode, // String theCode --> HTML form data
entered by the user;
ConstraintValidatorContext theConstraintValidatorContext)
{ //ConstraintValidatorContext theConstraintValidatorContext --> additional error
messages can be placed here.
boolean result = theCode.startsWith(coursePrefix);
return result;
}
import com.luv2code.springdemo.mvc.validation.CourseCode;
….
…..
// @CourseCode //not using attribute value here, passing the defaults only.
@CourseCode(value = "SSG", message = "Start with SSG")
private String courseCode; ….
….
…..
public String getCourseCode() {
return courseCode;
}
Question:
Is it possible to integrate multiple validation string in one annotation? For example, validate against both
LUV and TOPS.
Answer:
Yes, you can do this. In your validation, you will make use of an array of strings.
---
Detailed Steps
Note the use of square brackets for the array of Strings. Also, the initialized value uses curley-braces for
array data.
Update the isValid(...) method to loop through the course prefixes. In the loop, check to see if the code
matches any of the course prefixes.
1. @Override
3. ConstraintValidatorContext theConstraintValidatorContext) {
5.
6. if (theCode != null) {
7.
8. //
10. //
12. //
15.
17. if (result) {
18. break;
19. }
20. }
21. }
22. else {
24. }
25.
27. }
---
https://gist.github.com/darbyluv2code/0275ddb6e70e085a10fd464e36a42739
---
That's it. This provides a solution to integrate multiple validation string in one annotation. In this
example, the code validates against both LUV and TOPS.