job-api
├── src
│ └── main
│ └── java
│ └── com/yourcompany/yourproject/jobapi
│ ├── controller
│ │ └── JobController.java
│ ├── service
│ │ ├── JobService.java
│ │ └── JobServiceImpl.java
│ ├── repository
│ │ └── JobRepository.java
│ ├── model
│ │ └── Job.java
│ ├── dto
│ │ ├── JobRequestDto.java
│ │ └── JobResponseDto.java
│ ├── exception
│ │ └── JobNotFoundException.java
│ └── util
│ └── JobConstants.java
│
└── test
└── java
└── com/yourcompany/yourproject/jobapi
├── controller
│ └── JobControllerTest.java
├── service
│ └── JobServiceTest.java
└── repository
└── JobRepositoryTest.java
Annotation | Usage | Level |
---|---|---|
@Controller | Handle req/res. returns string(view name) used by view resolver | Class |
@ResponseBody | Converts object to Json/Xml using jackson library | Method |
@RestController | combo of @Controller + @ResponseBody | Class |
@RequestMapping | maps html request to methods, based on Httpmethods,content type etc | Method |
@RequestBody | data from http body, @RequestBody User user |
Argument |
@RequestHeader | data from http header, @RequestHeader("Authorization") String auth |
Argument |
@RequestParam | data from http requestparam | Argument |
@PathVariable | data from http url, used in Spring MVC | Argument |
@PathParam | DONT USE. data from http url, used in JAX-RS framework | Argument |
@CookieValue | data from http cookies @CookieValue(value = "uname") String name |
Argument |
@CrossOrigin | enables Cross-Origin Resource Sharing (CORS) | Method |
@GetMapping | shortcut for @RequestMapping(method = RequestMethod.GET) | Method |
@PostMapping | shortcut for @RequestMapping(method = RequestMethod.POST) | Method |
@PutMapping | shortcut for @RequestMapping(method = RequestMethod.PUT) | Method |
@DeleteMapping | shortcut for @RequestMapping(method = RequestMethod.DELETE) | Method |
@PatchMapping | shortcut for @RequestMapping(method = RequestMethod.PATCH) | Method |
@RequestMapping(value = "/hello", method = RequestMethod.GET, params = "name", headers = "Content-Type=application/json", consumes = "application/json", produces = "application/json", name = "HelloEndpoint")
Annotation | Usage | Level |
---|---|---|
@ControllerAdvice | Defines global exception handlers class, model attributes, data binding | Class |
@RestControllerAdvice | Combines @ControllerAdvice and @ResponseBody functionalities. | Class |
@ExceptionHandler | handle exceptions thrown by controllers. | Method |
@ResponseStatus | Sets the HTTP status code for the response, while exception | Method |
Annotation | Usage | Level |
---|---|---|
@Validated | indicate that the class should be subject to validation constraints. | class |
@Valid | @Valid @RequestParam("name") String name . throws MethodArgumentNotValidException |
Argument |
@NotNull | Ensures field is not null. @NotNull(message = "Name cannot be null") |
Field |
@NotBlank | ensure field is not blank “” @NotBlank(message = "Name cannot be blank") |
Field |
@NotEmpty | Ensures field is not null or empty.@NotEmpty(message = "Name cannot be empty") |
Field |
@Size | Validates field’s size @Size(min = 2, max = 30, message = "invalid") |
Field |
@Min | Ensures field value more than given value @Min(value = 18, message = "invalid" |
Field |
@Max | Ensures field value less than given value @Max(value = 18, message = "invalid" |
Field |
Ensure field has valid email format @Email(message = "Email should be valid") |
Field | |
@Pattern | Ensure field matches specified regex @Pattern(regexp = "^[a-z]+$", message = "Usernam |
Field |
@Past | Ensure date is less then today @Past(message = "invalid") |
Field |
@Future | Ensure date is more then today @Future(message = "invalid") |
Field |
@Positive | number should be positive @Positive(message = "invalid") |
Field |
@Negative | number should be negative @Negative(message = "invalid") |
Field |
@PositiveOrZero | number should be positive or zero @PositiveOrZero(message = "invalid") |
Field |
@NegativeOrZero | number should be negative or zero @NegativeOrZero(message = "invalid") |
Field |
// Employee.java
package com.example.employee;
import javax.persistence.*;
import lombok.*;
@Entity
@Data //combines @Getter, @Setter, @ToString, @RequiredArgsConstructor and @EqualsAndHashCode
@Builder //provide builder patter for class //User.builder().name("John").email("Doe@gmail.com").build();
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
public EmployeeDto toDto() {
return EmployeeDto.builder()
.id(this.id)
.name(this.name)
.email(this.email)
.build();
}
}
/ EmployeeController.java
package com.example.employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/employees")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@GetMapping("/{id}")
public ResponseEntity<Employee> getEmployeeById(@PathVariable Long id) {
return new ResponseEntity<>(employeeService.getEmployeeById(id), HttpStatus.OK);
}
}
// EmployeeService.java
package com.example.employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
public Employee getEmployeeById(Long id) {
return employeeRepository.findById(id)
.map(Employee::toDto)
.orElseThrow(() -> new EmployeeNotFoundException("Employee not found with id: " + id));
}
}
// EmployeeRepository.java
package com.example.employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
// EmployeeControllerTest.java
package com.example.employee;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@ExtendWith(MockitoExtension.class)
public class EmployeeControllerTest {
@InjectMocks
private EmployeeController employeeController;
@Mock
private EmployeeService employeeService;
@Test
public void testGetEmployeeById_success() {
Employee employee = Employee.builder().id(1L).name("John Doe").email("john.doe@example.com").build();
when(employeeService.getEmployeeById(1L)).thenReturn(employee);
ResponseEntity<Employee> response = employeeController.getEmployeeById(1L);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals(employee, response.getBody());
}
@Test
public void testGetEmployeeById_notFound() {
when(employeeService.getEmployeeById(1L)).thenThrow(new EmployeeNotFoundException("Employee not found with id: 1"));
assertThrows(EmployeeNotFoundException.class, () -> employeeController.getEmployeeById(1L));
}
}
// EmployeeDto.java
package com.example.employee;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EmployeeDto {
private Long id;
private String name;
private String email;
}
package com.example.employee;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(EmployeeNotFoundException.class)
public ResponseEntity<Object> handleEmployeeNotFoundException(EmployeeNotFoundException ex, WebRequest request) {
Map<String, Object> body = new HashMap<>();
body.put("message", ex.getMessage());
return new ResponseEntity<>(body, HttpStatus.NOT_FOUND);
// return handleExceptionInternal(ex, body, new HttpHeaders(), HttpStatus.NOT_FOUND, request);
}
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error -> {
errors.put(error.getField(), error.getDefaultMessage());
});
return handleExceptionInternal(ex, errors, headers, HttpStatus.BAD_REQUEST, request);
}
@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
Map<String, Object> body = new HashMap<>();
body.put("message", "Invalid request body");
return handleExceptionInternal(ex, body, headers, HttpStatus.BAD_REQUEST, request);
}
}
@Constraint(validatedBy = MyCustomValidator.class)
@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomConstraint {
String message() default "Invalid value";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class MyCustomValidator implements ConstraintValidator<MyCustomConstraint, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
// Custom validation logic
if (value == null) {
return false;
}
}
}
@NotNull
@MyCustomConstraint
private String myField;
Two types of Response @RestController:
1.Add Jackdon-bind pom.xml, just return object list
2.use ResponseEntity<> class - ex: return new ResponseEntity<>(ResBody,HttpCode);
File upload:
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(@FormDataParam("file") InputStream uploadedInputStream,@FormDataParam("file") FormDataContentDisposition fileDetail){}
1. Configure pom.xml: add dependency spring-webmvc
2. Configure web.xml
<web-app>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- servelet declaration -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.dispatcher-servlet.xml</servlet-class>
</servlet>
<!-- servelet declaration, doing servlet mapping -->
<servlet-mapping>
<!-- servlet-name + (-sevlet.xml) will get search inside WEB-INF folder-->
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
<!-- if >=0 it's create while deployed in server, if <0 then it'll created while someone try to access-->
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/customised-frontcontroller-name.xml</param-value>
</init-param>
</servlet-mapping>
<init-param>
<param-value></param-value>
<param-value></param-value>
</init-param>
</web-app>
2a.DispatcherServletInitializer.java add pom.xml dependency servlet-api
public class DispatcherServletInitializer implments abstractAnnotationConfigDispatcherServletInitializer{
@Override
protected Class<?>[] getRootConfigClasses(){
return null;
}
@Override
protected Class<?>[] getServletConfigClasses(){
return new Class[]{DispatcherServlet.class};
}
@Override
protected String[] getServletMapping(){
return new String[]{"/"};
}
}
3. Configure dispatcher-servlet.xml
<beans>
<context:component-scan base-package="com.controller">
<mvc:annotation-driven>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/">
<property name="suffix" value=".jsp">
</bean>
</beans>
3a. DispatcherServlet.java
@Configuration
@EnableWebMvc //it is <mvc:annotaion-driven/>
@ComponentScan(basePackages="org.myApp")
public class DispatcherServlet(){
@Bean
public ViewResolver viewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.prefix("WEB-INF/view/");
viewResolver.suffix(".jsp");
return viewSolver;
}
}
4. Create controller class
@Controller
public class HelloController {
@RequestMapping("/display")
public String display()
{
return "index";
}
}
HANDLER MAPPING:- Helps to choose controller
VIEW RESOLVER:- Maps view name to actual views
Model interface:
public String display(Model m){
m.addAllAttributes(Collection<?> arg);
m.addAttribute("attributeName",attributeValue);
return "pageName";
}
ModelAndView class:
public ModelAndView display(){
ModelAndView m = new ModelAndView("pageName");
m.addObject("objectName",objValue);
return m;
}
@RequestMapping(value={"/display","/show"}) //handles multiple url
@RequestMapping(method = RequestMethod.GET) //handles based on http get,post,delete,put,patch
@RequestMapping() //handles default url
@GetMapping(value=".display") //shortcuts. also use PostMapping,PutMapping,DeleteMapping,PatchMapping
@RequestMapping(value = "user")
String display(@RequestParam("id") String personId) //id will come in post parameters
String display(@RequestParam(value="id",required = false, defaultValue = "John") String personId)
@RequestMapping(value = "user/{id}")
String display(@PathVariable("id") String personId)
@SessionAttribute:
@Qualifier(“beanName”): avoid ambiguity
A Java class that uses annotations to configure and manage Spring beans and their dependencies, providing a type-safe and more maintainable alternative to XML-based configuration.
Add maven dependency
Create a class that extends AbstractAnnotationConfigDispatcherServletInitializer to replace web.xml. ```java public class DispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override protected Class<?>[] getRootConfigClasses() { return null; }
@Override protected Class<?>[] getServletConfigClasses() { return new Class[]{WebConfig.class}; }
@Override protected String[] getServletMappings() { return new String[]{“/”}; } }
3. Configure dispatcherservelet
```java
@Configuration
@EnableWebMvc // Equivalent to <mvc:annotation-driven/>
@ComponentScan(basePackages = "com.controller") // Scans for components (e.g., @Controller)
public class WebConfig implements WebMvcConfigurer {
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
@Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setDriverClassName("com.mysql.cj.jdbc.Driver");
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydatabase");
config.setUsername("myuser");
config.setPassword("mypassword");
config.setMaximumPoolSize(10);
return new HikariDataSource(config);
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
remainings steps are same