Stateless Service Layer in Spring - spring

These days im working on a Web project and i just want to clarify couple of things regarding Spring bean scopes and best practices for Spring based developments. Here i am using a scenario using a sample code
I have a Web Controller as below
#Controller
Public class JobController{
private JobService jobService;
#Autowired
public void setJobService(JobService jobService ) {
this.jobService = jobService ;
}
public void run(){
Job job = new Job();
-- Setting the properties for the Object
jobService.run(job);
}
}
Then I have the Service as below
#Service
Public class JobService {
public void run(Job job){
-- perform the business logic
}
}
In Here i want to make the JobService class stateless so i can define JobService as singleton hence reduce the unnecessary object creation. As per my understanding in-order make a class stateless we do not want to keep instance properties.In This scenario i pass different Job objects to the service. Does this make this JobService statefull because JObservice process different different job objects? Can you please help me to understand
Thanks,
Keth

Passing different objects does not make your service stateful.
Consider this for example.
#Service
Public class JobService {
private Job currentJob;
public void setJob(Job job) {
currentJob = job;
}
public void run(){
-- perform the business logic on currentJob
}
}
This would make the bean 'stateful' and cause unexplained behavior.
The execution of the method in your singleton by multiple controller/threads will not collide and can be assumed to be safe.

Related

How to call #Scheduled method in only one instance using #PostConstruct

There is job that needs to be done on cron schedule
The same logic as in the job must be performed at the start of the spring boot application, therefore #PostConstruct method is used
Shedlock is used, since it is planned to run the application in multiple instances
The question is: how to make the logic from the #PostConstruct method be called in only one instance and not called in others?
An example of my code:
#Component
#AllArgsConstructor
public class TestJob {
private TestService testService;
#PostConstruct
public void init() {
testService.upload();
}
#Scheduled(cron = "${cron}")
#SchedulerLock(name = "uploadJob", lockAtMostFor = "${lockAtMostFor}")
public void execute() {
testService.upload();
}
}
It should work if you put the #PostConstruct method to a different service and call the execute() method.
The reason is that ShedLock by default uses Spring AOP which wraps each method marked by #SchedulerLock in an aspect that does the locking. And Spring AOP usually does not get applied if you call another method in the same class.

How to publish a bean via static method in Spring

I'm working on a legacy Spring-Boot application where I would like to use dependency injection with some code that exists outside the application context. One a part of the application comes as a separate JAR-file and cannot be modified. But I am able to modify some classes that are instantiated in that part. Here how I'm planning to do this:
class ServiceHolder {
private static FooService fooService;
public static FooService getFooService() { return fooService; }
public static void setFooService(FooService service) { fooService = service; }
}
#Bean
#Profile("production")
FooService fooService() {
var service = new ProductionFooService();
ServiceHolder.setFooService(service);
return service;
}
public class LegacyPojo {
private final FooService fooService;
public LegacyPojo() {
fooService = ServiceHolder.getFooService();
}
//.. some business logic
}
I'm worried about possible visibility problems when different requests in separate threads will call new LegacyPojo() and reach for FooService instance.
So my question is: should I declare ServiceHolder#getFooService and ServiceHolder#setFooService synchronized or not?
There is a lot of others things you can do that with security, dont you think that you could have the instance of FooService into LegacyPojo passing it by constructor, it will be less coupled.
Other thing that you can do is to control the instances of FooService, you may do it as a singleton, declaring it as a static property on ServiceHolder and not to have a setMethod. I think, the way you told, you want a single instance of FooService.
Even LegacyPojo being a Pojo, you dont need to create a getter for FooService.
Once you use ServiceHolder.setFooService(service); you may do a implementation like this:
class ServiceHolder{
private static FooService fooService;
public static void setFooService(FooService newFooService){
if(fooService== null){
fooService = newFooService;
}
}
}
So that way, you will set only the first instance of FOoService and it will not be changed, of course you can do any condition to setFooService in ServiceHolder
It would work without any synchronization because singleton bean will be instantiated in a critical section inside synchronized block. In DefaultSingletonBeanRegistry class there is a method getSingleton which, according to the doc:
/**
* Return the (raw) singleton object registered under the given name,
* creating and registering a new one if none registered yet.
* ...
*/
And at the very beginning of this method, the critical section starts synchronized (this.singletonObjects). So the effect of ServiceHolder.setFooService(service) call will be visible to all threads after leaving the critical section.

