Issue
Service Class
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class StudentService{
@Cacheable(value = "student",key = "{#id,#name}")
public Student getStudentByID(String id,String name)
{
return new Student(id,name ,"V");
}
}
ThreadExample Class
public class ThreadExample extends Thread{
@Autowired
StudentService studentService;
@Override
public void run() {
studentService.getStudentByID("3","f");
}
}
I have both class and start the threads as :
for (int i = 0; i < 24; i++) {
new ThreadExample().start();
}
The problem is cacheable annotation does not work here , because it is not a spring bean class. But the things I try to do just execute one time getStudentByID(String id,String name) function with same id and name. How I can do that ? Do you have any idea. Thank you
Solution
StudentService
in ThreadExample
class would be null
because;
Spring only autowires components the components it creates. You are calling
new ThreadExample()
, Spring doesn't know about this object so no auto-wiring will take place. See https://stackoverflow.com/a/42502608/2039546
There may be 2 different methods for this, depending on your use case;
The first and fastest option is to send the service to the
ThreadExample
class with the constructor. You can do this easily.Other option when you create
ThreadExample
ask app context to do your auto-wiring. Let me give an example of this.
The pseudocode will be something like this;
@Controller
public class TestController {
private final ApplicationContext applicationContext;
public TestController(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@GetMapping(value = "/test")
public void test() {
ThreadExample threadExample = new ThreadExample();
applicationContext.getAutowireCapableBeanFactory()
.autowireBean(threadExample);
for (int i = 0; i < 24; i++) {
Thread thread = new Thread(threadExample);
thread.start();
}
}
}
public class ThreadExample implements Runnable {
@Autowired
private StudentService studentService;
@Override
public void run() {
long threadId = Thread.currentThread().getId();
String threadName = Thread.currentThread().getName();
System.out.println(String
.format("Thread has been called! [threadId=%s, threadName=%s]",
threadId, threadName));
Student student = studentService.getStudentByID("3", "f");
System.out.println(String
.format("Thread has been completed. [threadId=%s, threadName=%s, studentId=%s]",
threadId, threadName, student.getId()));
}
}
@Service
public class StudentService {
@Cacheable(value = "students", key = "{#id, #name}", sync = true)
public Student getStudentByID(String id, String name) {
System.out.println(String
.format("getStudentById() has been called! [id=%s, name=%s]", id, name));
return new Student(id, name , "V");
}
}
Notice the sync = true
attribute which tells the framework to block any concurrent threads while the value is being computed. This will make sure that this intensive operation is invoked only once in case of concurrent access.
The console output is:
Thread has been called! [threadId=40, threadName=Thread-7]
Thread has been called! [threadId=41, threadName=Thread-8]
Thread has been called! [threadId=42, threadName=Thread-9]
Thread has been called! [threadId=43, threadName=Thread-10]
.
.
.
getStudentById() has been called! [id=3, name=f] <- ONLY WORKED ONCE!
Thread has been completed. [threadId=54, threadName=Thread-21, studentId=3]
Thread has been completed. [threadId=55, threadName=Thread-22, studentId=3]
Thread has been completed. [threadId=57, threadName=Thread-24, studentId=3]
Thread has been completed. [threadId=47, threadName=Thread-14, studentId=3]
Thread has been completed. [threadId=42, threadName=Thread-9, studentId=3]
.
.
.
Answered By - İsmail Y.
Answer Checked By - David Goodson (JavaFixing Volunteer)