Actuator exposes health and metrics endpoints for operations. The session builds full CRUD REST APIs twice, once with annotations and once with XML configuration, then puts Spring Security’s default login in front of them.
Objectives
Do not copy. Read for understanding and the viva- Complete questions 33 to 36 of the manual: rest api, spring security and actuator
- Prepare the deliverable before the lab and finish it during the session
- Be ready to explain every step in the viva
Questions Covered
Do not copy. Read for understanding and the viva| Question | Requirement | Status |
|---|---|---|
| Q33 | Add actuator dependency in Spring boot and Test Actuator (all available Endpoints) | Complete |
| Q34 | Create REST API for CRUD operation using Spring Boot and Hibernate/JPA using annotation | Complete |
| Q35 | Create a REST API for CRUD operation using Spring Boot and Hibernate/JPA using XML… | Complete |
| Q36 | Add Spring Security dependency in the existing spring boot application created in… | Complete |
Preparation
Do not copy. Read for understanding and the viva- Actuator endpoints:
/actuator/health,/actuator/info,/actuator/metrics; expose them withmanagement.endpoints.web.exposure.include=*for the lab. - REST CRUD maps HTTP verbs: POST create, GET read, PUT update, DELETE delete. Test each with curl or Postman and keep the request and response in the record.
- Adding spring-boot-starter-security alone protects every URL with a generated password printed in the console.
This session continues the admission-api project from Session 7. Listings marked “replaces” overwrite the Session 7 file of the same name. Nothing was executed here; every listing and every expected response was checked by reading against Spring Boot 3.3.
Question 33
Problem Statement
Write in lab recordAdd actuator dependency in Spring boot and Test Actuator (all available Endpoints).
Solution
Write in lab recordSteps
- Confirm
spring-boot-starter-actuatoris inpom.xml(added in Question 29). If not, paste the block below inside<dependencies>and run Maven, Update Project. - Replace
application.propertieswith the listing: it keeps the Session 7 settings and adds themanagement.*andinfo.*keys. - Restart. Open
http://localhost:8080/actuatorin a browser; the JSON lists every exposed endpoint with its URL. - Call each endpoint with curl and paste the responses into the record.
Configuration
The dependency block, for reference:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency># src/main/resources/application.properties (Session 8: Session 7 settings + Actuator, Q33)
spring.application.name=admission-api
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/admission_db?createDatabaseIfNotExist=true&serverTimezone=Asia/Kolkata
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.defer-datasource-initialization=true
spring.sql.init.mode=always
# --- Actuator (Q33) ---
# By default only /actuator/health is exposed over HTTP. Expose every endpoint for the lab.
management.endpoints.web.exposure.include=*
# show the db and diskSpace components, not just {"status":"UP"}
management.endpoint.health.show-details=always
# allow shutdown via POST /actuator/shutdown (disabled by default)
management.endpoint.shutdown.enabled=true
# /actuator/info reads info.* properties only when env info is enabled
management.info.env.enabled=true
info.app.name=admission-api
info.app.course=MCSL-222 Web Technologies Lab
info.app.version=0.0.1Output
Checked by reading. The discovery page lists the endpoints this project exposes (Boot 3.3, no Prometheus, no Flyway, no logging.file):
curl -s http://localhost:8080/actuator{"_links":{"self":{"href":"http://localhost:8080/actuator","templated":false},
"beans":{"href":"http://localhost:8080/actuator/beans","templated":false},
"health":{"href":"http://localhost:8080/actuator/health","templated":false},
"health-path":{"href":"http://localhost:8080/actuator/health/{*path}","templated":true},
"info":{"href":"http://localhost:8080/actuator/info","templated":false},
"conditions":{"href":"http://localhost:8080/actuator/conditions","templated":false},
"configprops":{"href":"http://localhost:8080/actuator/configprops","templated":false},
"env":{"href":"http://localhost:8080/actuator/env","templated":false},
"loggers":{"href":"http://localhost:8080/actuator/loggers","templated":false},
"heapdump":{"href":"http://localhost:8080/actuator/heapdump","templated":false},
"threaddump":{"href":"http://localhost:8080/actuator/threaddump","templated":false},
"metrics":{"href":"http://localhost:8080/actuator/metrics","templated":false},
"sbom":{"href":"http://localhost:8080/actuator/sbom","templated":false},
"scheduledtasks":{"href":"http://localhost:8080/actuator/scheduledtasks","templated":false},
"mappings":{"href":"http://localhost:8080/actuator/mappings","templated":false},
"shutdown":{"href":"http://localhost:8080/actuator/shutdown","templated":false}}}Each endpoint, what it returns, and the command used:
| Endpoint | Command | Expected response (shortened) |
|---|---|---|
| health | curl -s localhost:8080/actuator/health | "status":"UP" with db (MySQL, isValid()), diskSpace (total, free, threshold) and ping components |
| info | curl -s localhost:8080/actuator/info | "app": with name, course and version from the info.* properties |
| metrics | curl -s localhost:8080/actuator/metrics | "names":[...] about 60 metric names: jvm.memory.used, http.server.requests, hikaricp.connections.active, system.cpu.usage, … |
| metrics by name | curl -s localhost:8080/actuator/metrics/jvm.memory.used | "measurements":[ with "statistic":"VALUE","value":1.2e8 and availableTags for area and id |
| env | curl -s localhost:8080/actuator/env/server.port | "property": with "value":"8080" and source Config resource 'class path resource [application.properties]' |
| beans | curl -s localhost:8080/actuator/beans | Every bean in the context with its scope, type and dependencies, including studentRestController and studentRepository |
| mappings | curl -s localhost:8080/actuator/mappings | dispatcherServlets with a handler entry per method, for example GET /api/students/[id] |
| loggers | curl -s localhost:8080/actuator/loggers/in.ignou | "configuredLevel":null,"effectiveLevel":"INFO" |
| configprops | curl -s localhost:8080/actuator/configprops | Bound @ConfigurationProperties such as spring.datasource (password masked as ******) |
| conditions | curl -s localhost:8080/actuator/conditions | positiveMatches and negativeMatches of every auto-configuration |
| threaddump | curl -s localhost:8080/actuator/threaddump | Array of threads with name, state and stack |
| heapdump | curl -o heap.hprof localhost:8080/actuator/heapdump | Binary .hprof file |
| scheduledtasks, sbom | curl -s localhost:8080/actuator/scheduledtasks | Empty lists (nothing scheduled; no SBOM file in the jar). caches is absent because no CacheManager bean exists |
| shutdown | curl -s -X POST localhost:8080/actuator/shutdown | "message":"Shutting down, bye..." and the process exits |
Two full responses worth copying:
curl -s http://localhost:8080/actuator/health{"status":"UP","components":{
"db":{"status":"UP","details":{"database":"MySQL","validationQuery":"isValid()"}},
"diskSpace":{"status":"UP","details":{"total":499963174912,"free":201524125696,"threshold":10485760,"path":"/home/mca/admission-api/.","exists":true}},
"ping":{"status":"UP"}}}curl -s http://localhost:8080/actuator/info{"app":{"name":"admission-api","course":"MCSL-222 Web Technologies Lab","version":"0.0.1"}}Change a log level without restarting:
curl -s -X POST -H "Content-Type: application/json" -d '{"configuredLevel":"DEBUG"}' \
http://localhost:8080/actuator/loggers/in.ignou.admissionResponse: HTTP 204, then the console starts printing DEBUG lines from the project package.
Explanation
Actuator registers each endpoint as a bean; the management.endpoints.web.exposure.include=* line maps all of them under /actuator. Without that line only health is reachable over HTTP, which is the safe default because env, beans and heapdump leak configuration and memory. show-details=always makes health include the db component, which runs Connection.isValid() on a pooled connection; that is the check operations teams poll. info is empty in Boot 3 until management.info.env.enabled=true and some info.* keys exist. shutdown is the only endpoint disabled by default; it needs management.endpoint.shutdown.enabled=true and a POST. After Session 9 the whole /actuator tree sits behind login except /actuator/health.
Question 34
Problem Statement
Write in lab recordCreate REST API for CRUD operation using Spring Boot and Hibernate/JPA using annotation.
Solution
Write in lab recordSteps
- Replace
StudentRestController.javafrom Session 7 with the listing below; it keeps the two GET methods and adds POST, PUT and DELETE. - Restart with DevTools (saving the file is enough).
- Run the five curl calls in order; the ids in the later calls come from the POST response.
- Verify in MySQL after each step:
SELECT * FROM student;.
Program
// src/main/java/in/ignou/admission/web/StudentRestController.java (Session 8, Q34: full CRUD)
package in.ignou.admission.web;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import in.ignou.admission.entity.Student;
import in.ignou.admission.repository.StudentRepository;
/**
* Annotation-based REST CRUD.
* POST /api/students create -> 201 + saved student
* GET /api/students read -> 200 + list
* GET /api/students/{id} read -> 200 or 404
* PUT /api/students/{id} update -> 200 or 404
* DELETE /api/students/{id} delete -> 204 or 404
*/
@RestController
@RequestMapping("/api/students")
public class StudentRestController {
private final StudentRepository students;
public StudentRestController(StudentRepository students) {
this.students = students;
}
/** @RequestBody: Jackson turns the JSON body into a Student; id is ignored and generated. */
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Student create(@RequestBody Student student) {
student.setId(null);
return students.save(student);
}
@GetMapping
public List<Student> all(@RequestParam(required = false) String city) {
return city == null ? students.findAll() : students.findByCityIgnoreCase(city);
}
@GetMapping("/{id}")
public ResponseEntity<Student> one(@PathVariable Long id) {
return students.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
/** Copy the editable fields onto the managed entity, then save (an UPDATE, because id is set). */
@PutMapping("/{id}")
public ResponseEntity<Student> update(@PathVariable Long id, @RequestBody Student in) {
return students.findById(id).map(s -> {
s.setName(in.getName());
s.setEmail(in.getEmail());
s.setPhone(in.getPhone());
s.setCity(in.getCity());
s.setDateOfBirth(in.getDateOfBirth());
return ResponseEntity.ok(students.save(s));
}).orElse(ResponseEntity.notFound().build());
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
if (!students.existsById(id)) {
return ResponseEntity.notFound().build();
}
students.deleteById(id);
return ResponseEntity.noContent().build();
}
}Output
Checked by reading. Create:
curl -i -X POST http://localhost:8080/api/students \
-H "Content-Type: application/json" \
-d '{"name":"Meera Nair","email":"meera@example.com","phone":"9000011111","city":"Kochi","dateOfBirth":"2003-01-25"}'HTTP/1.1 201
Content-Type: application/json
{"id":3,"name":"Meera Nair","email":"meera@example.com","phone":"9000011111","city":"Kochi","dateOfBirth":"2003-01-25"}Read all and read one:
curl -s http://localhost:8080/api/students
curl -s http://localhost:8080/api/students/3[{"id":1,"name":"Asha Verma",...},{"id":2,"name":"Ravi Kumar",...},{"id":3,"name":"Meera Nair",...}]
{"id":3,"name":"Meera Nair","email":"meera@example.com","phone":"9000011111","city":"Kochi","dateOfBirth":"2003-01-25"}Update (full replacement of the editable fields):
curl -s -X PUT http://localhost:8080/api/students/3 \
-H "Content-Type: application/json" \
-d '{"name":"Meera Nair","email":"meera.nair@example.com","phone":"9000011111","city":"Thrissur","dateOfBirth":"2003-01-25"}'{"id":3,"name":"Meera Nair","email":"meera.nair@example.com","phone":"9000011111","city":"Thrissur","dateOfBirth":"2003-01-25"}Delete, then read again:
curl -i -X DELETE http://localhost:8080/api/students/3
curl -i http://localhost:8080/api/students/3HTTP/1.1 204
HTTP/1.1 404Error cases: a duplicate email on POST violates the unique key and returns HTTP 500 with DataIntegrityViolationException in the console; a body that is not JSON returns HTTP 400 (HttpMessageNotReadableException).
Explanation
Each HTTP verb has its own annotation, all shortcuts for @RequestMapping(method = ...). @RequestBody asks Jackson to build a Student from the request JSON; student.setId(null) guarantees an INSERT even if the client sent an id. @ResponseStatus(CREATED) sets 201 for the create path where the body is always present. PUT loads the managed entity, copies the fields, and calls save; because the entity has an id, Hibernate issues an UPDATE. DELETE checks existsById first so a missing id is 404 rather than a silent 204. Every method goes through the same JpaRepository, so the controller has no SQL and no session handling of its own; Boot’s OpenEntityManagerInViewInterceptor and the repository’s @Transactional methods do that.
Question 35
Problem Statement
Write in lab recordCreate a REST API for CRUD operation using Spring Boot and Hibernate/JPA using XML configuration.
Solution
Write in lab recordSteps
- Create a new package
in.ignou.xmlapibeside (not inside)in.ignou.admission. Component scanning does not reach it, so nothing in it becomes a bean unless XML says so. - Add
CourseService(a plain class) andCourseRestControllerto that package. - Create
src/main/resources/beans.xmlwith the two<bean>definitions. - Replace
AdmissionApiApplication.javawith the version that carries@ImportResource("classpath:beans.xml"). - Restart and test
/xml/courseswith the curl commands. - Prove the wiring is XML: comment out the
courseRestControllerbean inbeans.xml, restart, andGET /xml/coursesreturns 404.
Program
Lab record: every tab is one file of the answer. Write all of them.
// src/main/java/in/ignou/xmlapi/CourseService.java (Session 8, Q35)
// NOTE the package: in.ignou.xmlapi is OUTSIDE in.ignou.admission, so component scanning
// never sees these classes. Only beans.xml creates them.
package in.ignou.xmlapi;
import java.util.List;
import java.util.Optional;
import in.ignou.admission.entity.Course;
import in.ignou.admission.repository.CourseRepository;
/** Plain class: no @Service, no @Autowired. beans.xml supplies the repository through the constructor. */
public class CourseService {
private final CourseRepository courses;
public CourseService(CourseRepository courses) {
this.courses = courses;
}
public Course create(Course c) {
c.setId(null);
return courses.save(c);
}
public List<Course> all() {
return courses.findAll();
}
public Optional<Course> find(Long id) {
return courses.findById(id);
}
public Optional<Course> update(Long id, Course in) {
return courses.findById(id).map(c -> {
c.setCode(in.getCode());
c.setTitle(in.getTitle());
c.setCredits(in.getCredits());
c.setProgramme(in.getProgramme());
return courses.save(c);
});
}
public boolean delete(Long id) {
if (!courses.existsById(id)) {
return false;
}
courses.deleteById(id);
return true;
}
}// src/main/java/in/ignou/xmlapi/CourseRestController.java (Session 8, Q35)
package in.ignou.xmlapi;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import in.ignou.admission.entity.Course;
/**
* The bean is declared in beans.xml, not found by scanning. @RestController stays,
* because Spring MVC 6 only treats @Controller-annotated beans as request handlers.
*/
@RestController
@RequestMapping("/xml/courses")
public class CourseRestController {
private final CourseService service;
public CourseRestController(CourseService service) {
this.service = service;
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Course create(@RequestBody Course course) {
return service.create(course);
}
@GetMapping
public List<Course> all() {
return service.all();
}
@GetMapping("/{id}")
public ResponseEntity<Course> one(@PathVariable Long id) {
return service.find(id).map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
}
@PutMapping("/{id}")
public ResponseEntity<Course> update(@PathVariable Long id, @RequestBody Course course) {
return service.update(id, course).map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
return service.delete(id) ? ResponseEntity.noContent().build() : ResponseEntity.notFound().build();
}
}<?xml version="1.0" encoding="UTF-8"?>
<!-- src/main/resources/beans.xml : XML wiring for the Course CRUD API (Session 8, Q35) -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- courseRepository already exists: Spring Data registered it from the interface name.
Constructor injection by reference, same as @Autowired would do. -->
<bean id="courseService" class="in.ignou.xmlapi.CourseService">
<constructor-arg ref="courseRepository"/>
</bean>
<bean id="courseRestController" class="in.ignou.xmlapi.CourseRestController">
<constructor-arg ref="courseService"/>
</bean>
</beans>// src/main/java/in/ignou/admission/AdmissionApiApplication.java (Session 8: + @ImportResource)
package in.ignou.admission;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
@SpringBootApplication
@ImportResource("classpath:beans.xml") // Q35: load the XML-defined beans into the same context
public class AdmissionApiApplication {
public static void main(String[] args) {
SpringApplication.run(AdmissionApiApplication.class, args);
}
}Output
Checked by reading. A course carries its programme, so the JSON nests it:
curl -s http://localhost:8080/xml/courses[{"id":1,"code":"MCS-221","title":"Data Warehousing and Data Mining","credits":4,"programme":{"id":1,"code":"MCA","name":"Master of Computer Applications","durationYears":2}},
{"id":2,"code":"MCSL-222","title":"Web Technologies Lab","credits":2,"programme":{"id":1,"code":"MCA","name":"Master of Computer Applications","durationYears":2}},
{"id":3,"code":"BCS-011","title":"Computer Basics and PC Software","credits":3,"programme":{"id":2,"code":"BCA","name":"Bachelor of Computer Applications","durationYears":3}}]Create (only the programme id is needed; Hibernate resolves the reference):
curl -i -X POST http://localhost:8080/xml/courses -H "Content-Type: application/json" \
-d '{"code":"MCS-224","title":"Artificial Intelligence and Machine Learning","credits":4,"programme":{"id":1}}'HTTP/1.1 201
{"id":4,"code":"MCS-224","title":"Artificial Intelligence and Machine Learning","credits":4,"programme":{"id":1,"code":null,"name":null,"durationYears":0}}The nested programme shows nulls in the POST response because the request only carried the id; GET /xml/courses/4 right after returns the full programme.
Read one, update, delete:
curl -s http://localhost:8080/xml/courses/4
curl -s -X PUT http://localhost:8080/xml/courses/4 -H "Content-Type: application/json" \
-d '{"code":"MCS-224","title":"AI and Machine Learning","credits":4,"programme":{"id":1}}'
curl -i -X DELETE http://localhost:8080/xml/courses/4{"id":4,"code":"MCS-224","title":"Artificial Intelligence and Machine Learning","credits":4,"programme":{"id":1,"code":"MCA","name":"Master of Computer Applications","durationYears":2}}
{"id":4,"code":"MCS-224","title":"AI and Machine Learning","credits":4,"programme":{"id":1,"code":null,"name":null,"durationYears":0}}
HTTP/1.1 204Start-up evidence that the XML was read:
Loading XML bean definitions from class path resource [beans.xml]Explanation
Annotation configuration and XML configuration produce the same thing, bean definitions in one ApplicationContext. @ImportResource reads beans.xml into the Boot context, so an XML bean can reference courseRepository, which Spring Data registered from the interface. Constructor injection in XML is <constructor-arg ref="..."/>; the classes have no @Service, @Component or @Autowired. The controller keeps @RestController and @GetMapping for one reason: since Spring 6, RequestMappingHandlerMapping accepts a bean as a handler only when its class is annotated with @Controller, and the URL-to-method table can only be declared with mapping annotations. What XML controls is the bean’s existence and its dependencies, which is exactly why step 6 makes the endpoint disappear. Putting the classes in in.ignou.xmlapi avoids a BeanDefinitionOverrideException: if they sat under in.ignou.admission, scanning and XML would both try to register courseRestController.
Question 36
Problem Statement
Write in lab recordAdd Spring Security dependency in the existing spring boot application created in Exercise 2 of Session 8 and configure it as the default login.
Solution
Write in lab recordSteps
- Paste the dependency below into
pom.xmland run Maven, Update Project. - Restart. Copy the generated password from the console line
Using generated security password:. - Open
http://localhost:8080/api/studentsin the browser. Spring redirects to/login, its built-in page with Username, Password and a Sign in button. - Log in as
userwith the generated password; the browser is redirected back to/api/studentsand the JSON appears. - Test from curl with HTTP Basic (
-u user:password). - Optional: pin the credentials in
application.propertiesso they survive restarts (spring.security.user.name=admin,spring.security.user.password=admin123).
Configuration
<!-- Session 8, Q36: add inside <dependencies> of pom.xml, then Maven > Update Project -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>Output
Checked by reading. Console at start-up (the UUID changes every run):
Using generated security password: 6a1b7f5c-9d2e-4c3a-8b0f-1e2d3c4b5a69
This generated password is for development use only. Your security configuration must be updated before running your application in production.Browser: any URL now shows Spring’s default login page, a centred form titled “Please sign in” with Username, Password and a blue “Sign in” button. A wrong password shows “Bad credentials” above the form. After login, the original URL loads.
curl without credentials, then with:
curl -i http://localhost:8080/api/studentsHTTP/1.1 401
WWW-Authenticate: Basic realm="Realm"
Set-Cookie: JSESSIONID=...; Path=/; HttpOnlycurl -s -u user:6a1b7f5c-9d2e-4c3a-8b0f-1e2d3c4b5a69 http://localhost:8080/api/students[{"id":1,"name":"Asha Verma",...},{"id":2,"name":"Ravi Kumar",...}]Writes are blocked even with the right password, because the default chain also turns on CSRF:
curl -i -u user:6a1b7f5c-9d2e-4c3a-8b0f-1e2d3c4b5a69 -X DELETE http://localhost:8080/api/students/2HTTP/1.1 403Session 9 fixes that by excluding /api/** from CSRF in the custom SecurityFilterChain.
Explanation
Adding the starter triggers SecurityAutoConfiguration. With no SecurityFilterChain bean of our own, Boot installs its default: every request must be authenticated, form login at /login and HTTP Basic are both enabled, CSRF protection is on, and an InMemoryUserDetailsManager holds one user named user with a random UUID password printed once. Actuator endpoints are covered too, which is why /actuator/health also asks for a login now. The WWW-Authenticate: Basic header on the 401 is what lets curl’s -u work; browsers get a 302 to /login instead because Spring detects the Accept: text/html header. The point of the exercise is to see the whole surface locked down with zero code; Session 9 replaces each default with a database-backed one.
Viva Questions
Do not copy. Read for understanding and the viva- Q: Which Actuator endpoint is exposed over HTTP by default and why only that one? A:
health; the others (env,beans,heapdump) reveal configuration and memory, so they are opt-in. - Q: What does
management.endpoint.health.show-details=alwayschange? A: The response includes thedb,diskSpaceandpingcomponents instead of a bare status. - Q: Which HTTP status should POST, DELETE and a missing id return? A: 201 Created, 204 No Content, 404 Not Found.
- Q: How does
savedecide between INSERT and UPDATE? A: If the entity id is null it persists (INSERT); otherwise it merges (UPDATE). - Q: Why does the XML-configured controller still carry
@RestController? A: Spring MVC 6 only treats@Controller-annotated beans as handlers; XML supplies the bean and its dependencies, not the URL mappings. - Q: What would happen if
CourseServicewere also annotated@Serviceinside the scanned package? A: Two definitions of the same bean name; Boot refuses to start withBeanDefinitionOverrideException. - Q: Where does the generated security password come from? A:
UserDetailsServiceAutoConfigurationcreates an in-memory useruserwith a random UUID when noUserDetailsServicebean exists. - Q: Why does curl get 401 but the browser gets a redirect to
/login? A: The default chain enables both HTTP Basic and form login and picks the response by the request’sAcceptheader.
Common Mistakes
Do not copy. Read for understanding and the viva- Testing
/actuator/metricswithout the exposure property and reporting “Actuator is broken” when the 404 only means the endpoint is not exposed. - Sending JSON without
Content-Type: application/json; Spring answers 415 Unsupported Media Type. - Treating PUT as a partial update; a body with a missing field overwrites that column with null.
- Placing the XML-wired classes inside
in.ignou.admissionand getting duplicate-bean errors, or forgetting@ImportResourceand getting 404 on/xml/courses. - Copying the generated password from an old console; it changes on every restart.
- Expecting
DELETEthrough curl to work right after adding the security starter; CSRF blocks it until the chain is customised.
Session Summary
Write in lab recordapplication.propertieswith Actuator exposure, health details andinfo.*keys, plus the/actuatordiscovery JSON and the endpoint table with one curl per endpointStudentRestControllerwith POST, GET, PUT and DELETE and the five curl transcripts (201, 200, 200, 204, 404)CourseService,CourseRestController,beans.xmland the@ImportResourceapplication class, with the/xml/coursestranscripts- The security dependency, the generated-password console line, the default login page description and the 401/403 curl results