Why does the service class variables persist on every new hit? [duplicate] - java

This question already has answers here:
What is the default bean scope used by Spring Boot?
(2 answers)
Closed 1 year ago.
I am working on the Spring Boot project. I have a controller which uses my service class. Here is the situation, in my service class I have few class variables which I use as counters, the issue is that whenever I hit my endpoint, the values of my counter variable persists for each hit, meaning if on first request counter value is 1 then on the second hit it becomes 2 so on and on.
Shouldn't the class variables get reset for each new request? I mean the service class should be new object for each request, right?
Here is essentially what I am doing.
My controller:
#Getmapping(path='/learningSpringBoot')
public RequestEntity<String> myMethod() {
myServiceObject.learnSpring();
}
And in my service:
#Service public class Myservice {
private int counter;
public void learnSpring() {
System.out.println("counter : "+ counter);
counter++;
}
}
Please let me know if I'm doing something fundamentally wrong here or absolutely missing some concept.

Shouldn't the class variables get reset for each new request? I mean the service class should be new object for each request, right?
No, the #Service class is instantiated when you start your app since it's a #Bean.
Check out how scopes and different annotations work. By default #Service is #Scope("singleton") so it does not get instantiated per-request and thus the variables are keeping their states.

Related

How to access object without passing it as parameter?

