Tuesday, 8 November 2016

5. Hibernate Spring DataSource JNDI with Tomcat

In all previous Hibernate tutorials, we were using 'DriverManagerDataSource' provided by Spring to connect to database. However this approach is not very convenient for obvious reasons like database schema password needs to be configured in application itself which is visible to everyone who has access to application deployable file. Also if password changes, application needs to be redeployed.

So other approach which is recommended and widely used is to use 'DataSource' which is configured on web container/application server and connect to that database using JNDI name of datasource.

We will use tomcat to configure datasource and then using Spring MVC application integrated with Hibernate to connect to that datasource to fetch records.

Below will be final project structure once finished.


Let's first configure datasource in tomcat.
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<Resource name="jdbc/rmsystem" 
      auth="Container" 
      type="javax.sql.DataSource" 
      driverClassName="org.postgresql.Driver" 
      url="jdbc:postgresql://localhost:5432/rmsystem" 
      username="postgres" 
      password="password"      
      maxTotal="10" 
      maxIdle="4" 
      minIdle="2" 
      maxWaitMillis="10000"
      validationQuery="SELECT 1"/>

Add this element inside the root element  in a context.xml file. This file is located inside $CATALINA_BASE/conf directory: this is the preferred place because the JNDI DataSource will be available to all web applications and it’s independent of any applications.

Once done, configure this datasource in Spring bean configuration file like below,
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?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:tx="http://www.springframework.org/schema/tx"
 xmlns:jee="http://www.springframework.org/schema/jee"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.3.xsd
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

 <context:property-placeholder location="classpath:hibernate.properties"/>
 
 <bean id="myDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName" value="jdbc/rmsystem"></property>
  <property name="resourceRef" value="true"></property>
 </bean>

<!--  <jee:jndi-lookup id="myDataSource" jndi-name="jdbc/rmsystem" expected-type="javax.sql.DataSource" /> -->
 
 <bean id="sessionFactory"
  class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
  <property name="packagesToScan"
   value="in.blogspot.ashish4java.employeesystem.model"></property>
  <property name="dataSource" ref="myDataSource" />
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">${hibernate.dialect}</prop>
    <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
    <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
    <prop key="current_session_context_class">${hibernate.current_session_context_class}</prop>
    <prop key="cache.provider_class">${hibernate.cache.provider_class}</prop>
    <prop key="hibernate.generate_statistics">${hibernate.generate_statistics}</prop>
    <prop key="hibernate.autocommit">${hibernate.autocommit}</prop>
    <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>  
   </props>
  </property>
 </bean>

 <bean id="transactionManager"  class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
 
</beans>

Note, there are two ways in which we can configure this. I have commented out the second way. Difference in these way is with property 'resourceRef'. This property set whether the look up occurs in a J2EE container, i.e. if the prefix "java:comp/env/" needs to be added if the JNDI name doesn't already contain it. If configured as bean shown in first way then default value is 'false'. So we need to set that property to 'true'. However if datasource is configured using jee:jndi-lookup then default value is 'true' so need to set explicitly to true. After this, this datasource is passed to SessionFactory bean to create and configure  SessionFactory object.

mvc-context.xml
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

 <mvc:annotation-driven />
 <context:component-scan base-package="in.blogspot.ashish4java.employeesystem" />

 <tx:annotation-driven />

 <bean
  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="prefix" value="WEB-INF/jsp/" />
  <property name="suffix" value=".jsp" />
 </bean>

</beans>

EmployeeDao.java
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package in.blogspot.ashish4java.employeesystem.dao;

import java.util.List;

import in.blogspot.ashish4java.employeesystem.model.Employee;

public interface EmployeeDao {

 List<Employee> findAllEmployees();

}

EmployeeDaoImpl.java
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package in.blogspot.ashish4java.employeesystem.dao;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import in.blogspot.ashish4java.employeesystem.model.Employee;

