spring

Spring Framework Angularjs Integration Tutorial

Spring Application Framework enables us develop RESTful services using its Web MVC subsystem, and we can consume those services via any of the client side front end UI technologies, e.g Angular, Backbone or React JS. In this example, I will explain how a CRUD like scenario can be developed using Spring MVC at the back side, and Angular JS in front.

We will use latest versions of JDK 8 and Spring Tool Suite IDE and Angular JS in order to develop our example web application. You can download and install latest versions from here, here and here.

Let’s start developing server side part of our example application as the first step.

1. Create a new Maven WebApp project

Create a new maven web app project called as “example” within the STS IDE.

2. Add necessary dependencies

Add necessary dependencies within the pom.xml file of the project.

pom.xml

<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>4.3.5.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>4.3.5.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.7.1-1</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<version>1.4.192</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>5.2.7.Final</version>
		</dependency>

3. Create domain model, DAO and service classes

User.java

package com.example.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="t_user")
public class User {
	@Id
	@GeneratedValue(strategy=GenerationType.SEQUENCE)
	private Long id;
	
	@Column(name="first_name")
	private String firstName;
	
	@Column(name="last_name")
	private String lastName;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
		result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		User other = (User) obj;
		if (firstName == null) {
			if (other.firstName != null)
				return false;
		} else if (!firstName.equals(other.firstName))
			return false;
		if (lastName == null) {
			if (other.lastName != null)
				return false;
		} else if (!lastName.equals(other.lastName))
			return false;
		return true;
	}
}

Our domain class is super simple. It has only two attributes; “firstName” and “lastName”. We have also a simple UserDao interface as well.
UserDao.java

package com.example.dao;

import java.util.List;

import com.example.model.User;

public interface UserDao {
	public List<User> findAll();
	public User findById(Long id);
	public void save(User user);
	public void update(User user);
	public void delete(Long id);
}

HibernateUserDao.java

package com.example.dao;

import java.util.List;

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

import com.example.model.User;

@Repository
public class HibernateUserDao implements UserDao {

	@Autowired
	private SessionFactory sessionFactory;

	@SuppressWarnings("unchecked")
	public List<User> findAll() {
		return sessionFactory.getCurrentSession().createQuery("from User").getResultList();
	}

	public User findById(Long id) {
		return sessionFactory.getCurrentSession().find(User.class, id);
	}

	public void save(User user) {
		sessionFactory.getCurrentSession().save(user);
	}

	public void update(User user) {
		sessionFactory.getCurrentSession().update(user);
	}

	public void delete(Long id) {
		sessionFactory.getCurrentSession().delete(sessionFactory.getCurrentSession().getReference(User.class, id));
	}

}

We will employ Hibernate in order to perform CRUD operations on our domain instances. Therefore, we created HibernateUserDao concrete class as above which implements UserDao interface we already defined. SessionFactory bean was injected within the dao bean so that persistence operations could be performed with Hibernate Session.

UserService.java

package com.example.service;

import java.util.List;

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

import com.example.dao.UserDao;
import com.example.model.User;

@Service
@Transactional
public class UserService {
	@Autowired
	private UserDao userDao;
	
	public List<User> findAll() {
		return userDao.findAll();
	}
	
	public User findById(Long id) {
		return userDao.findById(id);
	}
	
	public void save(User user) {
		userDao.save(user);
	}
	
	public void update(User user) {
		userDao.update(user);
	}
	
	public void delete(Long id) {
		userDao.delete(id);
	}
}

We also created a service class and inject UserDao bean into it. Our service class is only a thin layer which just delegates to its DAO counterpart in addition to making each public service method call transactional.

4. Create REST Controller layer

UserController.java

package com.example.web;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.example.model.User;
import com.example.service.UserService;

@RestController
@RequestMapping("/rest")
public class UserController {
	@Autowired
	private UserService userService;
	
	@RequestMapping(value="/users",method=RequestMethod.GET)
	public List<User> findAll() {
		return userService.findAll();
	}
	
