Spring Boot Interview Questions
Spring Boot
Spring Boot is an open source Java-based framework used to
create a micro Service. It is developed by Pivotal Team and is used to build
stand-alone and production ready spring applications.
Spring
Boot is designed with the following goals −
·
To avoid complex XML configuration in Spring
·
To develop a production ready Spring applications in an
easier way
·
To reduce the development time and run the application
independently
·
Offer an easier way of getting started with the application
Why Spring Boot
·
It provides a flexible way to configure Java Beans, XML
configurations, and Database Transactions.
·
It provides a powerful batch processing and manages REST
endpoints.
·
In Spring Boot, everything is auto configured; no manual configurations
are needed.
·
It offers annotation-based spring application
·
Eases dependency management
·
It includes Embedded Servlet Container
What is Micro Service?
Micro Service is an architecture that allows the developers
to develop and deploy services independently. Each service running has its own
process and this achieves the lightweight model to support business
applications.
Advantages
·
Micro services offers the following advantages to its
developers −
·
Easy deployment
·
Simple scalability
·
Compatible with Containers
·
Minimum configuration
·
Lesser production time
The entry point of the spring boot application is
the class contains @SpringBootApplication annotation
and the main method.
Spring Boot automatically scans all the
components included in the project by using @ComponentScan annotation.
Spring Boot Application
The entry point of
the Spring Boot Application is the class contains @SpringBootApplication annotation. This class should have the
main method to run the Spring Boot application.
@SpringBootApplicationannotation
includes Auto- Configuration, Component Scan, and Spring Boot Configuration.
If you
added @SpringBootApplication annotation to the class, you do not need
to add the @EnableAutoConfiguration, @ComponentScan and @SpringBootConfiguration annotation.
The @SpringBootApplicationannotation includes all other annotations.
Component Scan
Spring Boot
application scans all the beans and package declarations when the application
initializes. You need to add the @ComponentScan annotation
for your class file to scan your components added in your project.
The @Value annotation is used to read the environment or application property value in Java code. The syntax to read the property value is shown below −
@Value("${property_key_name}")
Spring Boot provides a very good support to building RESTful
Web Services for enterprise applications.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Rest Controller
The
@RestController annotation is used to define the RESTful web services. It
serves JSON, XML and custom response. Its syntax is shown below −
@RestController
public class ProductServiceController {
}
Request Mapping
The
@RequestMapping annotation is used to define the Request URI to access the REST
Endpoints. We can define Request method to consume and produce object. The
default request method is GET.
@RequestMapping(value = "/products")
public ResponseEntity<Object> getProducts() { }
Request Body
The @RequestBody
annotation is used to define the request body content type.
public ResponseEntity<Object> createProduct(@RequestBody Product product) {
}
Path Variable
The @PathVariable
annotation is used to define the custom or dynamic request URI. The Path
variable in request URI is defined as curly braces {} as shown below −
public ResponseEntity<Object> updateProduct(@PathVariable("id") String id) {
}
Request Parameter
The @RequestParam
annotation is used to read the request parameters from the Request URL. By
default, it is a required parameter. We can also set default value for request
parameters as shown here −
public ResponseEntity<Object> getProduct(
@RequestParam(value = "name", required = false, defaultValue = "honey") String name) {
}
GET API
The default HTTP
request method is GET. This method does not require any Request Body. You can
send request parameters and path variables to define the custom or dynamic URL.
@RestController
public class ProductServiceController {
private static Map<String, Product> productRepo = new HashMap<>();
static {
Product honey = new Product();
honey.setId("1");
honey.setName("Honey");
productRepo.put(honey.getId(), honey);
}
@RequestMapping(value = "/products")
public ResponseEntity<Object> getProduct() {
return new ResponseEntity<>(productRepo.values(), HttpStatus.OK);
}
POST API
The HTTP POST
request is used to create a resource. This method contains the Request Body. We
can send request parameters and path variables to define the custom or dynamic
URL.
@RestController
public class ProductServiceController {
private static Map<String, Product> productRepo = new HashMap<>();
@RequestMapping(value = "/products", method = RequestMethod.POST)
public ResponseEntity<Object> createProduct(@RequestBody Product product) {
productRepo.put(product.getId(), product);
return new ResponseEntity<>("Product is created successfully", HttpStatus.CREATED);
}
}
PUT API
The HTTP PUT
request is used to update the existing resource. This method contains a Request
Body. We can send request parameters and path variables to define the custom or
dynamic URL.
@RestController
public class ProductServiceController {
private static Map<String, Product> productRepo = new HashMap<>();
@RequestMapping(value = "/products/{id}", method = RequestMethod.PUT)
public ResponseEntity<Object> updateProduct(@PathVariable("id") String id, @RequestBody Product product) {
productRepo.remove(id);
product.setId(id);
productRepo.put(id, product);
return new ResponseEntity<>("Product is updated successsfully", HttpStatus.OK);
}
}
DELETE API
The HTTP Delete
request is used to delete the existing resource. This method does not contain
any Request Body. We can send request parameters and path variables to define
the custom or dynamic URL.
@RestController
public class ProductServiceController {
private static Map<String, Product> productRepo = new HashMap<>();
@RequestMapping(value = "/products/{id}", method = RequestMethod.DELETE)
public ResponseEntity<Object> delete(@PathVariable("id") String id) {
productRepo.remove(id);
return new ResponseEntity<>("Product is deleted successsfully", HttpStatus.OK);
}
}
Spring Boot Exception Handling
Handling exceptions and errors in APIs and sending the proper
response to the client is good for enterprise applications.
Controller
Advice
The @ControllerAdvice is an annotation, to
handle the exceptions globally.
Exception
Handler
The @ExceptionHandler is an annotation used
to handle the specific exceptions and sending the custom responses to the
client.
@ControllerAdvice
public class ProductExceptionController {
}
public class ProductNotfoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
}
@ExceptionHandler(value = ProductNotfoundException.class)
public ResponseEntity<Object> exception(ProductNotfoundException exception) {
}
@RequestMapping(value = "/products/{id}", method = RequestMethod.PUT)
public ResponseEntity<Object> updateProduct() {
throw new ProductNotfoundException();
}
Spring Boot - Interceptors
You can
use the Interceptor in Spring Boot to perform operations under the following
situations −
·
Before sending the request
to the controller
·
Before sending the response
to the client
For
example, you can use an interceptor to add the request header before sending the
request to the controller and add the response header before sending the
response to the client.
To work
with interceptor, you need to create @Component class that
supports it and it should implement the HandlerInterceptor interface.
The
following are the three methods you should know about while working on
Interceptors −
·
preHandle() method − This is used to perform operations before sending the
request to the controller. This method should return true to return the
response to the client.
·
postHandle() method − This is used to perform operations before sending the
response to the client.
·
afterCompletion() method − This is used to perform operations after completing
the request and response.
@Component
public class ProductServiceInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object
handler) throws Exception {
return true;
}
@Override
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object
handler,
ModelAndView
modelAndView) throws Exception {}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object
handler, Exception exception) throws Exception {}
}
You
will have to register this Interceptor with InterceptorRegistry by
using WebMvcConfigurerAdapter as shown below –
@Component
public class ProductServiceInterceptorAppConfig extends WebMvcConfigurerAdapter {
@Autowired
ProductServiceInterceptor productServiceInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(productServiceInterceptor);
}
}
A filter is an object
used to intercept the HTTP requests and responses of your application. By using
filter, we can perform two operations at two instances −
·
Before sending the request to the controller
·
Before sending a response to the client.
The
following code shows the sample code for a Servlet Filter implementation class
with @Component annotation.
@Component
public class SimpleFilter implements Filter {
@Override
public void destroy() {}
@Override
public void doFilter
(ServletRequest request, ServletResponse response, FilterChain filterchain)
throws IOException, ServletException {}
@Override
public void init(FilterConfig filterconfig) throws ServletException {}
}
Spring Boot-Rest Template
Rest Template is used to
create applications that consume RESTful Web Services. You can use the exchange() method
to consume the web services for all HTTP methods.
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
You will have to follow the given
points to consume the API −
·
Autowired the Rest
Template Object.
·
Use HttpHeaders to
set the Request Headers.
·
Use HttpEntity to
wrap the request object.
·
Provide the URL,
HttpMethod, and Return type for Exchange() method.
@RestController
public class ConsumeWebService {
@Autowired
RestTemplate restTemplate;
@RequestMapping(value = "/template/products")
public String getProductList() {
HttpHeaders headers =
new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity <String>
entity = new HttpEntity<String>(headers);
return restTemplate.exchange("
http://localhost:8080/products",
HttpMethod.GET,
entity, String.class).getBody();
}
}
File Upload
For uploading a file, you
can use MultipartFile as a Request Parameter and this API
should consume Multi-Part form data value.
@RestController
public class FileUploadController {
@RequestMapping(value
= "/upload",
method = RequestMethod.POST,
consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String
fileUpload(@RequestParam("file") MultipartFile
file) throws IOException {
File
convertFile = new File("/var/tmp/"+file.getOriginalFilename());
convertFile.createNewFile();
FileOutputStream
fout = new FileOutputStream(convertFile);
fout.write(file.getBytes());
fout.close();
return "File
is upload successfully";
}
}
File Download
For file download, you
should use InputStreamResource for downloading a File. We need to set the
HttpHeader Content-Disposition in Response and need to specify
the response Media Type of the application.
@RequestMapping(value = "/download",
method = RequestMethod.GET)
public ResponseEntity<Object>
downloadFile() throws IOException {
String
filename = "/var/tmp/mysql.png";
File
file = new File(filename);
InputStreamResource
resource = new InputStreamResource(new FileInputStream(file));
HttpHeaders
headers = new HttpHeaders();
headers.add("Content-Disposition", String.format("attachment;
filename=\"%s\"", file.getName()));
headers.add("Cache-Control", "no-cache,
no-store, must-revalidate");
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
ResponseEntity<Object>
responseEntity
= ResponseEntity.ok().headers(headers).contentLength(file.length()).contentType(
MediaType.parseMediaType("application/txt")).body(resource);
return
responseEntity;
}
Service Components are the
class file which contains @Service annotation. These class files are used to
write business logic in a different layer, separated from @RestController class
file.
@Service
public class ProductServiceImpl implements ProductService {
}
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$("button").click(function() {
var
productmodel = {
id : "3",
name : "Ginger"
};
var
requestJSON = JSON.stringify(productmodel);
$.ajax({
type : "POST",
url : "http://localhost:9090/products",
headers : {
"Content-Type" : "application/json"
},
data :
requestJSON,
success : function(data) {
alert(data);
},
error
: function(data) {
}
});
});
});
</script>
Cross-Origin Resource Sharing (CORS) is a
security concept that allows restricting the resources implemented in web
browsers. It prevents the JavaScript code producing or consuming the requests
against different origin.
We need to set the origins for RESTful web
service by using @CrossOriginannotation for the controller method.
This @CrossOrigin annotation supports specific REST API, and not for the entire
application.
@RequestMapping(value = "/products")
@CrossOrigin(origins = "http://localhost:8080")
public ResponseEntity<Object> getProduct() {
return null;
}
Spring Boot-Eureka Server
Eureka Server is an
application that holds the information about all client-service applications.
Every Micro service will register into the Eureka server and Eureka server
knows all the client applications running on each port and IP address. Eureka
Server is also known as Discovery Server.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
@SpringBootApplication
@EnableEurekaServer
public class EurekaserverApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaserverApplication.class, args);
}
}
Application.properties
eureka.client.registerWithEureka
= false
eureka.client.fetchRegistry
= false
server.port = 8761
By default, the Eureka
Server registers itself into the discovery.
How
to register the Spring Boot Micro service application into the Eureka Server.
Before registering the
application, please make sure Eureka Server is running on the port 8761 or
first build the Eureka Server and run it.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
@SpringBootApplication
@EnableEurekaClient
public class EurekaclientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaclientApplication.class, args);
}
}
To register the Spring Boot application into
Eureka Server we need to add the following configuration in our
application.properties file.
eureka.client.serviceUrl.defaultZone = http://localhost:8761/eureka
eureka.client.instance.preferIpAddress = true
spring.application.name = eurekaclient
Now, add the Rest Endpoint to return String in the
main Spring Boot application and the Spring Boot Starter web dependency in
build configuration file.
@SpringBootApplication
@EnableEurekaClient
@RestController
public class EurekaclientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaclientApplication.class, args);
}
@RequestMapping(value = "/")
public String home() {
return "Eureka Client application";
}
}
Zuul Proxy Server and Routing
Zuul Server is a gateway application that handles all the requests and does the dynamic routing of microservice applications. The Zuul Server is also known as Edge Server.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
@SpringBootApplication
@EnableZuulProxy
public class ZuulserverApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulserverApplication.class, args);
}
}
For Zuul
routing, add the below properties in your application.properties file
spring.application.name = zuulserver
zuul.routes.products.path = /api/demo/**
zuul.routes.products.url = http://localhost:8080/
server.port = 8111
This means that http calls to /api/demo/ get
forwarded to the products service.
Spring Cloud Configuration Server is a centralized
application that manages all the application related configuration
properties.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
@SpringBootApplication
@EnableConfigServer
public class ConfigserverApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigserverApplication.class, args);
}
}
server.port = 8888
spring.cloud.config.server.native.searchLocations=file:///C:/configprop/
SPRING_PROFILES_ACTIVE=native
Configuration Server runs
on the Tomcat port 8888 and application configuration properties are loaded
from native search locations.
Now, in file:///C:/configprop/,
place your client application - application.properties file.
The
@RefreshScope annotation is used to load the configuration properties value
from the Config server.
Spring
Boot Actuator provides secured endpoints for monitoring and managing your
Spring Boot application. By default, all actuator endpoints are secured.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
In the application.properties file, we need to
disable the security for actuator endpoints.
management.security.enabled = false
/metrics |
To view the application metrics such
as memory used, memory free, threads, classes, system uptime etc. |
/env |
To view the list of Environment
variables used in the application. |
/beans |
To view the Spring beans and its
types, scopes and dependency. |
/health |
To view the application health |
/info |
To view the information about the
Spring Boot application. |
/trace |
To view the list of Traces of your
Rest endpoints. |
Swagger
Swagger2 is an open
source project used to generate the REST API documents for RESTful web
services. It provides a user interface to access our RESTful web services via the
web browser.
To enable the Swagger2
in Spring Boot application, you need to add the following dependencies in our
build configurations file.
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
Now, add the @EnableSwagger2 annotation in your main Spring
Boot application. The @EnableSwagger2 annotation is used to enable the Swagger2
for your Spring Boot application.
@SpringBootApplication
@EnableSwagger2
public class SwaggerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SwaggerDemoApplication.class, args);
}
}
Next, create Docket Bean to configure Swagger2 for your
Spring Boot application. We need to define the base package to configure REST API(s)
for Swagger2.
@SpringBootApplication
@EnableSwagger2
public class SwaggerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SwaggerDemoApplication.class, args);
}
@Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.basePackage("com.tutorialspoint.swaggerdemo")).build();
}
}
Docker is a container management service that eases building
and deployment.
First,
create a file with the name Dockerfile under the
directories src/main/docker with the contents shown below.
Note that this file is important to create a Docker image.
FROM java:8
VOLUME /tmp
ADD dockerapp-0.0.1-SNAPSHOT.jar
app.jar
RUN bash -c 'touch /app.jar'
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
or Maven, add the Docker Maven plugin into
your build configuration file pom.xml
<properties>
<docker.image.prefix>spring-boot-tutorialspoint</docker.image.prefix>
</properties>
<build>
<plugins>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.0.0</version>
<configuration>
<imageName>${docker.image.prefix}/${project.artifactId}</imageName>
<dockerDirectory>src/main/docker</dockerDirectory>
<resources>
<resource>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
you can run your application
by using the Maven command mvn package docker:build
See the Docker images by the
command using docker images and see the image info on the console
docker images
Spring Boot - Tracing Micro Service Logs
Most developers face difficulty of tracing logs if any issue
occurred. This can be solved by Spring Cloud Sleuth and ZipKin server for
Spring Boot application.
Spring Cloud Sleuth
Spring cloud Sleuth
logs are printed in the following format −
[application-name,traceid,spanid,zipkin-export]
Where,
·
Application-name = Name of the application
·
Traceid = each request and response traceid is same when calling
same service or one service to another service.
·
Spanid = Span Id is printed along with Trace Id. Span Id is
different every request and response calling one service to another service.
·
Zipkin-export = By default it is false. If it is true, logs will
be exported to the Zipkin server.
Now, add the Spring
Cloud Starter Sleuth dependency in your build configuration file as follows −
Maven users can add
the following dependency in your pom.xml file –
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
Now, add the
application name in application.properties file
spring.application.name = tracinglogs
Zipkin
Server
Zipkin is an application
that monitors and manages the Spring Cloud Sleuth logs of your Spring Boot
application. To build a Zipkin server, we need to add the Zipkin UI and Zipkin
Server dependencies in our build configuration file.
Maven users can add the
following dependency in your pom.xml file –
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
</dependency>
Add the @EnableZipkinServer annotation in your main Spring
Boot application class fie. The @EnableZipkinServer annotation is used to
enable your application act as a Zipkin server.
Then, add the following dependency in
your client service application and point out the Zipkin Server URL to trace
the microservice logs via Zipkin UI.
Now, add the Spring Cloud Starter
Zipkin dependency in your build configuration file as shown −
Maven users can add the following
dependency in pom.xml file –
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
Now,
add the Always Sampler Bean in your Spring Boot application to
export the logs into Zipkin server.
@Bean
public AlwaysSampler defaultSampler() {
return new AlwaysSampler();
}
If
you add the AlwaysSampler Bean, then automatically Spring Sleuth Zipkin Export
option will change from false to true.
Next,
configure your Zipkin Server base URL in client service application.properties
file.
spring.zipkin.baseUrl =
http://localhost:9411/zipkin/
Then,
provide the trace id and find the traces in Zipkin UI.
http://localhost:9411/zipkin/traces/{traceid}/
Spring Boot- Batch Service
Batch Service is a process to execute more than one command
in a single task.
To create a Batch Service program, we
need to add the Spring Boot Starter Batch dependency and HSQLDB dependency in
our build configuration file.
Maven users can add the following
dependencies in pom.xml file.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
</dependency>
Now, add the simple CSV data file under classpath resources –
src/main/resources and name the file as file.csv
Next, write a SQL script for HSQLDB – under the classpath
resource directory
Create a POJO class for USERS model
Now, create an intermediate processor to
do the operations after the reading the data from the CSV file and before
writing the data into SQL.
package com.tutorialspoint.batchservicedemo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ItemProcessor;
public class UserItemProcessor implements ItemProcessor<User, User> {
private static final Logger log = LoggerFactory.getLogger(UserItemProcessor.class);
@Override
public User process(final User user) throws Exception {
final String
firstName = user.getFirstName().toUpperCase();
final String
lastName = user.getLastName().toUpperCase();
final User
transformedPerson = new User(firstName,
lastName);
log.info("Converting
(" + user + ") into (" + transformedPerson + ")");
return
transformedPerson;
}
}
Let us create a Batch configuration file, to read the data
from CSV and write into the SQL file as shown below. We need to add the
@EnableBatchProcessing annotation in the configuration class file. The @EnableBatchProcessing annotation is
used to enable the batch operations for your Spring Boot application.
@Configuration
@EnableBatchProcessing
public class BatchConfiguration {
@Autowired
public JobBuilderFactory jobBuilderFactory;
@Autowired
public StepBuilderFactory stepBuilderFactory;
@Autowired
public DataSource dataSource;
@Bean
public FlatFileItemReader<User>
reader() {
FlatFileItemReader<User>
reader = new FlatFileItemReader<User>();
reader.setResource(new ClassPathResource("file.csv"));
reader.setLineMapper(new DefaultLineMapper<User>() {
{
setLineTokenizer(new DelimitedLineTokenizer() {
{
setNames(new String[] { "firstName", "lastName" });
}
});
setFieldSetMapper(new BeanWrapperFieldSetMapper<User>() {
{
setTargetType(User.class);
}
});
}
});
return
reader;
}
@Bean
public UserItemProcessor processor() {
return new UserItemProcessor();
}
@Bean
public JdbcBatchItemWriter<User>
writer() {
JdbcBatchItemWriter<User>
writer = new JdbcBatchItemWriter<User>();
writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<User>());
writer.setSql("INSERT
INTO USERS (first_name, last_name) VALUES (:firstName, :lastName)");
writer.setDataSource(dataSource);
return
writer;
}
@Bean
public Job importUserJob(JobCompletionNotificationListener listener) {
return
jobBuilderFactory.get("importUserJob").incrementer(
new RunIdIncrementer()).listener(listener).flow(step1()).end().build();
}
@Bean
public Step step1() {
return
stepBuilderFactory.get("step1").<User, User>chunk(10).reader(reader()).processor(processor()).writer(writer()).build();
}
}
Next, we will
have to write a Job Completion Notification Listener class – used to notify
after the Job completion.
@Component
public class JobCompletionNotificationListener extends JobExecutionListenerSupport {
private static final Logger log = LoggerFactory.getLogger(JobCompletionNotificationListener.class);
private final JdbcTemplate jdbcTemplate;
@Autowired
public JobCompletionNotificationListener(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate =
jdbcTemplate;
}
@Override
public void afterJob(JobExecution jobExecution) {
if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
log.info("!!!
JOB FINISHED !! It's time to verify the results!!");
List<User> results =
jdbcTemplate.query(
"SELECT first_name, last_name FROM USERS", new RowMapper<User>() {
@Override
public User
mapRow(ResultSet rs, int row) throws SQLException {
return new User(rs.getString(1), rs.getString(2));
}
});
for (User person : results) {
log.info("Found
<" +
person + ">
in the database.");
}
}
}
}
Spring Boot-Apache Kafka
Apache Kafka is an open source project used to publish and subscribe
the messages based on the fault-tolerant messaging system. It is fast, scalable
and distributed by design.
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
Producing Messages
To produce messages into
Apache Kafka, we need to define the Configuration class for Producer
configuration as shown –
@Configuration
public class KafkaProducerConfig {
@Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object>
configProps = new HashMap<>();
configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
return new DefaultKafkaProducerFactory<>(configProps);
}
@Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
To publish a message, auto wire the
Kafka Template object and produce the message as shown.
@Autowired
private KafkaTemplate<String, String>
kafkaTemplate;
public void sendMessage(String
msg) {
kafkaTemplate.send(topicName,
msg);
}
Consuming a Message
To consume messages, we need to write a
Consumer configuration class file
@EnableKafka
@Configuration
public class KafkaConsumerConfig {
@Bean
public ConsumerFactory<String, String> consumerFactory() {
Map<String, Object> props
= new HashMap<>();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:2181");
props.put(ConsumerConfig.GROUP_ID_CONFIG, "group-id");
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
return new DefaultKafkaConsumerFactory<>(props);
}
@Bean
public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String>
factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());
return
factory;
}
}
Next, write a Listener to listen to
the messages.
@KafkaListener(topics = "tutorialspoint",
groupId = "group-id")
public void listen(String
message) {
System.out.println("Received
Messasge in group - group-id: " + message);
}
Let us call the sendMessage() method
from ApplicationRunner class run method from the main Spring Boot application
class file and consume the message from the same class file.
@SpringBootApplication
public class KafkaDemoApplication implements ApplicationRunner {
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
public void sendMessage(String msg) {
kafkaTemplate.send("tutorialspoint", msg);
}
public static void main(String[] args) {
SpringApplication.run(KafkaDemoApplication.class, args);
}
@KafkaListener(topics
= "tutorialspoint",
groupId = "group-id")
public void listen(String
message) {
System.out.println("Received Messasge in group - group-id: " + message);
}
@Override
public void run(ApplicationArguments args) throws Exception {
sendMessage("Hi
Welcome to Spring For Apache Kafka");
}
}
Spring Boot- Twilio
Twilio is a 3rd party
application used to send SMS and make voice calls from our application. It
allows us to send the SMS and make voice calls programmatically.
<dependency>
<groupId>com.twilio.sdk</groupId>
<artifactId>twilio</artifactId>
<version>7.16.1</version>
</dependency>
Now, initialize the Twilio account
with ACCOUNT_SID and AUTH_ID in static block as shown −
static {
Twilio.init(ACCOUNT_SID,
AUTH_ID);
}
Sending SMS
To send the SMS, we need
to provide a from-number and to-number to the Message.create() method. Message
body content also we need to provide for the method Message.creator()as shown –
Message.creator(new
PhoneNumber("to-number"), new PhoneNumber("from-number"),
"Message from Spring Boot Application").create();
Voice
Calls
To make voice calls by using Twilio, we need to call the
Call.creator() method. For this method, we need to provide a to-number,
from-number, and voice-note as shown here.
Call.creator(new
PhoneNumber("<to-number>"), new
PhoneNumber("<from-number>"),
new URI("http://demo.twilio.com/docs/voice.xml")).create();
Spring Boot – Unit Test Case
Unit Testing is
a one of the testing done by the developers to make sure individual unit or
component functionalities are working fine.
Mockito
For injecting Mockito Mocks
into Spring Beans, we need to add the Mockito-core dependency in our build
configuration file.
Maven users can add the following
dependency in your pom.xml file.
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Then,
configure the Application context for the tests. The @Profile(“test”)
annotation is used to configure the class when the Test cases are running.
package com.tutorialspoint.mockitodemo;
import org.mockito.Mockito;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
@Profile("test")
@Configuration
public class ProductServiceTestConfiguration {
@Bean
@Primary
public ProductService productService() {
return Mockito.mock(ProductService.class);
}
Now, you can
write a Unit Test case like below
@SpringBootTest
@ActiveProfiles("test")
@RunWith(SpringJUnit4ClassRunner.class)
public class MockitoDemoApplicationTests {
@Autowired
private OrderService orderService;
@Autowired
private ProductService productService;
@Test
public void whenUserIdIsProvided_thenRetrievedNameIsCorrect() {
Mockito.when(productService.getProductName()).thenReturn("Mock
Product Name");
String
testName = orderService.getProductName();
Assert.assertEquals("Mock
Product Name", testName);
}
}
Writing a Unit Test for REST Controller
First, we
need to create Abstract class file used to create web application context by
using MockMvc and define the mapToJson() and mapFromJson() methods to convert
the Java object into JSON string and convert the JSON string into Java object.
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes
= DemoApplication.class)
@WebAppConfiguration
public abstract class AbstractTest {
protected MockMvc mvc;
@Autowired
WebApplicationContext webApplicationContext;
protected void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
protected String mapToJson(Object obj) throws JsonProcessingException {
ObjectMapper
objectMapper = new ObjectMapper();
return
objectMapper.writeValueAsString(obj);
}
protected <T> T mapFromJson(String json, Class<T> clazz)
throws JsonParseException, JsonMappingException, IOException {
ObjectMapper
objectMapper = new ObjectMapper();
return
objectMapper.readValue(json, clazz);
}
}
Next, write a
class file that extends the AbstractTest class and write a Unit Test for each
method such GET, POST, PUT and DELETE.
@Test
public void getProductsList() throws Exception {
String uri = "/products";
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(uri)
.accept(MediaType.APPLICATION_JSON_VALUE)).andReturn();
int status = mvcResult.getResponse().getStatus();
assertEquals(200, status);
String content =
mvcResult.getResponse().getContentAsString();
Product[]
productlist = super.mapFromJson(content, Product[].class);
assertTrue(productlist.length
> 0);
}
The code for POST API test case is given
below. This API is to create a product.
@Test
public void createProduct() throws Exception {
String uri = "/products";
Product product = new Product();
product.setId("3");
product.setName("Ginger");
String inputJson = super.mapToJson(product);
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.post(uri)
.contentType(MediaType.APPLICATION_JSON_VALUE).content(inputJson)).andReturn();
int status = mvcResult.getResponse().getStatus();
assertEquals(201, status);
String content =
mvcResult.getResponse().getContentAsString();
assertEquals(content, "Product
is created successfully");
}
The code for PUT API Test case is given
below. This API is to update the existing product.
@Test
public void updateProduct() throws Exception {
String uri = "/products/2";
Product product = new Product();
product.setName("Lemon");
String inputJson = super.mapToJson(product);
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.put(uri)
.contentType(MediaType.APPLICATION_JSON_VALUE).content(inputJson)).andReturn();
int status = mvcResult.getResponse().getStatus();
assertEquals(200, status);
String content =
mvcResult.getResponse().getContentAsString();
assertEquals(content, "Product
is updated successsfully");
}
The code for Delete API Test case is
given below. This API will delete the existing product.
@Test
public void deleteProduct() throws Exception {
String uri = "/products/2";
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.delete(uri)).andReturn();
int status = mvcResult.getResponse().getStatus();
assertEquals(200, status);
String content =
mvcResult.getResponse().getContentAsString();
assertEquals(content, "Product
is deleted successsfully");
}
JDBCTemplate
To access the
Relational Database by using JdbcTemplate in Spring Boot application, we need
to add the Spring Boot Starter JDBC dependency in our build configuration file.
Then, if you
@Autowired the JdbcTemplate class, Spring Boot automatically connects the
Database and sets the Datasource for the JdbcTemplate object.
@Autowired
JdbcTemplate jdbcTemplate;
Collection<Map<String, Object>> rows = jdbc.queryForList("SELECT
QUERY");
The @Repository annotation should be added into the class file.
The @Repository annotation is used to create database repository for your
Spring Boot application.
@Repository
public class ProductServiceDAO {
}
Now, create a Configuration class to
create a DataSource and JdbcTemplate for multiple data sources.
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
@Configuration
public class DatabaseConfig {
@Bean(name = "dbProductService")
@ConfigurationProperties(prefix
= "spring.dbProductService")
@Primary
public DataSource createProductServiceDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "dbUserService")
@ConfigurationProperties(prefix
= "spring.dbUserService")
public DataSource createUserServiceDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "jdbcProductService")
@Autowired
public JdbcTemplate createJdbcTemplate_ProductService(@Qualifier("dbProductService") DataSource productServiceDS) {
return new JdbcTemplate(productServiceDS);
}
@Bean(name = "jdbcUserService")
@Autowired
public JdbcTemplate createJdbcTemplate_UserService(@Qualifier("dbUserService") DataSource userServiceDS) {
return new JdbcTemplate(userServiceDS);
}
}
Then, auto wire the JDBCTemplate object
by using @Qualifier annotation.
@Qualifier("jdbcProductService")
@Autowired
JdbcTemplate jdbcTemplate;
@Qualifier("jdbcUserService")
@Autowired
JdbcTemplate jdbcTemplate;
Spring Boot-Security
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
Now, in the main Spring Boot
application, add the @EnableAuthorizationServer and @EnableResourceServer
annotation to act as an Auth server and Resource Server in the same
application.
@SpringBootApplication
@EnableAuthorizationServer
@EnableResourceServer
@RestController
public class WebsecurityappApplication {
public static void main(String[] args) {
SpringApplication.run(WebsecurityappApplication.class, args);
}
@RequestMapping(value = "/products")
public String getProductName() {
return "Honey";
}
}
Use the following code to define the POJO
class to store the User information for authentication.
package com.tutorialspoint.websecurityapp;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
public class UserEntity {
private String username;
private String password;
private Collection<GrantedAuthority>
grantedAuthoritiesList = new ArrayList<>();
public String getPassword() {
return
password;
}
public void setPassword(String password) {
this.password =
password;
}
public Collection<GrantedAuthority>
getGrantedAuthoritiesList() {
return
grantedAuthoritiesList;
}
public void setGrantedAuthoritiesList(Collection<GrantedAuthority>
grantedAuthoritiesList) {
this.grantedAuthoritiesList =
grantedAuthoritiesList;
}
public String getUsername() {
return
username;
}
public void setUsername(String username) {
this.username =
username;
}
}
Now, use the following code and define the
CustomUser class that extends the org.springframework.security.core.userdetails.User
class for Spring Boot authentication.
package com.tutorialspoint.websecurityapp;
import org.springframework.security.core.userdetails.User;
public class CustomUser extends User {
private static final long serialVersionUID = 1L;
public CustomUser(UserEntity user) {
super(user.getUsername(), user.getPassword(), user.getGrantedAuthoritiesList());
}
}
You can
create the @Repository class to read the User information from the database and
send it to the Custom user service and also add the granted authority
“ROLE_SYSTEMADMIN”.
@Repository
public class OAuthDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public UserEntity getUserDetails(String username) {
Collection<GrantedAuthority>
grantedAuthoritiesList = new ArrayList<>();
String
userSQLQuery = "SELECT
* FROM USERS WHERE USERNAME=?";
List<UserEntity> list =
jdbcTemplate.query(userSQLQuery, new String[] { username },
(ResultSet rs, int rowNum) -> {
UserEntity user = new UserEntity();
user.setUsername(username);
user.setPassword(rs.getString("PASSWORD"));
return user;
});
if (list.size() > 0) {
GrantedAuthority
grantedAuthority = new SimpleGrantedAuthority("ROLE_SYSTEMADMIN");
grantedAuthoritiesList.add(grantedAuthority);
list.get(0).setGrantedAuthoritiesList(grantedAuthoritiesList);
return list.get(0);
}
return null;
}
}
You can create a Custom User detail
service class that extends the
org.springframework.security.core.userdetails.UserDetailsService to call the
DAO repository class as shown.
package com.tutorialspoint.websecurityapp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class CustomDetailsService implements UserDetailsService {
@Autowired
OAuthDao oauthDao;
@Override
public CustomUser loadUserByUsername(final String username) throws UsernameNotFoundException {
UserEntity
userEntity = null;
try {
userEntity =
oauthDao.getUserDetails(username);
CustomUser
customUser = new CustomUser(userEntity);
return
customUser;
} catch (Exception e) {
e.printStackTrace();
throw new UsernameNotFoundException("User
" +
username + "
was not found in the database");
}
}
}
Next, create a @configuration class to enable the Web
Security, defining the Password encoder (BCryptPasswordEncoder),
and defining the AuthenticationManager
bean. The Security configuration class should extend WebSecurityConfigurerAdapter class.
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled
= true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private CustomDetailsService customDetailsService;
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
@Override
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customDetailsService).passwordEncoder(encoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Spring Boot - Google OAuth2
First, add the Spring Boot
OAuth2 security dependency in your build configuration file and your build
configuration file
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
Now, add the HTTP Endpoint
to read the User Principal from the Google after authenticating via Spring Boot
in main Spring Boot application class file as given below –
@SpringBootApplication
@RestController
public class GoogleserviceApplication {
public static void main(String[] args) {
SpringApplication.run(GoogleserviceApplication.class, args);
}
@RequestMapping(value = "/user")
public Principal user(Principal principal) {
return
principal;
}
}
Now, write a Configuration
file to enable the OAuth2SSO for web security and remove the authentication for
index.html file
@Configuration
@EnableOAuth2Sso
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/index.html")
.permitAll()
.anyRequest()
.authenticated();
}
}
Next, add the index.html file under static
resources and add the link to redirect into user HTTP Endpoint to read the
Google user Principal as shown below −
<!DOCTYPE html>
<html>
<head>
<meta charset = "ISO-8859-1">
<title>Insert
title here</title>
</head>
<body>
<a href = "user">Click
here to Google Login</a>
</body>
</html>
Then, go the Credentials
section and create a credentials and choose OAuth Client ID.
Next, provide a Product Name
in OAuth2 consent screen.
Next, choose the Application
Type as “Web application”, provide the Authorized JavaScript origins and
Authorized redirect URIs.
Now, your OAuth2 Client Id
and Client Secret is created.
Next, add the Client Id and
Client Secret in your application properties file.
security.oauth2.client.clientId
= <CLIENT_ID>
security.oauth2.client.clientSecret
= <CLIENT_SECRET>
security.oauth2.client.accessTokenUri = https://www.googleapis.com/oauth2/v3/token
security.oauth2.client.userAuthorizationUri = https://accounts.google.com/o/oauth2/auth
security.oauth2.client.tokenName
= oauth_token
security.oauth2.client.authenticationScheme
= query
security.oauth2.client.clientAuthenticationScheme
= form
security.oauth2.client.scope =
profile email
security.oauth2.resource.userInfoUri = https://www.googleapis.com/userinfo/v2/me
security.oauth2.resource.preferTokenInfo
= false
Comments
Post a Comment