Is there a way to autowire an object that needs to be re-instantiated frequently?
I am using Netflix's DGS + spring boot framework, and basically storing the user authentication details in a custom context which is created for each request. I am trying to avoid adding context to the method signature because of the large amount of refactoring needed.
e.g.
public Result dataFetcher(DataFetchingEnvironment dfe) {
// this context contains user details which is used for authorization
// instantiated for every request
setRolesInContext(dfe);
MyCustomContext context = DgsContext.getCustomContext(dfe);
// trying to avoid adding context as an extra param e.g. dataFetcherHelper(context)
dataFetcherHelper(); // this calls other helper methods from other classes
}
I was thinking of using the facade pattern but this would not be thread safe. Basically autowire the RequestContextHolder, and call setRequestContext each time a new context gets initialized.
#Component
#NoArgsConstructor
#Getter
#Setter
public class RequestContextHolder {
private RequestContext requestContext;
}
I'm not sure how your question:
Is there a way to autowire an object that needs to be re-instantiated frequently?
Is related to the use case that you've presented in the question...
From the question it looks like you can consider using ThreadLocals as a conceptual "substitution" to the global variable available all over the place in the request if you don't want to add parameters to the methods to propagate the context through the flow.
This will work only in "thread-per-request" model, it won't work for reactive systems and for the complicated cases where you maintain different thread pools and switch the threads while implementing the Business Logic on backend:
So to achieve "thread-safety" in your context holder that you have suggested you can use:
#Configuration
public class MyConfig {
#Bean
public ThreadLocal<MyCustomContext> ctxHolder() {
return new ThreadLocal<>();
}
}
Then, again, if you're working in thread-per-request model, you can:
#Component
public class DataFetcherInterceptor {
#Autowired
private ThreadLocal<MyCustomContext> ctxHolder;
public Result dataFetcher(DataFetchingEnvironment dfe) {
// this context contains user details which is used for authorization
// instantiated for every request
setRolesInContext(dfe);
MyCustomContext context = DgsContext.getCustomContext(dfe);
ctxHolder.set(context);
dataFetcherHelper();
}
}
In the dataFetcherHelper or in general in any method that requires the access to the context you can:
public class SomeService {
#Autowired ThreadLocal<MyCustomContext> ctxHolder;
public void dataFetcherHelper() {
MyCustomContext ctx = ctxHolder.get();
}
Now, I see that dataFetcherHelper is just a method that you call from withing this "interceptor" class, in this case its an overkill, but I assume, you've intended that this is actually a method that belongs to another class, that might be an element in the call-chain of different classes. For these situations, this can be a working solution.

Thread safety in Spring Boot with Instance variables [duplicate]

This question already has answers here:
Must Spring component classes be thread-safe
(4 answers)
Closed 1 year ago.
Are methods (without using class variables) thread safe when using Spring Boot ?
I came across carious links where they mentioned instance variables aren't safe always ?
My doubt is how can I create a race condition ? Is the below code thread safe ?And if yes then how can i make it thread - unsafe without using class variables
#RestController
public class GreetingController {
#Autowired
private GreetingService greetingService;
#GetMapping("/hello")
public void greeting(#RequestBody MyUser myUser) throws Exception {
greetingService.getData(myUser);
}
#Service
public class GreetingService {
#Autowired
private DBService dBService;
public void getData (MyUser m ) throws InterruptedException
{
dBService.getData(m);
}
#Repository
public class DBService {
public MyUser getData(MyUser myUser) throws InterruptedException {
System.out.println( "message before: " + myUser.getA() + " Thread : " +Thread.currentThread().getName());
Thread.sleep(18000);
System.out.println( "message after " + myUser.getA() + " Thread : " +Thread.currentThread().getName());
return myUser;
}
In resume: yes, apparently, this code is thread-safe. Since you're using Servlets, each request will be served in a different thread provided by the servlet container (if you're using the default configurations for Spring Boot, the servlet container is a Embedded Tomcat).
Why apparently? Because this code is only thread-safe if the instances of objects declared in the class scope are also thread-safe (i.e GreetingService must be thread-safe)
Take your own example:
The Thread#sleep executed in a first request has no effect on the subsequent ones (i.e other request will not be blocked) because the subsequent ones are served on different threads, as said above.
You will be fine as long as you don't assign new values to the global variables during a request life cycle.
By default (see Does spring dependency injection create singleton objects?), objects injected by spring dependency injection (#Autowired in your case) are singletons.
In your example, there will be exactly one instance each of the controller, service, and repository. So, not inherently thread-safe.
The usual pattern for these classes is as you have shown - use class-level variables only for instances of objects that are themselves thread-safe.
Do not keep any state in class-level variables unless you want that data shared (which is rare), and your code will be thread-safe.
If you do share state on purpose, make sure that you take care to make access to that state thread-safe.

How to invoke a method in a service class with #transactional and #service annotation by reflection?

First, forgive my poor English, I am just working hard on my English:).
I'm trying to find an easy way to set the communication more simple between front-end and back-end, because I use ActiveMQ as the Message Oriented Middleware. So XML string became the request carrier.
For example, front-end send a string request to back-end including package name, class name, method name and parameters list, in this way, back-end is allowed to invoke the correct method by these information, and send invoke result back to front-end. It works, but not perfect. The problem is that when I tried to invoke a method in a service class with #Transational and #Service annotation(which is the common practice to connect to the database),the transaction seemed not being opened, request and response are both received, just left a lot of sleeping connection in mysql database process, as much as the ActiveMQ's consumers every time.
Target method in service class:
#Service
#Transactional
public class UserService {
#Autowired
private IUserDAO udao;
public User getUserByName(String username) {
return udao.findByUsername(username);
}
}
Invoke method(some code has been omitted):
#Component
public class ReflectTool {
public Object invokeMethod(String packageName,String className,String methodName,List paramList) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
Object obj = beanFactory.getBean(packageName+"."+className);
Class cla = obj.getClass();
Method method = findMethod(Class cla,String methodName);
return method.invoke(obj, params);
}
}
I've searched a lot answer, but none of them worked. Like:use a proxy object to invoke but not the target object, cause spring framework has used a proxy class instead the service class with #Transactional annotation to help we manager the transaction, but the code (AopUtils.isAopProxy(obj)) returns true, so it mean the object is exactly a proxy object which I got from the spring context? I'm not very familiar with Dynamic Agent Model.
Thanks for your attention, please tell me if I did something wrong.
Well I was going to this I'd try the following approach:
DON'T use BeanFactory, inject in your ReflectTool the ApplicationContect: #Autowired private ApplicationContext applicationContext; if the beans you want to recover implements some interface or extends a class then maybe you can replace this injection by a Map. Spring will make it work
Try to get the object of the bean you need.
the lines you have regarding the capture of the method and execution should work since it is an bean calling another bean.
Hope this helps. I had a similar situation where I needed to invoke a #Transactional method and I fixed in a similar way I have described.

Spring Async for batch insertion

I'm using spring boot. I was new to spring and started a spring project. So I didn't know about pre defined repositories (JPA, CRUD) which can be easily implemented. In case, I wanted to save a bulk data, so I use for loop and save one by one, Its taking more time. So I tried to use #Async. But it doesn't also work, is my concept wrong?
#Async has two limitation
it must be applied to public methods only
self-invocation – calling the async method from within the same class won’t work
1) Controller
for(i=0;i < array.length();i++){
// Other codes
gaugeCategoryService.saveOrUpdate(getEditCategory);
}
2) Dao implementation
#Repository
public class GaugeCategoryDaoImpl implements GaugeCategoryDao {
// Other codings
#Async
#Override
public void saveOrUpdate(GaugeCategory GaugeCategory) {
sessionFactory.getCurrentSession().saveOrUpdate(GaugeCategory);
}
}
After removing #Async , it working normally. But with that annotation it doesn't work. Is there any alternative method for time consuming? Thanks in advance.
the #Async annotation creates a thread for every time you call that method. but you need to enable it in your class using this annotation #EnableAsync
You also need to configure the asyncExecutor Bean.
You can find more details here : https://spring.io/guides/gs/async-method/
In my opinion, there are several issues with your code:
You overwrite the saveOrUpdate() method without any need to do so. A simple call to "super()" should have been enough to make #Async work.
I guess that you somewhere (within your controller class?) declare a transactional context. That one usually applies to the current thread. By using #Async, you might leave this transaction context as (because of the async DAO execution), the main thread may already be finished when saveOrUpdate() is called. And even though I currently don't know it exactly, there is a good change that the declared transaction is only valid for the current thread.
One possble fix: create an additional component like AsyncGaugeCategoryService or so like this:
#Component
public class AsyncGaugeCategoryService {
private final GaugeCategoryDao gaugeCategoryDao;
#Autowired
public AsyncGaugeCategoryService(GaugeCategoryDao gaugeCategoryDao) {
this.gaugeCategoryDao = gaugeCategoryDao;
}
#Async
#Transactional
public void saveOrUpdate(GaugeCategory gaugeCategory) {
gaugeCategoryDao.saveOrUpdate(gaugeCategory);
}
}
Then inject the service instead of the DAO into your controller class. This way, you don't need to overwrite any methods, and you should have a valid transactional context within your async thread.
But be warned that your execution flow won't give you any hint if something goes wrong while storing into the database. You'll have to check the log files to detect any problems.