	@RequestMapping(value="/users/{id}",method=RequestMethod.GET)
	public User findById(@PathVariable Long id) {
		return userService.findById(id);
	}
	
	@RequestMapping(value="/users",method=RequestMethod.POST)
	@ResponseStatus(code=HttpStatus.CREATED)
	public void save(@RequestBody User user) {
		userService.save(user);
	}
	
	@RequestMapping(value="/users",method=RequestMethod.PUT)
	@ResponseStatus(code=HttpStatus.OK)
	public void update(@RequestBody User user) {
		userService.update(user);
	}
	
	@RequestMapping(value="/users/{id}",method=RequestMethod.DELETE)
	@ResponseStatus(code=HttpStatus.OK)
	public void delete(@PathVariable Long id) {
		userService.delete(id);
	}
}

It will be a simple REST controller class which is going to handle our GET, POST, PUT and DELETE HTTP method calls towards “/users” URI, and delegate the job to the service layer.
As there is no JAXB annotation within our domain model class User, and we have already included Jackson JSON library within our classpath, Spring Web MVC at runtime converts return values to JSON format and vice versa.

5. Create Configuration and WebApplicationInitializer classes

Create Configuration and WebApplicationInitializer classes in order to configure and bootstrap our Spring enabled web application on server side.

ExampleWebMvcConfig.java

package com.example.config;

import javax.sql.DataSource;

