Purpose of Service Interface Class in Spring Boot - java

My question is regarding the use of the interface class. I am fairly new to Spring so please bear with me if this is overly simple.
First of all, what is the point of having an IBoxService interface here when you could just declare the find all in BoxService. Secondly, in the controller how is IBoxService being used. Meaning, we are calling IBoxService.findAll(). But, how is this being tied to the BoxService class. What if multiple service classes implemented IBoxService? Is this a java thing or a Spring injection thing. Thanks.
package com.xyz.service;
import com.xyz.model.Box;
import java.util.Set;
public interface IBoxService {
Set<Box> findAll();
}
package com.xyz.service;
import com.xyz.model.Box;
import com.xyz.repository.BoxRepository;
import java.util.Set;
import org.springframework.stereotype.Service;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
#Service
#AllArgsConstructor
#Slf4j
#Transactional
public class BoxService implements IBoxService {
#Autowired
private BoxRepository boxRepo;
#Override
public Set<City> findAll() {
return (Set<City>) repository.findAll();
}
}
package com.xyz.controller;
import com.xyz.model.Box;
import com.xyz.service.IBoxService;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
#RestController
#RequestMapping("/api/box")
public class BoxController {
#Autowired
private IBoxService boxService;
#GetMapping
public ResponseEntity<Set<Boxes>> allBoxes() {
return (Set<Box>) boxService.findAll();
}
}

There are various reasons why Service layer interfaces are created. The first and most important reason is testability. You can create mocks of service interface and test your code easily, if you cannot create mocks using a mocking library then you can create test stubs.
One more reason is, we can achieve loose coupling between Controller and Service layer. Suppose you want to entirely change the implementation of service, you can create new service implementation and inject that implementation by injecting new bean by qualifier name

Please understand basic Java and use of interface . Spring boot is just abstraction over Java hence all the basic concepts applies as it is.
Coming back to your questions IBoxService is a interface which allows to inject required implementation of it at controller level. As of now only implementation of IBoxServic is BoxService hence it is getting injected automatically. In case you have multiple implementations you need to use qualifier annotation to specify kind of implementation you need to inject. Or you can create object bu yourself using class names
Consider below:
IBoxService is implemented by two classes BoxService and TiffinBoxService
Now in controller you can inject implementation which you want. Which allow us to achieve principle of interface which is hide internal details.
User which is controller in this case doesn't need to know which class is being use internally as we are using reference of interface.
List interface is best example which has ArrayList and LinkedList as implementation classes.
Hope it is useful !!

Related

#Value not Injecting ANY value