@Repository
public class EmployeeDaoImpl implements EmployeeDao {

 @Autowired
 SessionFactory sessionFactory;

 protected Session getsession() {
  return sessionFactory.getCurrentSession();
 }

 @SuppressWarnings("unchecked")
 @Override
 public List<Employee> findAllEmployees() {
  Session session = getsession();
  Criteria criteria = session.createCriteria(Employee.class);
  return (List<Employee>) criteria.list();
 }

}

EmployeeService.java
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package in.blogspot.ashish4java.employeesystem.service;

import java.util.List;

import in.blogspot.ashish4java.employeesystem.model.Employee;

public interface EmployeeService {

 List<Employee> findAllEmployees();

}

EmployeeServiceImpl.java
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package in.blogspot.ashish4java.employeesystem.service;

import java.util.List;

import org.springframework.transaction.annotation.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import in.blogspot.ashish4java.employeesystem.dao.EmployeeDao;
import in.blogspot.ashish4java.employeesystem.model.Employee;

@Service
@Transactional
public class EmployeeServiceImpl implements EmployeeService {

 @Autowired
 EmployeeDao employeeDao;

 @Override
 public List<Employee> findAllEmployees() {
  return employeeDao.findAllEmployees();
 }

}

finally, WelcomeController.java
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package in.blogspot.ashish4java.employeesystem.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import in.blogspot.ashish4java.employeesystem.model.Employee;
import in.blogspot.ashish4java.employeesystem.service.EmployeeService;

@Controller
public class WelcomeController {

 @Autowired
 private EmployeeService service;
 
 @RequestMapping(path = "/employeelist", method = RequestMethod.GET)
 public String sendMessage(ModelMap map) {
  List<Employee> empList = service.findAllEmployees();
  map.addAttribute("employeeList", empList);
  return "EmployeeList";
 }

}

As mentioned in controller, this controller accepts GET request at {context-root}/employeeList url and returns 'EmployeeList' String which will translate to 'WEB-INF/jsp/EmployeeList.jsp'

EmployeeList.jsp
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@ page session="false"%>
<html>
<head>
<title>Employee System</title>
<style type="text/css">
.tg {
 border-collapse: collapse;
 border-spacing: 0;
 border-color: #ccc;
}

.tg td {
 font-family: Arial, sans-serif;
 font-size: 14px;
 padding: 10px 5px;
 border-style: solid;
 border-width: 1px;
 overflow: hidden;
 word-break: normal;
 border-color: #ccc;
 color: #333;
 background-color: #fff;
 text-align: center;
}

.tg th {
 font-family: Arial, sans-serif;
 font-size: 14px;
 font-weight: normal;
 padding: 10px 5px;
 border-style: solid;
 border-width: 1px;
 overflow: hidden;
 word-break: normal;
 border-color: #ccc;
 color: #333;
 background-color: #f0f0f0;
 text-align: center;
}

.tg .tg-4eph {
 background-color: #f9f9f9
}
</style>
</head>
<body>
 <h1>List of Employees</h1>
 <c:if test="${!empty employeeList}">
  <table class="tg">
   <tr>
    <th width="80">Employee ID</th>
    <th width="120">First Name</th>
    <th width="120">Last Name</th>
    <th width="220">Designation</th>
    <th width="180">Joining Date</th>
   </tr>
   <c:forEach items="${employeeList}" var="employee">
    <tr>
     <td>${employee.employeeId}</td>
     <td>${employee.firstName}</td>
     <td>${employee.lastName}</td>
     <td>${employee.designation}</td>
     <td>${employee.joiningDate}</td>
    </tr>
   </c:forEach>
  </table>
 </c:if>
</body>
</html>

Apart from this, to load some initial data in database once hibernate drops and create new schema, create 'import.sql' file on classpath. Hibernate will execute this file after new schema is created by it.

import.sql