import org.hibernate.SessionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBuilder;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
@EnableWebMvc
@EnableTransactionManagement
@ComponentScan(basePackages = "com.example")
public class ExampleWebMvcConfig extends WebMvcConfigurerAdapter {
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/angular-1.6.1/**").addResourceLocations("/angular-1.6.1/");
		registry.addResourceHandler("/app/**").addResourceLocations("/app/");
		registry.addResourceHandler("/user/**").addResourceLocations("/user/");
	}

	@Bean
	public PlatformTransactionManager transactionManager(SessionFactory sessionFactory) {
		return new HibernateTransactionManager(sessionFactory);
	}

	@Bean
	public SessionFactory sessionFactory(DataSource dataSource) {
		return new LocalSessionFactoryBuilder(dataSource).scanPackages("com.example.model")
				.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect").buildSessionFactory();
	}

	@Bean
	public DataSource dataSource() {
		return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).addScript("classpath:db.sql").build();
	}
}

ExampleWebAppInitializer.java

package com.example.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class ExampleWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	protected Class<?>[] getRootConfigClasses() {
		return null;
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		return new Class[]{ExampleWebMvcConfig.class};
	}

	@Override
	protected String[] getServletMappings() {
		return new String[]{"/"};
	}
}

6. Create db.sql

Create db.sql with the following content within src/main/resources file. This file is going to be processed by Spring’s EmbeddedDataBaseBuilder in order to create in memory H2 DataSource instance.

db.sql

CREATE USER IF NOT EXISTS SA SALT 'a9160799b9bc9c38' HASH '55cdff35bf0a2fe6bf8d88ab8eba4911cc8fac325176cd02f0498640181089cc' ADMIN;            
CREATE SEQUENCE PUBLIC.HIBERNATE_SEQUENCE START WITH 3;        
CREATE CACHED TABLE PUBLIC.T_USER(
    ID BIGINT NOT NULL,
    FIRST_NAME VARCHAR(255),
    LAST_NAME VARCHAR(255)
);          
ALTER TABLE PUBLIC.T_USER ADD CONSTRAINT PUBLIC.CONSTRAINT_9 PRIMARY KEY(ID);  
INSERT INTO PUBLIC.T_USER(ID, FIRST_NAME, LAST_NAME) VALUES
(1, 'John', 'Doe'),
(2, 'Joe', 'Doe');   

7. Deploy the example web application

We can now deploy the example web application into the “tc Server”, already configured within the STS IDE. You can check that your application has been deployed and bootstrapped correctly by typing http://localhost:8080/example/rest/users in your browser’s URL toolbar. You should have seen JSON response produced by the REST service you invoked.

At this point our server side is ready to serve for the client side. Let’s create our front end now using Angular JS. Angular JS is a client side JS framework in order to develop single page web applications based on MVC architectural pattern. Simply saying, View is generated through Controller at runtime via consuming Model data. HTML pages are used as templates to generate view representations at runtime. Controller is in charge of interacting with backend services through REST API calls. It is a good practice to partition a web application developed using Angular JS into several modules/folders and sub modules each containing several related JS and HTML template files of their own.

8. Extract and install Angular JS

Extract Angular JS zipped file into the src/main/webapp folder. You should now see a folder with name angular-1.6.1 within that folder.

9. Create app and user folders within src/main/webapp folder

Those folders will be main module folders in our application.

10. Create app.module.js file within app folder

app.module.js

angular.module('app',['ngRoute','user']);

This is the main “app” module which imports other dependent application or Angular specific modules in the application. “ngRoute” is an Angular module which is used to navigate through URIs, and “user” is application specific module within which our CRUD scenarios reside.

11. Create user.module.js file within user folder

user.module.js

angular.module('user',['ngResource','userList','userDetail']);

This is the “user” module file which combines two other sub modules namely “userList” and “userDetail” and Angular specific “ngResource” module which is used for REST API calls.

12. Create app.config.js file within app folder

app.config.js

angular.module('app').config(['$locationProvider','$routeProvider',
	function config($locationProvider,$routeProvider) {
		$routeProvider.when('/list', {
			template:'<user-list></user-list>'
		}).when('/edit/:id', {
			template:'<user-detail></user-detail>'
		}).when('/create',{
			template:'<user-detail></user-detail>'
		}).otherwise('/list');
}]);

This JS file contains routing information. When URI fragment contains “#!/list”, <user-list> module will be displayed on the screen, or when URI fragment contains either “#!/edit/:id” or “#!/create” <user-detail> module will be displayed.

13. Create user.service.js file within user folder

user.service.js

angular.module('user').factory('UserService',['$resource', function($resource) {
	return $resource(
			'rest/users/:id',
			{
				
			},
			{
				update:{method:'PUT'}
			},
			{
				stripTrailingSlashes:true
			});
}]);

This JS makes use of Angular’s “$resource” service to define a REST service in order to perform REST API calls on the server side.

14. Create “user-list” and “user-detail” modules

Create “user-list” and “user-detail” modules sub folders within user folder, and place their corresponding module files under user-list and user-detail folders respectively.

user-list.module.js

angular.module('userList',[]);

user-detail.module.js

angular.module('userDetail',['ngRoute']);

15. Create user-list.template.html and user-detail.template.html files

We will bring controller and template files together within those module folders. Let’s create user-list.template.html and user-detail.template.html files within their respective folders with the following contents.

user-list.template.html

<button ng-click="$ctrl.create()">Create User</button>
<br/>
<br/>
<table border="0">
	<tr bgcolor="lightgray">
		<td>ID</td>
		<td>First Name</td>
		<td>Last Name</td>
		<td>Action</td>
	</tr>
	<tr ng-repeat="user in $ctrl.users">
		<td>{{user.id}}</td>
		<td>{{user.firstName}}</td>
		<td>{{user.lastName}}</td>
		<td>
			<button ng-click="$ctrl.edit(user.id)">Edit</button>
		</td>
	</tr>
</table>

We will list all users returned by our REST API call “/users” and display them in tabular format. Each row will have an Edit action button. We will be able to navigate to “user-detail” view by clicking it. There is also a Create User button above the table in order to create a new User. This will also take us to “user-detail” view as well, however with a slightly different UI state.

user-detail.template.html

	<table border="0">
		<tr>
			<td bgcolor="lightgray">First Name:</td>
			<td><input ng-model="$ctrl.user.firstName"/></td>
		</tr>
		<tr>
			<td bgcolor="lightgray">Last Name:</td>
			<td><input ng-model="$ctrl.user.lastName"/></td>
		</tr>
	</table>
	<button ng-show="!$ctrl.user.id" ng-click="$ctrl.save()">Save</button>
	<button ng-show="$ctrl.user.id" ng-click="$ctrl.update()">Update</button>
	<button ng-show="$ctrl.user.id" ng-click="$ctrl.delete()">Delete</button>

When Edit button is clicked a REST API call will be made to “/users/{id}” URI with HTTP GET method, and User details will be shown in the page. You can either decide to change user’s properties or delete the user. When Update button is clicked, a REST API call will be made to “/users/{id}” URI with HTTP PUT method in order to update contents of the user record. When Delete button is clicked, a REST API call will be made to “/users/{id}” with HTTP DELETE method in order to delete the user record from DB.

If the detail view is navigated through “#!/create”, then Create button will be visible instead, and whenever you click Create button after filling out input fields, a new User will be created by invoking REST API call towards “/users” URI with HTTP POST method.

16. Create Controller files

All the above work is handled within controllers in Angular, and let’s create our controllers as follows.

user-list.component.js

angular.module('userList').component('userList', {
	templateUrl:'user/user-list/user-list.template.html',
	controller: ['$location','UserService',UserListController]
});

function UserListController($location,UserService) {
		var self = this;
		self.users = UserService.query();
		
		self.create = function() {
			$location.path('/create');
		};
		
		self.edit = function(id) {
			$location.path('/edit/' + id);
		};
}

user-detail.component.js

angular.module('userDetail').component('userDetail',{
	templateUrl:'user/user-detail/user-detail.template.html',
	controller:['$routeParams','$location','UserService',UserDetailController]
});

function UserDetailController($routeParams,$location,UserService) {
	var self = this;
	self.user = $routeParams.id?UserService.get({id:$routeParams.id}):null;
	
	self.save = function() {
		UserService.save(self.user,function() {		
			$location.path('/list');
		});
	};
	
	self.update = function() {
		UserService.update(self.user, function() {
			$location.path('/list');
		});
	};
	
	self.delete = function() {
		UserService.delete({id:self.user.id}, function() {
			$location.path('/list');	
		});
	};
}

We simply bound controllers with their templates together in those files, define functions to handle view navigation requests, and perform REST API service calls.

17. Modify index.jsp

index.jsp

<html ng-app="app">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Example Web Application</title>
<script type="text/javascript" src="angular-1.6.1/angular.js"></script>
<script type="text/javascript" src="angular-1.6.1/angular-route.js"></script>
<script type="text/javascript" src="angular-1.6.1/angular-resource.js"></script>
<script type="text/javascript" src="app/app.module.js"></script>
<script type="text/javascript" src="app/app.config.js"></script>
<script type="text/javascript" src="user/user.module.js"></script>
<script type="text/javascript" src="user/user-list/user-list.module.js"></script>
<script type="text/javascript"
	src="user/user-detail/user-detail.module.js"></script>
<script type="text/javascript" src="user/user.service.js"></script>
<script type="text/javascript"
	src="user/user-list/user-list.component.js"></script>
<script type="text/javascript"
	src="user/user-detail/user-detail.component.js"></script>
</head>
<body>
	<div ng-view></div>
</body>
</html>

It lists JS files we already created and define the place which will be controlled by our Angular module “app”.
That’s all we need to do integrate Angular JS with Spring Web MVC. Let’s give it a try by typing http://localhost:8080/example. You should have seen Users listed. You can create a new User, edit or delete an existing one.

18. Summary

In this installment, we created a simple REST API to perform CRUD operation via Spring Web MVC, and developed a client side using Angular JS to consume those REST services we created.

19. Download the Source Code

Download
You can download the full source code of this example here: Spring with Angular JS Example

Kenan Sevindik

Develops enterprise software, gives training, mentoring and consultancy services about Java, Design Patterns, OOP, AOP, Spring, Spring Security, Vaadin and Hibernate. Specialized on the architecture and development of enterprise applications using various Java technologies for more than 15 years. Works with various enterprise Java frameworks, such as Spring Application Framework, Spring Security Framework, Hibernate Persistence Framework since their initial phases. Co-author of "Beginning Spring" book published by Wiley Publishing in February 2015. Public speaker. B.S. in Computer Engineering.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button