Spring Boot program structure for access to data objects - java

I have an spring boot app that has a mongo repository and is using spring data to connect to it. This means there's an "entity" class and then a "repository" class.
However, to actually use the repository I need spring to #Autowire my repo to a variable inside another class (we'll call it X) that might want to use it. But if the X class is itself not a spring bean, it's just a regular pojo created by "new X" somewhere then it can't make use of Autowiring and therefore cant use the repo.
This seems like a show stopper... No one could ever make use of a repo outside of some very specific situations like calling the repo directly from your RestController or whatever without any intervening logic. Yet I am sure people are using this.
So my question is how to structure code so that it can be used? Do I need to do a bunch of processing and then return back up to the controller to interact with the databases? Is there a way to create some kind of other "intermediate helper bean" to mediate the connection? What should that look like?
disclaimer: I am fairly new to spring

Perhaps I can post some excerpts that can clarify your situation.
//nothing spring specific
public class MyPojo {
//properties
}
in a different package:
#Repository
public class MyRepositoryImpl implements MyRepository {
//CRUD implementation or whatever
}
in a different package:
#Service
public class MyServiceImpl implements MyService {
#Autowired //constructor-injection
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
private final MyRepository myRepository;
public void myBusinessLogic() {
MyPojo pojo = new MyPojo(); //not dependent on Spring
myRepository.doSomething();
//place calls to X here as needed
}
}
And finally:
#Controller
public class MyController {
#Autowired
public MyController(MyService myService) {
this.myService = myService;
}
private final MyService myService;
#GetMapping("/myPage")
public String doIt() {
myService.myBusinessLogic();
return "myPage";
}
}
Where MyRepository and MyService are interfaces that would contain the contract for their respective implementations.
Single Responsibility Principle
A major point to note is that your POJO isn't going to "use the repo" as you mention in your question. It represents an entity and shouldn't care about any specific repository. And this seems related to your issue - a POJO shouldn't be making calls to a repository (your "X" class in this case). Seems like the design should be revisited if that is the case.

As you say, you can only autowire fields in objects that themselves are autowired. This is inherent to bean injection. You should annotate X with for instance #Component and inject it where you need it.

Related

Spring Boot | Using a separate file for logic