How to configure spring-ioc dependency Injection in a personal way?

I'm currently working on a web application using Angular 7 for front-end, spring-boot for back-end (in which I'm developing a restful web service).
I'm using #Autowired annotation to inject my services into each other and my rest controller. The problem is that in some of my services, there are some attributes that get shared when the injection is done. How do I prevent that?
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
#Service
public class ServiceA {
private boolean test;
public ServiceA (){
test = true;
}
public changeValues(){
test = false;
}
}
#Service
public class ServiceB {
#Autowired
private ServiceA serivceA;
public void method1() {
serviceA.changeValues();
}
}
#Service
public class ServiceC {
#Autowired
private ServiceA serivceA;
public void method2(){
if(serviceA.getTest()){
doSomethingNeeded();
}
}
}
public class Application{
#Autowired
private ServiceB b;
#Autowired
private ServiceC c;
public static void main(String[] args) {
b.method1();
c.method2();
}
}
In this case the method doSomethingNeeded() in ServiceC won't be able to be executed because the ressource 'test' of ServiceA is shared between both ServiceB and ServiceC. How do I prevent That?
P.S. In my case, the dependency injections are way too complex for applying any modifications to the services, that's why I need a way to configure spring-ioc dependency injection in a way to create instances of private attributes to each client session.
Spring Beans are by default Singletons and the should not contain state.
Single Page Applications (like you create with Angular) should anyway hold the state on the client side and pass the information with every request.
The main reason is when your backend is stateless it's easy to scale and is more reliable because if a backend service is restarted you don't loose anything.
You just need change the scope of ServiceA to prototype by adding #Scope(scopeName = "prototype"):
#Scope(scopeName = "prototype")
#Service
public class ServiceA {
}
Then when ServiceB and ServiceC are instantiated , separated ServiceA will be created and inject into them.
P.S. Please note that a new instance of prototype bean will only be created during ServiceB and ServiceC are instantiated. It does not mean that a new instance of prototype bean will always be created whenever you access them. You need to use one of these technique if you want such behaviour.

Is spring boot aspect working on method inside the scheduled method

For spring boot application.
I have my aspect listen on my private or public method inside my scheduled method.
But it doesn't work. However, the aspect can listen on my scheduled method.
Here is an example on my github.
https://github.com/benweizhu/spring-boot-aspect-scheduled
Does any know the answer or why? or how to resolve it?
Thanks
Aspects will not work on calling other methods within the same class as it cannot be proxied.
It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.
Okay, so what is to be done about this? The best approach (the term best is used loosely here) is to refactor your code such that the self-invocation does not happen
note on proxying private methods :
Due to the proxy-based nature of Spring’s AOP framework, protected methods are by definition not intercepted, neither for JDK proxies (where this isn’t applicable) nor for CGLIB proxies (where this is technically possible but not recommendable for AOP purposes). As a consequence, any given pointcut will be matched against public methods only!
If your interception needs include protected/private methods or even constructors, consider the use of Spring-driven native AspectJ weaving instead of Spring’s proxy-based AOP framework. This constitutes a different mode of AOP usage with different characteristics, so be sure to make yourself familiar with weaving first before making a decision.
refer : How can I log private methods via Spring AOP?
change the code as below:
#Component
public class Test{
public void reportInPrivateMethod() {
System.out.println("private method");
}
public void reportInPublicMethod() {
System.out.println("public method");
}
}
Now invoke this method :
#Component
public class ScheduledTasks {
#Autowired
private Test test;
private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
#Scheduled(fixedRate = 5000)
public void reportCurrentTime() {
test.reportInPrivateMethod();
test.reportInPublicMethod();
log.info("The time is now {}", dateFormat.format(new Date()));
}
}
Modify the aspects as per the changes :
#Aspect
#Component
public class Monitor {
#AfterReturning("execution(* com.zeph.aop.ScheduledTasks.reportCurrentTime())")
public void logServiceAccess(JoinPoint joinPoint) {
System.out.println("Completed: " + joinPoint);
}
#AfterReturning("execution(* com.zeph.aop.Test.reportInPrivateMethod())")
public void logServiceAccessPrivateMethod() {
System.out.println("Completed PRIVATE :");
}
#AfterReturning("execution(* com.zeph.aop.Test.reportInPublicMethod())")
public void logServiceAccessPublicMethod() {
System.out.println("Completed PUBLIC: ");
}
}

