In the dynamic landscape of web development, the combination of a robust Web module and an efficient Dependency Injection system is crucial for crafting maintainable and extensible software. Bring Svydovets brings together the best of both worlds, offering a comprehensive framework that simplifies web application development while promoting loose coupling and code organization through Dependency Injection.
The Dependency Injection framework embedded in this project facilitates the management of component dependencies. Say goodbye to tangled and hard-to-maintain code. With our Dependency Injection system, you can structure your application in a modular fashion, making it easier to understand, test, and extend. Enjoy the benefits of inversion of control, dependency injection, and advanced features like lifecycle management and contextual binding.
Svydovets Web provides a foundation for building modern web applications. Whether you're creating REST APIs. With built-in support for routing, request handling, and response generation, you can focus on implementing your application's logic without the hassle of low-level details.
- Java: Version 17 or later.
- Maven: Make sure Maven is installed on your system. You can download it from here.
Follow these steps to get started with our project:
-
Clone the Repository:
git clone https://github.com/Svydovets-Bobocode-Java-Ultimate-3-0/Bring.git cd Bring
-
Build the Project:
mvn clean install
-
To test Bring Application, create new Maven Project and add dependency:
<dependency> <groupId>com.bobocode.ultimate</groupId> <artifactId>bring-svydovets</artifactId> <version>1.0</version> </dependency>
-
You can download the Bring demo project to simplify setup: DEMO Project
-
Run the application:
public class DemoApp { public static void main(String[] args) { var context = BringApplication.run(DemoApp.class); } }
-
Enjoy :)
- Application context
- @Configuration
- @Bean
- @Component
- @Scope
- @Qualifier
- @Primary
- @PostConstruct
- @Autowired
- @ComponentScan
- @RestController
- @RequestMapping
- @RequestParam
- @PathVariable
- @RequestBody
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @PatchMapping
- ResponseEntity
ApplicationContext - main part of the IoC container. ApplicationContext - a magic box that helps application run smoothly. When application starts, it tells the ApplicationContext about all different beans it has. Each bean has responsibilities, like being saving data. The ApplicationContext keeps track of all these beans and makes sure they are ready to do their job when it needed.
Example of code
public class Application {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example.bring_test.beans");
Map<String, Object> beans = applicationContext.getBeans();
}
}
The @Configuration annotation is used to indicate that a class declares one or more @Bean methods, and define user's custom beans. The @Configuration annotation is an integral part of the creating application context.
Example of code
@Configuration
@ComponentScan("com.example.bring_test.ioc.beans")
public class TestConfig {
}
Bean is object that is instantiated, assembled, and managed by the IoC container The Bean annotation is a custom annotation that you can use in your own implementation of the Bring framework. It is used to mark methods in code that produce bean that should be managed.
Example of code
@Configuration
@ComponentScan("com.example.bring_test.ioc.beans")
public class TestConfig {
@Bean
public MessageService messageService() {
MessageService messageService = new MessageService();
messageService.setMessage("Hello from \"MessageService\"");
return messageService;
}
@Bean
public PrintService printService(MessageService messageService) {
return new PrintService(messageService);
}
}
The @Component annotation is used to indicate that a class is component of context. It is a generic annotation that designates a class as bean, allowing IoC container to manage its lifecycle. Components are typically Java classes that encapsulate business logic or other processing capabilities within a user application.
Example of code
@Component
public class MessageService {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
The @Scope annotation in Java is used to specify the lifecycle of a bean within the application context. It enables developers to define whether a bean should be a singleton, with a single shared instance across the entire application, or a prototype, with a new instance created for each request or reference.
Singleton scope
Example of code with the Singleton scope
@Component
@Scope(ApplicationContext.SCOPE_SINGLETON)
public class SingletonCandidate {
}
Prototype scope
Example of code with the Prototype scope
@Component
@Scope(ApplicationContext.SCOPE_PROTOTYPE)
public class PrototypeCandidate {
}
The @Qualifier annotation in Java is utilized to enhance bean injection by providing a means to disambiguate and specify a particular bean when there are multiple candidates of the same type within the application context.
Example of code
@Component
public class OrderService {
@Autowired
@Qualifier("storeItem")
private Item item;
public Item getItem() {
return item;
}
}
@Primary is an effective way to use autowiring by type with several instances when one primary candidate can be determined. When marked with @Primary, a bean is designated as the primary candidate for autowiring within a specific type, meaning it takes precedence over other candidates of the same type.
Example of code
@Primary
@Component
public class PrimaryInjectionCandidate implements InjectionCandidate {
}
The @PostConstruct annotation in Java as a marker for methods that should be executed immediately after an object has been instantiated and its dependencies have been injected or initialized. This annotation allows developers to define post-construction logic for objects without relying on any specific framework or container.
Example of code
@Component
public class ServiceWithPostConstruct {
private String message;
@PostConstruct
public void init() {
message = "Message loaded with @PostConstruct";
}
public String getMessage() {
return message;
}
}
The @Autowired annotation in Java is employed to facilitate automatic dependency injection, allowing developers to inject dependencies into beans without explicit instantiation. This annotation can be applied to fields, methods, or constructors to signal the container to automatically resolve and provide the necessary dependencies.
Example of the constructor autowired
Example of code with the constructor inject
@Component
public class ServiceWithAutowiredConstructor {
private final MessageService messageService;
@Autowired
public ServiceWithAutowiredConstructor(MessageService messageService) {
this.messageService = messageService;
}
}
Example of the method autowired
Example of code with the method inject
@Component
public class TrimService {
private CommonService commonService;
@Autowired
public void setCommonService(CommonService commonService) {
this.commonService = commonService;
}
public CommonService getCommonService() {
return commonService;
}
}
Example of the field autowired
Example of code with the field inject
@Component
public class EditService {
@Autowired
private MessageService messageService;
@Autowired
private InjectionCandidate injectionCandidate;
}
The custom @CustomComponentScan annotation in Java serves as a marker for configuration classes that enable the automatic detection and registration of custom components within specified base packages. This annotation provides a mechanism for developers to implement component scanning functionality without relying on any specific framework.
Example of code
@Configuration
@ComponentScan("com.exmaple.bring_test.beans")
public class ScanConfigTest {
}
The @RestController annotation handles HTTP requests and generate responses. This annotation designates a class as a controller specifically focused on building REST APIs, where methods within the class are responsible for processing requests and producing responses in a JSON format.
Example of code
@RestController
public class UserController {
}
The @RequestMapping annotation is used to establish the base URL for the class that was marked with the @RestController annotation.
Example of code
@RestController
@RequestMapping("/users")
public class UserController {
}
The @RequestParam annotation is used to extract and bind query parameters from the URL of an HTTP request to the parameters of a method. This annotation simplifies the process of handling request parameters and allows developers to easily access and use them within their methods.
Example of code
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping
public User getOneByFirstName(@RequestParam String firstName) {
return userMap.values()
.stream()
.filter(user -> user.getFirstName().equals(firstName))
.findAny()
.orElseThrow();
}
}
The @PathVariable annotation is utilized to extract values from the URI path of an HTTP request and bind them to the parameters of a method. This annotation simplifies the process of handling path variables, allowing developers to access and utilize them within their methods.
Example of code
@RestController
@RequestMapping("/users")
public class UserController {
private AtomicLong idGenerator = new AtomicLong(0L);
private Map<Long, User> userMap = new ConcurrentHashMap<>();
@GetMapping("/{id}")
public User getOneById(@PathVariable Long id) {
return userMap.get(id);
}
}
The @RequestBody annotation is used to bind HTTP request body with method parameter in controller. When the method parameter is annotated with @RequestBody, it converts body of HTTP request to type of method parameter.
Example of code
@RestController
@RequestMapping("/users")
public class UserController {
@PutMapping("/{id}")
public User update(@PathVariable Long id, @RequestBody User user) {
User savedUser = userMap.get(id);
savedUser.setFirstName(user.getFirstName());
savedUser.setLastName(user.getLastName());
savedUser.setStatus(user.getStatus());
userMap.put(id, savedUser);
return savedUser;
}
}
The @GetMapping annotation is designed for handling HTTP GET requests.
Example of code
@RestController
@RequestMapping("/users")
public class UserController {
private AtomicLong idGenerator = new AtomicLong(0L);
private Map<Long, User> userMap = new ConcurrentHashMap<>();
@GetMapping
public User getOneByFirstName(@RequestParam String firstName) {
return userMap.values()
.stream()
.filter(user -> user.getFirstName().equals(firstName))
.findAny()
.orElseThrow();
}
@GetMapping("/{id}")
public User getOneById(@PathVariable Long id) {
return userMap.get(id);
}
}
The @PostMapping annotation is designed for handling HTTP POST requests.
Example of code
@RestController
@RequestMapping("/users")
public class UserController {
private AtomicLong idGenerator = new AtomicLong(0L);
private Map<Long, User> userMap = new ConcurrentHashMap<>();
@PostMapping
public Long save(@RequestBody User user) {
Long generatedId = idGenerator.incrementAndGet();
user.setId(generatedId);
userMap.put(generatedId, user);
return generatedId;
}
}
The @PutMapping annotation is designed for handling HTTP PUT requests.
Example of code
@RestController
@RequestMapping("/users")
public class UserController {
private AtomicLong idGenerator = new AtomicLong(0L);
private Map<Long, User> userMap = new ConcurrentHashMap<>();
@PutMapping("/{id}")
public User update(@PathVariable Long id, @RequestBody User user) {
User savedUser = userMap.get(id);
savedUser.setFirstName(user.getFirstName());
savedUser.setLastName(user.getLastName());
savedUser.setStatus(user.getStatus());
userMap.put(id, savedUser);
return savedUser;
}
}
The @DeleteMapping annotation is designed for handling HTTP DELETE requests.
Example of code
@RestController
@RequestMapping("/users")
public class UserController {
private AtomicLong idGenerator = new AtomicLong(0L);
private Map<Long, User> userMap = new ConcurrentHashMap<>();
@DeleteMapping("/{id}")
public void removeWithServletRequestAndResponse(@PathVariable Long id) {
userMap.remove(id);
}
}
The @PatchMapping annotation is designed for handling HTTP PATCH requests.
Example of code
@RestController
@RequestMapping("/users")
public class UserController {
private AtomicLong idGenerator = new AtomicLong(0L);
private Map<Long, User> userMap = new ConcurrentHashMap<>();
@PatchMapping
public User updateUserFirstNameById(@PathVariable Long id, @RequestBody String firstName) {
User user = userMap.get(id);
user.setFirstName(firstName);
userMap.put(id, user);
return user;
}
}
The ResponseEntity responsible for handling the status code, body, http headers. You can use it to wrap your DTO.
Example of code
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public ResponseEntity<User> getOneById(@PathVariable Long id) {
return ResponseEntity.ok().body(userMap.get(id));
}
}