Dependency injection is how Spring wires objects together. Controllers map URLs to methods and hand data to views. The session ends with a Spring Form for student admission, the form that Sessions 5 and 6 validate and persist.
Everything here goes into the student-admission project from Session 3. Q17 and Q18 are console programs in the ioc package; Q19 and Q20 add controllers and JSP views to the web application.
Objectives
Do not copy. Read for understanding and the viva- Complete questions 17 to 20 of the manual: dependency injection and controllers
- 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 |
|---|---|---|
| Q17 | Write a class and implement it using dependency injection | Complete |
| Q18 | Write the service interface with the getRandom() method, define 3 courses in an array,… | Complete |
| Q19 | Write a programme using Spring Framework to create a controller and display response… | Complete |
| Q20 | Create a Form to capture Student Admission information (make usual assumptions about… | Complete |
Preparation
Do not copy. Read for understanding and the viva- Constructor injection with
@Autowiredis the form to show; be able to contrast it with setter injection. - Decide the Student Admission attributes now (name, date of birth, gender, programme dropdown, address textarea, hostel required true/false, courses checkboxes) because Sessions 5, 6 and 7 reuse them.
- Spring form tags need the
spring-webmvcdependency and the form taglib declaration on the JSP.
Question 17
Problem Statement
Write in lab recordWrite a class and implement it using dependency injection.
Solution
Write in lab recordSteps
- Create
DiApp.javaincom.ignou.lab.admission.ioc. It holds one interface (FeeCalculator), one implementation (FlatFeeCalculator) and the class that depends on it (AdmissionDesk), plusmain. - Run it: Run As → Java Application, or
mvn -q compile exec:java -Dexec.mainClass=com.ignou.lab.admission.ioc.DiApp. - To see the injection fail, comment out
@ComponentonFlatFeeCalculatorand run again:UnsatisfiedDependencyException ... No qualifying bean of type FeeCalculator.
Program
package com.ignou.lab.admission.ioc;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
// ponytail: three small classes in one file so the whole DI example is one listing
/** What AdmissionDesk depends on. It never knows which implementation it gets. */
interface FeeCalculator {
int feeFor(String programme);
}
@Component
class FlatFeeCalculator implements FeeCalculator {
@Override
public int feeFor(String programme) {
return programme.equals("MCA") ? 12000 : 8000;
}
}
/** Constructor injection: Spring passes the FeeCalculator in; the field is final. */
@Component
class AdmissionDesk {
private final FeeCalculator fees;
AdmissionDesk(FeeCalculator fees) {
this.fees = fees;
}
String quote(String programme) {
return programme + " fee per semester: Rs " + fees.feeFor(programme);
}
}
public class DiApp {
public static void main(String[] args) {
try (var ctx = new AnnotationConfigApplicationContext(FlatFeeCalculator.class, AdmissionDesk.class)) {
AdmissionDesk desk = ctx.getBean(AdmissionDesk.class);
System.out.println(desk.quote("MCA"));
System.out.println(desk.quote("BCA"));
}
}
}Output
Expected console output, checked by reading, not executed:
MCA fee per semester: Rs 12000
BCA fee per semester: Rs 8000Explanation
AdmissionDeskdeclares what it needs (aFeeCalculator) as a constructor parameter. It never creates one. That is dependency injection: the dependency is pushed in from outside.- Spring sees a single constructor and calls it with the only
FeeCalculatorbean it knows,FlatFeeCalculator. Since Spring 4.3 a single constructor needs no@Autowired; add it when there is more than one. - Constructor injection makes the field
final, so the object is complete the moment it exists. Setter injection (@AutowiredonsetFees) allows an incomplete object and is used for optional dependencies. - A unit test can call
new AdmissionDesk(p -> 100)with a lambda; no Spring needed. That is the practical payoff of injecting an interface.
Question 18
Problem Statement
Write in lab recordWrite the service interface with the getRandom() method, define 3 courses in an array, and inject using dependency injection into the teacher Interface (exercise no 4 in session 3). Test the application and verify the retrieving of random courses.
Solution
Write in lab recordSteps
- In the
iocpackage addCourseService.javaandRandomCourseService.java. - Replace
JavaTeacher.javawith the version below: a constructor takesCourseServiceandgetFavouriteCourse()delegates to it.Teacher.javafrom Session 3 is unchanged. - Replace
TeacherApp.javaso the context knows both classes and prints five calls. - Run
TeacherAppthree times; the sequence of courses differs each run.
Program
Lab record: every tab is one file of the answer. Write all of them.
package com.ignou.lab.admission.ioc;
public interface CourseService {
String getRandom();
}package com.ignou.lab.admission.ioc;
import java.util.concurrent.ThreadLocalRandom;
import org.springframework.stereotype.Service;
@Service
public class RandomCourseService implements CourseService {
private static final String[] COURSES = {
"MCS-218 Data Communication and Computer Networks",
"MCS-219 Object Oriented Analysis and Design",
"MCS-220 Web Technologies"
};
@Override
public String getRandom() {
return COURSES[ThreadLocalRandom.current().nextInt(COURSES.length)];
}
}package com.ignou.lab.admission.ioc;
import org.springframework.stereotype.Component;
/** Session 4 version: the favourite course now comes from an injected CourseService. */
@Component
public class JavaTeacher implements Teacher {
private final CourseService courseService;
public JavaTeacher(CourseService courseService) { // constructor injection
this.courseService = courseService;
}
@Override
public String getName() {
return "Dr. Rao";
}
@Override
public String getFavouriteCourse() {
return courseService.getRandom();
}
}package com.ignou.lab.admission.ioc;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TeacherApp {
public static void main(String[] args) {
try (var ctx = new AnnotationConfigApplicationContext(JavaTeacher.class, RandomCourseService.class)) {
Teacher teacher = ctx.getBean(Teacher.class);
for (int i = 1; i <= 5; i++) {
System.out.println(i + ". " + teacher.getName() + " prefers " + teacher.getFavouriteCourse());
}
}
}
}Output
Expected console output (one run; yours will differ because the choice is random), checked by reading, not executed:
1. Dr. Rao prefers MCS-219 Object Oriented Analysis and Design
2. Dr. Rao prefers MCS-220 Web Technologies
3. Dr. Rao prefers MCS-220 Web Technologies
4. Dr. Rao prefers MCS-218 Data Communication and Computer Networks
5. Dr. Rao prefers MCS-219 Object Oriented Analysis and DesignVerification: over five calls only the three array entries ever appear, and at least two different ones show up in almost every run.
Explanation
RandomCourseServiceis a@Service(a@Componentwith a clearer name). The three courses sit in astatic final String[];ThreadLocalRandom.current().nextInt(3)picks an index.JavaTeachernow depends on theCourseServiceinterface, injected through its constructor. Session 3’s version hard-coded the answer; this one asks a collaborator.- The container wires the graph: it must build
RandomCourseServicefirst, then pass it toJavaTeacher’s constructor. Order is worked out from the constructor signatures, not from the order the classes are listed. - Because
JavaTeacheris in the web application’s scanned package, the same wiring happens inside Tomcat, which Q19 uses.
Question 19
Problem Statement
Write in lab recordWrite a programme using Spring Framework to create a controller and display response in view using @GetMapping() annotation.
Solution
Write in lab recordSteps
- Add
TeacherController.javatocom.ignou.lab.admission.webandteacher.jsptoWEB-INF/views. - Rebuild, redeploy, open http://localhost:8080/student-admission/teacher.
- Press F5 several times; the course changes.
Program
Lab record: every tab is one file of the answer. Write all of them.
package com.ignou.lab.admission.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.ignou.lab.admission.ioc.Teacher;
@Controller
public class TeacherController {
private final Teacher teacher; // JavaTeacher, found by the component scan
public TeacherController(Teacher teacher) {
this.teacher = teacher;
}
@GetMapping("/teacher")
public String show(Model model) {
model.addAttribute("teacherName", teacher.getName());
model.addAttribute("course", teacher.getFavouriteCourse());
return "teacher";
}
}<%@ page contentType="text/html;charset=UTF-8" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Teacher</title>
</head>
<body>
<h1>Teacher of the day</h1>
<p><b>${teacherName}</b> will teach <b>${course}</b> this session.</p>
<p><a href="">Refresh for another course</a></p>
</body>
</html>Output
Expected browser result, checked by reading, not executed:
Teacher of the day
Dr. Rao will teach MCS-220 Web Technologies this session.
Refresh for another courseExplanation
@Controllermakes the class a bean whose methods can be mapped to URLs.@GetMapping("/teacher")binds HTTP GET on/teachertoshow().- The
Modelparameter is a map that the view can read.teacher.jspreadsteacherNameandcoursewith${...}expressions. - The returned string
"teacher"is a view name;InternalResourceViewResolver(Session 3) turns it into/WEB-INF/views/teacher.jspand forwards the request. - The controller receives the
Teacherbean through its constructor, exactly likeTeacherAppdid; a web request is just another caller of the same object graph.
Question 20
Problem Statement
Write in lab recordCreate a Form to capture Student Admission information (make usual assumptions about the attributes) using Spring Form tags (must use Text, textarea, dropdown, date picker, true/false and checkbox) and write a controller using @PostMapping() to display the form information.
Solution
Write in lab recordAssumed attributes: name, email and mobile (text), date of birth (date picker), address (textarea), programme (dropdown), hostel required (true/false radio) and courses (checkboxes).
Steps
- Add
AdmissionForm.javaandAdmissionController.javatocom.ignou.lab.admission.web. - Add
admission-form.jspandadmission-result.jsptoWEB-INF/views. The form page declares the Spring form taglib with prefixform. - Rebuild, redeploy, open http://localhost:8080/student-admission/admission, fill every field, submit.
- View page source of the form before submitting: every
form:tag became a plain HTML element withidandnameequal to the path.
Program
Lab record: every tab is one file of the answer. Write all of them.
package com.ignou.lab.admission.web;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import org.springframework.format.annotation.DateTimeFormat;
/** Form-backing object for the Student Admission form. */
public class AdmissionForm {
private String name; // form:input
private String email; // form:input type="email"
private String mobile; // form:input
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate dob; // form:input type="date" (browser date picker)
private String address; // form:textarea
private String programme; // form:select
private Boolean hostelRequired; // form:radiobutton true / false
private List<String> courses = new ArrayList<>(); // form:checkboxes
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getMobile() { return mobile; }
public void setMobile(String mobile) { this.mobile = mobile; }
public LocalDate getDob() { return dob; }
public void setDob(LocalDate dob) { this.dob = dob; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
public String getProgramme() { return programme; }
public void setProgramme(String programme) { this.programme = programme; }
public Boolean getHostelRequired() { return hostelRequired; }
public void setHostelRequired(Boolean hostelRequired) { this.hostelRequired = hostelRequired; }
public List<String> getCourses() { return courses; }
public void setCourses(List<String> courses) { this.courses = courses; }
}package com.ignou.lab.admission.web;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class AdmissionController {
// ponytail: fixed lists; Session 6 reads programmes and courses from the database
private static final List<String> PROGRAMMES = List.of("MCA", "BCA", "PGDCA");
private static final List<String> COURSES = List.of(
"MCS-218 Data Communication and Computer Networks",
"MCS-219 Object Oriented Analysis and Design",
"MCS-220 Web Technologies",
"MCS-221 Data Warehousing and Data Mining");
@ModelAttribute("programmes")
public List<String> programmes() {
return PROGRAMMES;
}
@ModelAttribute("courseList")
public List<String> courseList() {
return COURSES;
}
@GetMapping("/admission")
public String showForm(Model model) {
model.addAttribute("admission", new AdmissionForm()); // empty object the form tags bind to
return "admission-form";
}
@PostMapping("/admission")
public String submit(@ModelAttribute("admission") AdmissionForm form) {
return "admission-result"; // "admission" is already in the model
}
}<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Student Admission Form</title>
</head>
<body>
<h1>Student Admission Form</h1>
<form:form modelAttribute="admission" method="post">
<p><form:label path="name">Full name</form:label><br>
<form:input path="name" size="40"/></p>
<p><form:label path="email">Email</form:label><br>
<form:input path="email" type="email" size="40"/></p>
<p><form:label path="mobile">Mobile</form:label><br>
<form:input path="mobile" size="12"/></p>
<p><form:label path="dob">Date of birth</form:label><br>
<form:input path="dob" type="date"/></p>
<p><form:label path="address">Address</form:label><br>
<form:textarea path="address" rows="3" cols="40"/></p>
<p><form:label path="programme">Programme</form:label><br>
<form:select path="programme">
<form:option value="" label="-- choose --"/>
<form:options items="${programmes}"/>
</form:select></p>
<p>Hostel required?<br>
<form:radiobutton path="hostelRequired" value="true" label="Yes"/>
<form:radiobutton path="hostelRequired" value="false" label="No"/></p>
<p>Courses this semester<br>
<form:checkboxes path="courses" items="${courseList}" delimiter="<br>"/></p>
<p><button type="submit">Submit application</button></p>
</form:form>
</body>
</html><%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Application received</title>
</head>
<body>
<h1>Application received</h1>
<table border="1" cellpadding="4">
<tr><th>Name</th><td><c:out value="${admission.name}"/></td></tr>
<tr><th>Email</th><td><c:out value="${admission.email}"/></td></tr>
<tr><th>Mobile</th><td><c:out value="${admission.mobile}"/></td></tr>
<tr><th>Date of birth</th><td>${admission.dob}</td></tr>
<tr><th>Address</th><td><c:out value="${admission.address}"/></td></tr>
<tr><th>Programme</th><td><c:out value="${admission.programme}"/></td></tr>
<tr><th>Hostel required</th><td>${admission.hostelRequired ? 'Yes' : 'No'}</td></tr>
<tr><th>Courses</th><td>
<c:forEach items="${admission.courses}" var="course">
<c:out value="${course}"/><br>
</c:forEach>
</td></tr>
</table>
<p><a href="${pageContext.request.contextPath}/admission">Another application</a></p>
</body>
</html>Output
Expected result after submitting the form, checked by reading, not executed:
Application received
Name Asha Verma
Email asha@example.com
Mobile 9876543210
Date of birth 2003-04-12
Address Sector 4, Rohini, Delhi
Programme MCA
Hostel required Yes
Courses MCS-218 Data Communication and Computer Networks
MCS-220 Web Technologies
Another applicationThe generated HTML for the radio buttons is input type="radio" name="hostelRequired" value="true" and value="false"; the date field is input type="date" name="dob".
Explanation
AdmissionFormis the form-backing object.showFormputs an empty one in the model under the nameadmission;form:form modelAttribute="admission"binds everyform:tag to a property of it bypath.- Tag to element:
form:input(text;type="email"andtype="date"pass through as HTML5 input types, so the browser shows its own date picker),form:textarea,form:selectwithform:options, twoform:radiobuttonwith valuestrueandfalsebound to aBoolean,form:checkboxesover a list bound toList<String>. @DateTimeFormat(iso = DATE)tells Spring’s conversion service to parse2003-04-12into aLocalDate; without it the post fails with a type mismatch.@PostMapping("/admission")receives the same URL by POST.@ModelAttribute("admission")makes Spring create anAdmissionForm, copy every request parameter into the matching property (data binding) and add it to the model, soadmission-result.jspreads${admission.name}.@ModelAttributeon theprogrammes()andcourseList()methods runs before every handler in the controller, so both GET and the POST re-render see the option lists.
Viva Questions
Do not copy. Read for understanding and the viva- Q: What is dependency injection? A: An object receives the objects it needs from outside (constructor or setter) instead of creating them; the container does the passing.
- Q: Constructor or setter injection, which and why? A: Constructor: mandatory dependencies, immutable fields, object valid at creation. Setter: optional dependencies or circular references.
- Q: What is the difference between
@Component,@Service,@Repositoryand@Controller? A: All are components; the names document the layer.@Repositoryalso translates persistence exceptions,@Controllerenables request mapping. - Q: What happens if two beans implement
Teacher? A:NoUniqueBeanDefinitionExceptionunless one is@Primaryor the injection point uses@Qualifier. - Q: What does
@ModelAttributedo on a method parameter? A: Creates or looks up the object, binds request parameters to its properties and adds it to the model. - Q: Why does
form:formneedmodelAttribute? A: The form tags read initial values from that object and derivenameattributes frompath, so binding on POST is by the same names. - Q: How does a
Booleanget a true/false value from radio buttons? A: Both radios share the namehostelRequired; the chosen one sendstrueorfalse, which Spring converts toBoolean.
Common Mistakes
Do not copy. Read for understanding and the viva- Injecting a concrete class (
JavaTeacher) instead of the interface, which defeats the point of Q18. - Forgetting the form taglib line; the JSP then prints
form:inputliterally. - Naming the model attribute
studentin one method andadmissionin the other; the POST binding then creates a new empty object. - Leaving out
@DateTimeFormaton theLocalDatefield and gettingFailed to convert property valueon submit. - Checkboxes bound to a
Stringinstead of aList<String>; only the last ticked box survives.
Session Summary
Write in lab record- Question 17:
DiAppwithAdmissionDeskreceiving aFeeCalculatorthrough constructor injection - Question 18:
CourseService.getRandom()over a three-course array, injected intoJavaTeacher, verified with five console calls - Question 19:
TeacherControllerwith@GetMapping("/teacher")renderingteacher.jsp - Question 20: Student Admission form with Spring form tags (text, textarea, select, date, true/false radios, checkboxes) and
@PostMappingshowing the posted data