#Autowired in static classes

This is an Spring MVC project with Hibernate.
I'm, trying to make a Logger class that, is responsible for inputting logs into database.
Other classes just call proper methods with some attributes and this class should do all magic.
By nature it should be a class with static methods, but that causes problems with autowiring dao object.
public class StatisticLogger {
#Autowired
static Dao dao;
public static void AddLoginEvent(LogStatisticBean user){
//TODO code it god damn it
}
public static void AddDocumentEvent(LogStatisticBean user, Document document, DocumentActionFlags actionPerformed){
//TODO code it god damn it
}
public static void addErrorLog(Exception e, String page, HashMap<String, Object> parameters){
ExceptionLogBean elb=new ExceptionLogBean();
elb.setStuntDescription(e);
elb.setSourcePage(page);
elb.setParameters(parameters);
if(dao!=null){ //BUT DAO IS NULL
dao.saveOrUpdateEntity(elb);
}
}
How to make it right? What should I do not to make dao object null?
I know that I could pass it as a method parameter, but that isn't very good.
I'm guessing that autowired can't work on static objects, because they are created to early to autowiring mechanism isn't created yet.
You can't #Autowired a static field. But there is a tricky skill to deal with this:
#Component
public class StatisticLogger {
private static Dao dao;
#Autowired
private Dao dao0;
#PostConstruct
private void initStaticDao () {
dao = this.dao0;
}
}
In one word, #Autowired a instance field, and assign the value to the static filed when your object is constructed. BTW, the StatisticLogger object must be managed by Spring as well.
Classical autowiring probably won't work, because a static class is not a Bean and hence can't be managed by Spring. There are ways around this, for example by using the factory-method aproach in XML, or by loading the beans from a Spring context in a static initializer block, but what I'd suggest is to change your design:
Don't use static methods, use services that you inject where you need them. If you use Spring, you might as well use it correctly. Dependency Injection is an Object Oriented technique, and it only makes sense if you actually embrace OOP.
I know this is an old question but just wanted to share what I did,
the solution by #Weibo Li is ok but the problem it raises Sonar Critical alert about assigning non static variable to a static variable
the way i resolved it with no sonar alerts is the following
I change the StatisticLogger to singlton class (no longer static)
like this
public class StatisticLogger {
private static StatisticLogger instance = null;
private Dao dao;
public static StatisticLogger getInstance() {
if (instance == null) {
instance = new StatisticLogger();
}
return instance;
}
protected StatisticLogger() {
}
public void setDao(Dao dao) {
this.dao = dao;
}
public void AddLoginEvent(LogStatisticBean user){
//TODO code it god damn it
}
public void AddDocumentEvent(LogStatisticBean user, Document document, DocumentActionFlags actionPerformed){
//TODO code it god damn it
}
public void addErrorLog(Exception e, String page, HashMap<String, Object> parameters){
ExceptionLogBean elb=new ExceptionLogBean();
elb.setStuntDescription(e);
elb.setSourcePage(page);
elb.setParameters(parameters);
if(dao!=null){
dao.saveOrUpdateEntity(elb);
}
}
I created a service(or Component) that autowire the service that i want and set it in the singlton class
This is safe since in spring it will initialize all the managed beans before doing anything else and that mean the PostConstruct method below is always called before anything can access the StatisticLogger
something like this
#Component
public class DaoSetterService {
#Autowired
private Dao dao0;
#PostConstruct
private void setDaoValue () {
StatisticLogger.getInstance().setDao(dao0);
}
}
Instead of using StatisticLogger as static class I just use it as StatisticLogger.getInstance() and i can access all the methods inside it
You can pass the DAO to StatisticLogger from where you call it.
public static void AddLoginEvent(LogStatisticBean user, DAO dao){
dao.callMethod();
}
It might be too late to put an answer to this question, especially when a question is already having an accepted answer. But it might help others in case they face the same issue.
inside the StatisticLogger class create an instance of the Dao service.
public static Dao daoService = new Dao();
then, auto-wire the service instance through the constructor of the StatisticLogger class.
#Autowired
public functionName(Dao daoService0) {
this.daoService = daoService0;
}
//use this service as usual in static class
daoService.fun();
I think this is the simplest solution for the problem.

Resources