1
2
3
4
INSERT INTO public.employee( employeeid, designation, firstname, joiningdate, lastname, leavingdate) VALUES (1, 'Full Stack Java Developer', 'Ashish', '01/01/2011', 'Gajabi', null);
INSERT INTO public.employee( employeeid, designation, firstname, joiningdate, lastname, leavingdate) VALUES (2, 'CTO', 'John', '09/12/2001', 'Varma', null);
INSERT INTO public.employee( employeeid, designation, firstname, joiningdate, lastname, leavingdate) VALUES (3, 'Solution Designer', 'Richard', '06/14/2007', 'Hank', null);
INSERT INTO public.employee( employeeid, designation, firstname, joiningdate, lastname, leavingdate) VALUES (4, 'Senior Tester', 'Sachin', '03/28/2015', 'Singh', null);

Once all this done, deploy this application on tomcat where we configured datasource and hit the url http://localhost:8080/SpringDataSourceExample/employeelist

Output clearly shows Spring MVC application was successfully able to connect to datasource configured on tomcat and able to fetch list of employees through Hibernate.

Saturday, 5 November 2016

4. Hibernate Use of Spring's HibernateTransactionManager

In previous Hibernate tutorials, we covered basics of Hibernate Spring integration however we also saw for every transaction, we have to open session using method SessionFactory.openSession() then begin transaction, perform operation and then either commit or rollback transaction. This makes it transaction handling manual and not very convenient, not so readable and cumbersome as well.

HibernateTransactionManager handles transaction manager in Spring. The application that uses single hibernate session factory for database transaction, has good choice to use HibernateTransactionManager. SessionFactory.getCurrentSession() is required for Hibernate access code that needs to support this transaction handling mechanism, with the SessionFactory being configured with SpringSessionContext.

In this tutorial, we will update previous tutorial to use HibernateTransactionManager.

In Spring bean configuration file spring.xml, add bean definition for 'transactionManager' for class 'org.springframework.orm.hibernate4.HibernateTransactionManager'

spring.xml
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?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:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:util="http://www.springframework.org/schema/util"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
  http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.3.xsd
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
  http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd">

 <context:component-scan base-package="in.blogspot.ashish4java.employeesystem" />

 <tx:annotation-driven />

 <context:property-placeholder location="classpath:hibernate.properties" />

 <bean id="myDataSource"
  class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName" value="${jdbc.driverClassName}" />
  <property name="url" value="${jdbc.url}" />
  <property name="username" value="${jdbc.user}" />
  <property name="password" value="${jdbc.pass}" />
 </bean>

 <bean id="sessionFactory"
  class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
  <property name="packagesToScan" value="in.blogspot.ashish4java.employeesystem.model"></property>
  <property name="dataSource" ref="myDataSource" />
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">${hibernate.dialect}</prop>
    <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
    <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
    <prop key="current_session_context_class">${hibernate.current_session_context_class}</prop>
    <prop key="cache.provider_class">${hibernate.cache.provider_class}</prop>
    <prop key="hibernate.autocommit">${hibernate.autocommit}</prop>
    <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
   </props>
  </property>
 </bean>

 <bean id="transactionManager"
  class="org.springframework.orm.hibernate4.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
 </bean>

</beans>

The default value for the transaction-manager attribute in  is 'transactionManager'. So we have kept bean id as 'transactionManager' for class 'org.springframework.orm.hibernate4.HibernateTransactionManager'

Once this is done, let's modify DAO classes first to get rid of all boilerplate code related to transaction.

AbstractDao.java
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package in.blogspot.ashish4java.employeesystem.dao;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;

public class AbstractDao {

 @Autowired
 SessionFactory sessionFactory;

 protected Session getsession() {
  return sessionFactory.getCurrentSession();
 }

 public void createEntity(Object entity) {
  getsession().save(entity);
 }

 public void updateEntity(Object entity) {
  getsession().update(entity);
 }

 public void deleteEntity(Object entity) {
  getsession().delete(entity);
 }

}