I am writing a small CRUD in Spring and Java. And I want to use a separate file for writing logic, this is very convenient for me, I did this when developing with NestJS. I have a few questions, is it correct to do this in Spring, or should I do everything inside a function in the controller. And if I write logic in a separate file, I should mark the logic class as #Component and the functions inside it as #Bean, right? I am new to Spring and as I understand it, I have to do this in order for the application to work correctly and my functions to be in the application context.
AuthLogic.java
#Component
public class AuthLogic {
#Bean
public void register() {
// code ...
}
}
AuthController.java
#RestController
public class AuthController {
#RequestMapping(value = "/register", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public void register(#Valid #RequestBody UserDTO newUser) {
// here I call the register function from AuthLogic
}
}
you can mark your logic class with #Service and use that for example you can make a AuthService and use it like
#Service
public class AuthService{
public returnType login(){
//Logic
}
}
and use this like
#RestController
public class AuthController {
AuthService authService;
#RequestMapping(value = "/register", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public void register(#Valid #RequestBody UserDTO newUser) {
authService.login();
}
}
You can write your business logic in a new separate file at service layer.
Suppose you name it as AuthService and you mark it with annotation #Service.
#Service
public class AuthService{
}
You can then Autowire it in your controller class.
#RestController
public class AuthController {
#Autowired
AuthService authService;
#RequestMapping(value = "/register", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public void register(#Valid #RequestBody UserDTO newUser) {
// here I call the register function from AuthLogic
}
}
Using separate files, or classes more importantly, is very recommended in Spring, and I assume most other languages.
The #Bean annotation on AuthLogic.java is unneeded and I think may cause startup or compilation errors.
I would change the name of AuthLogic to AuthLogicImpl, create an interface named AuthLogic with the method signature void register(), and have AuthLogicImpl implement AuthLogic. Then you can create a constructor for AuthController which accepts and AuthLogic parameter and sets it to a private field (note using the interface not the implementation in the constructor).
At the core of Spring is the IoC container. This container holds "beans" that can be injected or autowired into other beans. These beans are an instance of their class and can be used by other beans. Remember Spring uses the singleton pattern, so your beans should be stateless. This allows Spring to handle the application startup for you, so you don't need to write a ton of code creating all the different services/classes and wiring them together, it's all automagically done for you.
There are two key annoitations that you appear to be confused about:
#Component Putting this above a class will create an instance of that class (a bean) and put it into the IoC container. Other beans can access this by accepting the original beans interface in its constructor. So if I put #Component above my class FooImpl which implements Foo, then I can create a class, BarImpl with the constructor public BarImpl(Foo foo) {this.foo = foo} and BarImpl can use any public method of Foo (which will use FooImpl's implementation).
#Bean this is to be put on a method of a class that is annotated with #Configuration. This tells Spring that this method should be run at startup, and this method will return a bean that Spring should add to the IoC container. This is a popular way of configuring a bean that requires some parameters, such as the bean that manages the connection to a third party service or datastore, especially when that there is a little bit of logic that needs to go into creating the bean.
Note that the above is very broad, and there are some specifics if you need to dig deep into the spring framework, so there will be more clarification in the Spring documentation or you dig into some established Spring project. However it should suffice to answer the broad question of what is going on with #Component and #Bean.
There is no specific layout or code structure for Spring Boot Projects. However, there are some best practices followed by developers that will help us too. You can divide your project into layers like service layer, entity layer, and repository layer.
We use the entity layer to write all model and POJO classes. We annotate them with #Entity.
We use #Repository to indicate that this is a repository interface that is used to do some basic CRUD operations.
Sidenote:- You don't have to write #Repository for classes that implement or interfaces that extends Repository interfaces provided by Spring Boot framework.
We use #Service to say that this is the service class where your all business logic will be present.
We use the controller layer to receive HTTP requests and send back HTTP Responses or views.
You can learn and understand more from here
You can refer to this Image to understand the structure
project structure

Cannot find the Repository bean in Spring boot / Spring Data MongoDB

I have created a spring project which has a controller and all of its logic is written in Service Interface, which is implemented by ServiceImpl class. I have a repository with which has a model.
//Service Interface
public interface Service{
List<Model> getAllKpiData();
}
//ServiceImpl Class
#Service
public class ServiceImpl implements Service{
#Autowired
private KPIRepository kpiRepository;
#override
private List<Model> getAllKpiData()
{
this.kpiRepository.findAll();
//gets me an empty list.
}
}
//KPIRepository
#Repository
public inerface KPIRepository extends MongoRepository<KPIModel, String>
{
}
//Another Service Interface in another package
public interface AnotherService{
List<Model> getAllKpiData();
}
//ServiceImpl Class
#Service
public class AnotherServiceImpl implements Service{
#Autowired
private KPIRepository kpiRepository;
#override
private List<Model> getAllKpiData()
{
this.kpiRepository.findAll();
//gets me list of values, which are inside the repo(master data).
}
}
Both of them are pointing to same repo, but in AnotherService class i am able to get values inside the repository, whereas i am not able to get any values inside Service, on doing this.kpiRepository.findAll().
Do you have spring-boot-starter-data-mongodb dependency on classpath? If yes, then is KPIRepository in the same package as your main class? If not then in your main class put this annotation #EnableMongoRepositories(basePackageClasses=KPIRepository.class) to safely tell Spring Data MongoDB to scan a different root package by type if your project layout has multiple projects and its not finding your repositories. Or you can use #EnableMongoRepositories(basePackages = "com.acme.repositories.mongo") to specify the package that contains all of your repositories.
The presense of spring-boot-starter-data-mongodb will automatically enable #EnableMongoRepositories. And Spring will automatically create proxies of all classes implementing Repository<T,ID> (your class implements MongoRepository, which itself implements Repository) and create beans of them and make them available for injection. And when your repository is in a different package then it is unable to create proxies of your repository, hence fails to create a bean of it. And since there is no bean, it cannot inject it, hence you see the error.
Did you use the #EnableMongoRepositories annotation? Take a look to this link: https://docs.spring.io/spring-data/mongodb/docs/1.2.0.RELEASE/reference/html/mongo.repositories.html. Review the "6.2 usage" point.
Regards