Guice: How can I cache data

I am new to Guice and have a question regarding caching.
I have a WebApp which uses a provider to provide user configurations.
bind(UserConfiguration.class).toProvider(UserConfigurationProvider.class).in(ServletScopes.REQUEST);
The provider is declared as:
public class UserConfigurationProvider implements Provider<UserConfiguration>{
public UserConfiguration get() {
userConfigurationDatabase.getUserConfiguration(<some param here>);
}
}
In the get method, it calls DB and make a very expensive DB call.
I have a number of classes which get UserConfiguration injected like:
public class PriceView {
private UserConfiguration userConfiguration;
#Inject
public MyClass(UserConguration userConfiguration){
this.userConfiguration = userConfiguration;
}
.....
}
public class OrderView {
private UserConfiguration userConfiguration;
#Inject
public MyClass(UserConguration userConfiguration){
this.userConfiguration = userConfiguration;
}
........
}
The problem is that every time I switch a view, a new xxxxView object is created and hence a new UserConfiguration is injected and hence the expensive DB call is called.
I want to make calling the DB limited to once per user login. My idea is that cache the UserConfiguration somewhere when a user login and clear it after user logout or it expires.
But I want to know whehter Guice offers something more clever ways of caching about this use case?
Many thanks.
If you're using standard servlet session, then it should be enough to use ServletScopes.SESSION scope instead of ServletScopes.REQUEST. In this case the injected object should be created only once per session.
However, if you are not using standard servlet session but something custom, then you also need to create custom scope.

Categories

Resources