Here, we are getting current(same) session associated that thread as property hibernate.current_session_context_class is set to thread. We have also removed code related to begin transaction and committing/rolling back transaction. Spring's transaction manager takes care of that.

Updated EmployeeDaoImpl.java is as below,

EmployeeDaoImpl.java
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package in.blogspot.ashish4java.employeesystem.dao;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository;

import in.blogspot.ashish4java.employeesystem.model.Employee;

@Repository
public class EmployeeDaoImpl extends AbstractDao implements EmployeeDao {

 @Override
 public void createEmployee(Employee emp) {
  createEntity(emp);
 }

 @Override
 public void updateEmployee(Employee emp) {
  updateEntity(emp);
 }

 @Override
 public void deleteEmployee(Employee emp) {
  deleteEntity(emp);
 }

 @Override
 public Employee getEmployee(int employeeId) {
  Session session = getsession();
  Criteria criteria = session.createCriteria(Employee.class).add(Restrictions.eq("employeeId", employeeId));
  return (Employee) criteria.uniqueResult();
 }

 @SuppressWarnings("unchecked")
 @Override
 public List<Employee> findAllEmployees() {
  Session session = getsession();
  Criteria criteria = session.createCriteria(Employee.class);
  return (List<Employee>) criteria.list();
 }

 @SuppressWarnings("unchecked")
 @Override
 public List<Employee> findAllEmployeesByDesignation(String designation) {
  Session session = getsession();
  Query query = session.createQuery("from Employee where designation = :designation");
  query.setString("designation", designation);
  return query.list();
 }

}

We also need to annotate Service class or its method that they are transactional as below. 

EmployeeServiceImpl.java

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package in.blogspot.ashish4java.employeesystem.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import in.blogspot.ashish4java.employeesystem.dao.EmployeeDao;
import in.blogspot.ashish4java.employeesystem.model.Employee;

@Service ("empService")
@Transactional
public class EmployeeServiceImpl implements EmployeeService {

 @Autowired
 EmployeeDao employeeDao;

 @Override
 public void createEmployee(Employee emp) {
  employeeDao.createEmployee(emp);

 }

 @Override
 public void updateEmployee(Employee emp) {
  employeeDao.updateEmployee(emp);
 }

 @Override
 public void deleteEmployee(Employee emp) {
  employeeDao.deleteEmployee(emp);
 }

 @Override
 public Employee getEmployee(int employeeId) {
  return employeeDao.getEmployee(employeeId);
 }

 @Override
 public List<Employee> findAllEmployees() {
  return employeeDao.findAllEmployees();
 }

 @Override
 public List<Employee> findAllEmployeesByDesignation(String designation) {
  return employeeDao.findAllEmployeesByDesignation(designation);
 }

}

Once all these changes are done, run MainClient class and you will see no difference in outcome.

Thursday, 3 November 2016

Spring MVC

The Spring Web model-view-controller (MVC) framework is designed around a DispatcherServlet which is like FrontController Servlet that dispatches requests to handlers, with configurable handler mappings, view resolution, locale, time zone and theme resolution as well as support for uploading files. The default handler is based on the @Controller and @RequestMapping annotations. With Spring 3.0 onwards, the @Controller mechanism also allows you to create RESTful Web sites and applications, through the @PathVariable annotation and other features.

Spring’s web MVC framework is, like many other web MVC frameworks, request-driven, designed around a central Servlet that dispatches requests to controllers and offers other functionality that facilitates the development of web applications.

The request processing work flow of the Spring Web MVC is illustrated in following diagram.


Spring’s view resolution is extremely flexible. A Controller is typically responsible for preparing a model Map with data and selecting a view name but it can also write directly to the response stream and complete the request. View name resolution is highly configurable through file extension or Accept header content type negotiation, through bean names, a properties file, or even a custom ViewResolver implementation. The model (the M in MVC) is a Map interface, which allows for the complete abstraction of the view technology. You can integrate directly with template based rendering technologies such as JSP, Velocity and Freemarker, or directly generate XML, JSON, Atom, and many other types of content. The model Map is simply transformed into an appropriate format, such as JSP request attributes, a Velocity template model.