Spring boot - #Service class calling another #Service class

Is it fine to have a #Service annotated class calling another #Service annotated class? Or it is a bad practice?
Eg.:
#Service
public class MyService {
// ...
#Autowired
private MyOtherService myOtherService;
// ...
}
It is not any restriction calling a service from another one. Unless you make circular dependency between services.
Circular dependency : https://en.wikipedia.org/wiki/Circular_dependency
Circular dependency in spring : https://www.baeldung.com/circular-dependencies-in-spring
Its good practice since utility class are being ignored these days, approach getting motivated by horizontal scaling... Surely services got to interact with other.
No need to worry, its like one service manager needs services of another manager.
just only one should be dependent on other, not both.
#Service
public class MyService {
// ...
#Autowired
private MyOtherService myOtherService = new MyOtherService();
// ...}
Try this. It is worked for me. You can use the methods of a service in many sevice.

Is grouping multiple #Service and #Repository classes into wrappers an anti-pattern?

Question is a bit rhetorical. I've had an argument with my colleagues regarding the following pattern:
#Component
public class MetaService {
public static UserService userService;
public static GroupService groupService;
public static PermissionService permissionService;
// ...more fields
#Autowired
public MetaService(UserService userService,
GroupService groupService
PermissionService permissionService) {
MetaService.userService = userService;
MetaService.groupService = groupService;
MetaService.permissionService = permissionService;
}
}
Basically, MetaService is an entry point/wrapper for multiple Spring beans #Service classes. And there's one more similar MetaDao wrapper for #Repository beans class:
#Component
public class MetaDao {
public static UserDao userDao;
public static GroupDao groupDao;
public static PermissionDao permissionDao;
// ...more fields
#Autowired
public MetaDao(UserDao userDao,
GroupDao groupDao,
PermissionDao permissionDao) {
MetaDao.userDao = userDao;
MetaDao.groupDao = groupDao;
MetaDao.permissionDao = permissionDao;
}
}
The idea of having such a Meta-class is to reduce code base within #RestController and #Service classes, by providing concise API to call #Service and #Repository methods, for example:
#RestController
public class UserController {
#PostMapping
public UserCreateResponse createUser(UserCreateRequest request) {
MetaService.userService.createAndReturn(userEntity);
// other service calls
}
}
In other words, this is an alternative to having #Autowired fields/constructors for each #Service within each #Controller etc. In my opinion this approach greatly reduces boilerplate repetitive code a lot.
Well, my colleagues are saying that: such an approach makes the code harder to understand/maintain since combining #Service components in one place hides dependency model of other components which use them. In other words, if there were fields declared for UserController class. It would've been easier to quickly find out which components UserController depends upon. In addition, it's harder to mock such dependencies doing unit testing. And the most important
that such approach is super-anti-pattern. Whereas I can agree with mocking part, everything else seems ridiculous to me.
Question is, is this truly an anti-pattern in any way?
This is clearly an anti-pattern to me.
Why is there even a need for this? The UserController needs the UserService, why would it include a MetaService containing a GroupService it doesn't need?
Not sure what your MetaDAO is supposed to do, bundle together objects that are in a certain relation? I highly doubt that's a proper implementation for most objects (can a user be in many groups? can he have several permissions?) What domain relevance is this supposed to serve? Or are they supposed to be #Repositorys?
The static members are public static, but not final - breach of encapsulation. It's basically a badly implemented Singleton pattern.
The MetaService might as well implement all three interfaces and maintain a relation to the repositories. No real need to put two layers of services between controller and repository if the actual domain is properly represented by a single service.
Also, I think this question belongs in https://softwareengineering.stackexchange.com.
With regard to the principles of OOP, I find exposing grouped services through public static attributes even with final keyword as a bad principle. I think this approach increases the complexity: What if more of the DAOs/services are about to be added? - Then you'll be forced to enlarge the MetaDao and MetaSerice classes.
Exposing these services through this facade, another unnecessary layer would allow the UserController use and touch services which really doesn't need - Is it necessary? It breaks the encapsulation. Use this widely used approach:
#RestController
public class UserController {
#Autowired
private final UserService userService;
// ...
}
You're introducing Service Locator called MetaService that all other services depend on, instead of directly depending on their dependencies. This also means that you introduce hidden dependencies, see here:
MetaService.userService.createAndReturn(userEntity);
_ _
/|\ /|\
| |
You no longer know based on your constructor arguments what your dependencies really are.
If you already have a dependency injection system in place, you generally shouldn't need a service locator.

Injecting dependencies using #Autowired into objects created with "new ..."

I have a problem with injecting a bean into a helper class. It works basically like this: I create an object in the page constructor that does some work, returns some data and I show these on the page. In this helper object, a service should be injected via #Autowired annotation. However, I always get a null pointer exception when I use it. I also tried #SpringBean but it didn't help. On the other hand, when I inject this service directly into the page with #SpringBean, it's accessible and works fine. Do you know where the problem is?
This is the page:
public class Page extends BasePage {
public Page() {
HelperObject object = new HelperObject(new Application("APP_NAME"));
String result = object.getData();
add(new Label("label", result));
}
}
Helper object:
public class HelperObject {
private Application app;
#Autowired
private Service service;
public HelperObject(Application app) {
this.app = app;
}
public String getData() {
// use service, manipulate data, return a string
}
}
You can inject dependencies into non-Spring-non-Wicket-new-created objects using #SpringBean by calling InjectorHolder.getInjector().inject(this); in its constructor.
For example:
class MyPojo {
#SpringBean
MyDumbDAO dao;
MyPojo() {
InjectorHolder.getInjector().inject(this);
}
void justDoIt() {
dao.duh(); // dao is there!
}
}
Note that it will only work if called within a Wicket-managed request. If not (ie, if it's a Quartz job, or a Filter executed before Wicket's), the Application instance will not be available, and the injector won't know how to get the dependencies.
Another solution is to use Spring's #Configurable. It uses AspectJ to intercept creation of annotated objects, and inject its dependencies, even if you instantiate them directly with new (or some other framework, like Hibernate, creates them internally). But this requires runtime or build-time (works better for me) bytecode manipulation, which may be too much magic for some people.
#SpringBean only injects dependencies into classes that inherit from Wicket's Component. #Autowired only injects dependencies into classes created by Spring itself. That means you can't automatically inject a dependency into an object you create with new.
(Edit: you can also add a #SpringBean injection to your class by injecting in the constructor:
InjectorHolder.getInjector().inject(this);)
My normal workaround for this is to use my application class to help. (I'm a little puzzled by your use of new Application(...). I assume this isn't actually org.apache.wicket.Application.) For example:
public class MyApplication extends AuthenticatedWebApplication implements
ApplicationContextAware {
private ApplicationContext ctx;
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.ctx = applicationContext;
}
public static MyApplication get() {
return (MyApplication) WebApplication.get();
}
public static Object getSpringBean(String bean) {
return get().ctx.getBean(bean);
}
public static <T> T getSpringBean(Class<T> bean) {
return get().ctx.getBean(bean);
}
....
}
In my Spring application context:
<!-- Set up wicket application -->
<bean id="wicketApplication" class="uk.co.humboldt.Project.MyApplication"/>
My helper object then looks up the service on demand:
public class HelperObject {
private Service getService() {
return MyApplication.getSpringBean(Service.class);
}
The best practice would be to create your objects via a factory bean (that has those properties injected by Spring, and have that factory inject those properties to objects it spawns - pure IoC).
You should really avoid using SpringContext all over the place (or any other similar solution for that matter).
Here is a partial list of reasons:
Your code gets coupled with Spring way too much (low-cohesion).
You mix plumbing code with the business-logic.
Your code is less readable.
It's less maintainable (e.g., changing the name of the service bean would lead to code modification - this violates SRP & OCP).
It's less testable (e.g., you need the Spring framework to test it).

Categories

Resources