I'm learning how to create a RESTful API with Springboot, and the #Value tag is not injecting values from application.properties, nor is it injecting values specified in the tag itself. I have my project structured as such:
api
config
controllers
model
services
SpringApplication.java
resources
application.properties
Strangely, this behavior only seems to occur within files located in my "services" folder. The #Value tag works as expected in files located in my "controllers" folder. Below are examples of what I am doing:
#Value("${var}")
String variable
"var" is defined in application.properties as var=some_stringbut variable is still initialized as 'null'
#Value("I am directly assigning a value to this variable, but it still comes out null")
String variable
I believe I am using the correct import: import org.springframework.beans.factory.annotation.Value.
At first I just thought the "services" folder was blind to the directory where application.properties is located, but after trying to directly inject values, I'm not so sure what to think.
Edit
All of the classes in the services folder are annotated with #Service and nothing else. Below is what the class looks like. I've opted to leave out implementations of the methods, other variables, and irrelevant imports. The code/methods all work as expected when hard-coding the variables. My focus is the #Value tag.
package myapi.api.services;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
#Service
public class MyService {
#Value("${var}")
String variable;
public List<Data> getData() {
return new ArrayList<Data>();
}
public void postData() {
}
Edit 2
Below is the APIController class, stored in the "controllers" folder. Again, I've opted to leave out irrelevant methods/imports/variables. I would also like to note, that the #Value tag works as expected in this class.
package myapi.api.controllers;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import myapi.api.services.MyService;
import lombok.RequiredArgsConstructor;
#RestController
#RequestMapping("api")
#RequiredArgsConstructor
public class APIController {
#Autowired
private final static MyService myService = new MyService();
#GetMapping("/getdata")
public List<Data> getData() {
return myService.getData();
}
}
#Autowired
private final static MyService myService = new MyService();
Three issues:
First, you cannot have a new operator. Spring controls the lifecycle of your class instances. It will call new for you in the background. Remove the entire new operator stanza.
Next: your your field cannot be final. After construction of the class, Spring will need to modify that field with a proxy. Remove the final declaration;
Finally: your field cannot be static. static variables have a certain lifecycle with the JVM, and you need to let the Spring framework manage your lifecycle. remove the static operator.
The correct declaration should be:
#Autowired
private MyService myService;

Adding Apache Camel custom component/endpoint in a Spring application

I'm trying to implement a custom endpoint in a Spring Boot application.
Goal is to use routes as: from("...").process("...").to("my:...");
Now, I have 3 classes: a DefaultConsumer, a DefaultEndpoint, a DefaultComponent:
package com.my.endpoint;
import org.apache.camel.Consumer;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
import org.apache.camel.support.DefaultEndpoint;
public class MyEndpoint extends DefaultEndpoint {
public MyEndpoint(String uri, MyComponent myComponent) {
}
...
}
package com.my.endpoint;
import org.apache.camel.Endpoint;
import org.apache.camel.Processor;
import org.apache.camel.support.DefaultConsumer;
public class MyConsumer extends DefaultConsumer {
public MyConsumer(Endpoint endpoint, Processor processor) {
super(endpoint, processor);
}
}
package com.my.endpoint;
import org.apache.camel.CamelContext;
import org.apache.camel.Endpoint;
import org.apache.camel.spi.annotations.Component;
import org.apache.camel.support.DefaultComponent;
import java.util.Map;
#Component("my")
public class MyComponent extends DefaultComponent {
public MyComponent(CamelContext camelContext) {
super(camelContext);
}
...
}
Now: how can I register?
In a Spring configuration class, I have:
#Override
public void configure() throws Exception {
camelContext.addComponent("my", new MyComponent(camelContext));
But is not working:
Caused by: org.apache.camel.NoSuchEndpointException: No endpoint could be found for: my, please check your classpath contains the needed Camel component jar.
So, I added the META-INF file in services/org/apache/camel/component/my:
class=com.my.endpoint.MyComponent
But also this, is not working.
There is no complete tutorial on how to implement this.
Any help?
Note: I'm trying to implement an Endpoint because I need to integrate my systems using my data types. I tried using Transformer but failed because of this: Set a custom DataType in Apache Camel Processor
Before, I tried using data type converter, but failed because of this (marked duplicate because people are too lazy to really understand questions): Enforce type conversion on Rest consumer in Apache Camel
I've FULLY read "Apache Camel In Action, Second Edition" but, at the moment, I can't continue with my project because of?
This is because custom component must be annotated by #UriEndpoint annotation.
Another way to solve this problem: Set EndpointUri via Constructor or by implementing createEndpointUri() in MyEndpoint.
So easiest way might be changing your constructor to:
public MyEndpoint(String uri, MyComponent myComponent) {
super(uri, myComponent);
}

Does SpringBoot enforce Dependency Inversion design principle? And is this a good example of that?

So I understand that Dependency Inversion represents the D in SOLID design principles, and I have previously written a web-application using SpringBoot and was wondering if this code example shows a good example of the Dependency Inversion principle in action or not to help me understand this concept properly.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
/**
* Provides a set of methods for serving and handling Interview data.
*/
#Controller
#SessionAttributes(names = {"users"})
public class InterviewController {
private final InterviewAuditor interviewAuditor;
private final AthleteAuditor athleteAuditor;
/**
* Injects all the needed auditors to talk to the database.
*
* #param interviewAuditor - the interview Auditor.
* #param athleteAuditor - the athlete Auditor.
*/
#Autowired
public InterviewController(InterviewAuditor interviewAuditor, AthleteAuditor athleteAuditor) {
this.interviewAuditor = interviewAuditor;
this.athleteAuditor = athleteAuditor;
}
Thanks!
Yes. Dependency injection, which in this case is constructor injection specifically, is a good example of dependency inversion, and also a good example of inversion of control. These concepts can all be described in terms of a hierarchy.
Using a DI container like Spring is probably the easiest and certainly the most common approach to achieving dependency inversion. Note that if InterviewController has only one constructor, you don't even need to annotate it as #Autowired. Spring Boot will still know what to do.
Your code is pretty good, but you can remove #Autowired. It is not necessary when using Dependency Injection with constructor since Spring 4.2.
I don't know if InterviewAuditor and AthleteAuditor are interface but it's a good practice to inject interface (if it exists of course) to have flexibility. Thus you can reuse the same class with different dependencies (different implementation).
By the way it's good practice to use DI with constructor instead of property as you did.

Spring `JpaRepository` to be used only in test scope

I have a JpaRepository, that looks like
#Repository
public interface CustomRepository extends JpaRepository<EntityType, Integer> {
// Methods
}
which could possibly have queries that would run for long, in which case I need to enforce a timeout. I have successfully added the timeout-related configuration (the connection pool being used is Druid, if that matters) and now I want to test it in a unit test. I am using the following method in my JpaRepository<T, ID> interface.
#Query(value = "SELECT benchmark(:count, MD5('3094803'))", nativeQuery = true)
void run(#Param("count") Long count);
This method runs successfully and demonstrates the expected behavior. However, given that this method will run longer as the value given to parameter count becomes larger, having this in the production code, just for the sake of testing the timeouts, bothers me to my core, as this might end up being a vulnerability that could be leveraged to launch a denial attack.
So to the question, is some way I can use this exact method in my test scope, without having that going in the production code?
Turns out, it's not that complicated. Given that this project is running on Spring, I can have an extension of the above repository in my test sources, as follows.
package com.project.package.repo;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
#Repository
public interface TimedCustomRepository extends CustomRepository {
#Query(value = "SELECT benchmark(:count, MD5('3094803'))", nativeQuery = true)
void run(#Param("count") Long count);
}
As I have JUnit4, I can have a test class which will run on Spring boot, like follows.
package om.project.package.repo;
import com.github.database.rider.spring.api.DBRider;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.test.context.junit4.SpringRunner;
#SpringBootTest
#RunWith(SpringRunner.class)
#DBRider(dataSourceBeanName = "primaryDataSource") //Data source wiring; Not that important.
public class TransactionHistoryJpaRepoTest {
#Autowired //---(1)
private TimedCustomRepository timedCustomRepository;
#Test(expected = JpaSystemException.class)
public void whenQueryTimeoutOccurs() {
timedCustomRepository.run(100000000L);
}
}
The property at (1) will be wired using the repository bean which we have created above, and this test will execute as expected. Given the bean TimedCustomRepository extends CustomRepository, the data source configuration and everything will be the same. Most importantly, since this method with a long running query is now only on the test scope, it will not have any impact beyond the test scope.

SpringBoot #Autowire why does this example work?

I have created this class:
import org.springframework.stereotype.Component;
...
#Component("notTheNameTestMe") //shouldnt this only work with testMe ?
public class TestMe {
public void showMsg() {
System.out.println("Autowiring works");
}
}
And I'm using it this way in a second class (or better: controller):
import com.example.TestMe; //shouldnt this be not necessary with autowire? But getting error else...
...
#Autowired
private TestMe testMe;
...
this.testMe.showMsg();
But this works perfectly (so maybe Im not really using autowire here?), it even works if I rename the whole TestMe class to TestMeSomething (if I adjust the import in the second class)
I dont really understand what #Autowired does. I thought it just scans for SpringBoot Components (which are named by the string in #Component() and when it finds a match it Injects the dependancy. But in my example the match is impossible and I still can see the message "Autowiring works" in the console. This shouldnt be like this if I would really use autowire here or? What am I understanding in a wrong way? What is the difference to using new TestMe() then? I have the dependancy already with the import or? So not a real dependancy injection, or?
Spring is not operating on the name in the #Component annotation. Rather it's using the name of the class. It's simply finding a class named TestMe because that's the type of the variable you've annotated with #Autowired.

Categories

Resources