With this background, let's create basic Spring MVC application. 

In this tutorial, we will be using Spring-webmvc version 4.3.3.RELEASE.

Project Structure at completion of tutorial will be like below,



Create Maven WebApp project using archetype. Once done, update pom.xml as below,

pom.xml
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>in.blogspot.ashish4java</groupId>
 <artifactId>SpringMVCExample</artifactId>
 <packaging>war</packaging>
 <version>0.0.1-SNAPSHOT</version>
 <name>SpringMVCExample Maven Webapp</name>
 <url>http://maven.apache.org</url>
 <properties>
  <springframework.version>4.3.3.RELEASE</springframework.version>
 </properties>
 <dependencies>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>${springframework.version}</version>
  </dependency>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>servlet-api</artifactId>
   <version>2.5</version>
   <scope>provided</scope>
  </dependency>
 </dependencies>
 <build>
  <finalName>SpringMVCExample</finalName>
  <pluginManagement>
   <plugins>
    <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-compiler-plugin</artifactId>
     <version>3.6.0</version>
     <configuration>
      <source>1.8</source>
      <target>1.8</target>
     </configuration>
    </plugin>
   </plugins>
  </pluginManagement>
 </build>
</project>

Update web.xml as below,

web.xml
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
 version="3.1">
 <welcome-file-list>
  <welcome-file>index.jsp</welcome-file>
 </welcome-file-list>
 <servlet>
  <servlet-name>dispatcherservlet</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>/WEB-INF/spring/mvc-context.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
  <servlet-name>dispatcherservlet</servlet-name>
  <url-pattern>/</url-pattern>
 </servlet-mapping>
</web-app>

We have configured DispatcherServlet and passed location of Spring Bean config file as init-param. 

mvc-context.xml
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?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-4.3.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
  http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd">

 <mvc:annotation-driven />
 <context:component-scan base-package="in.blogspot.ashish4java.springmvcexample" />

 <bean
  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="prefix" value="WEB-INF/jsp/" />
  <property name="suffix" value=".jsp" />
 </bean>

</beans>

We have configured this mvc to be annotation driven and also provided base package to scan for annotations.

View Resolver is also configured which will identify view to render response. View will be resolved based on provided prefix and suffix to String returned from Controller method. If Controller method returns String "message" then to render response back, "WEB-INF/jsp/" + "message" + ".jsp" = "WEB-INF/jsp/message.jsp" will be used.

In web.xml, we have configured index.jsp as welcome file.

index.jsp
1
2
3
4
5
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>

At this point, if we deploy this app to tomcat server, we should at least get index.jsp page properly. If it doesn't then something is done incorrectly and needs to be further investigated.

Now controller part,
WelcomeController.java
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package in.blogspot.ashish4java.springmvcexample;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class WelcomeController {

 @RequestMapping(path = "/welcome", method = RequestMethod.GET)
 public String sendMessage(ModelMap map) {
  map.addAttribute("message", "Greetings from WelcomeController!");
  return "message";
 }

}

sendMessage method accept GET request coming from url '/welcome' and returning String "message" which as mentioned above will be used to render response. Also ModelMap which implements java.util.Map interface and extends java.util.LinkedHashMap is exposed to web view.

Now last part is to create message.jsp

message.jsp


1
2
3
4
5
6
7
8
<html>
<head>
<title>Spring MVC Example</title>
</head>
<body>
 <h1>Message: ${message}</h1>
</body>
</html>

Once all this done, deploy this application on tomcat server. Once deployed successfully, hit the url http://localhost:8080/SpringMVCExample/ 


then to url http://localhost:8080/SpringMVCExample/welcome to get desired result as below,