Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to understand Spring singleton

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article introduces the relevant knowledge of "how to understand Spring singletons". In the operation of actual cases, many people will encounter such a dilemma. Then let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

Talk about martial arts.

This is caused by a real bug, and the reason for bug is that the singleton pattern of Spring Bean is ignored. Come on, let's look at a simple piece of code.

Public class TestService {private String callback = "https://ip.com/token={token}"; public String getCallback () {Random random = new Random (); int number = random.nextInt (100); System.out.println (" this random number is: "+ number); callback = callback.replace (" {token} ", String.valueOf (number)); return callback } public static void main (String [] args) {TestService testService = new TestService (); while (true) {Scanner reader = new Scanner (System.in); int number = reader.nextInt (); if (number > 0) {String url = testService.getCallback (); System.out.println (url) }

Callback is an address with a callback, and the parameter token is uncertain.

Each time the getCallback method is called, it randomly generates a number less than 100, and then replaces {token} in callback with this random number, and the final format looks like this:

Https://ip.com/token=88

Then receive console input in the main method, each time the input number is greater than 0, call the getCallback method, and then output url.

I believe you can easily see the output of this program.

After executing the program, no matter how many times you enter the number, the final output callback is the first one.

Although the random number generated each time has changed, the callback has not changed.

In fact, it is a single case.

Some students said, you have gone too far, can I not know why?

The main method creates only one instance of TestService, and the string callback is changed to https://ip.com/token=89 the first time the getCallback method is called, so no matter how many times you call it, the replace action will not be executed, because there is no {token} section in callback.

TestService is a singleton during the execution of the program, so after the callback is modified for the first time, it will be executed later.

Callback.replace ("{token}", String.valueOf (number))

There is no {token} in the callback you get, so there will be no replacement action.

Of course, this is only the simplest procedure to illustrate the problem in the singleton, and if you want to use the singleton in a real project, you have to use the singleton design pattern to achieve it.

Go back to that bug.

There is a younger brother doing Wechat service number development, Wechat service number or Subscription account has an access_token concept, this is the voucher of all requests, valid for 2 hours, before expiration to refresh.

He designed this: when the project starts, he immediately calls Wechat API to get access_token, then writes a scheduled task that is refreshed every hour, and the obtained access_token is put into redis and database. When other APIs of Wechat service account are called, access_token is obtained in redis and spliced into the interface address.

The development and debugging went smoothly together, and it looked perfect.

There's a problem.

Problems arise when the project is deployed to the test environment for testing. When the project was released, the tests were normal, but after a period of time, an error occurred. When you check the log, it is found that the API of the Wechat service number has returned an error code, which means the access_token has expired and needs to be obtained again.

The younger brother suspected at first that there was a problem with the timing task, but through the update time in the log and database, he found that there was no problem with the scheduled task, and the time to refresh the access_token was exactly the same as the scheduled task, indicating that it had been refreshed in time.

I asked him to call the service number interface with redis or access_token in the database to see if there was the same expiration problem.

As a result, the storage in redis is fine and can be used normally.

That completely eliminates the problem of timing tasks, and the crux of the problem should lie in two places:

1. The process of obtaining access_token in redis

2. An error occurred when splicing the acquired access_token to the request interface URL

It is easy to judge here. He output the access_token and the final stitched URL from redis to the log. Sure enough, the two are inconsistent.

It is true that the latest available access_token was taken out of redis, but after splicing into interface URL, it was found to be another one. It is no problem to make sure that you get the access_token, but there is a problem in the final splicing of the URL. At this time, the younger brother examined the code carefully and was completely confused.

Talk about martial arts.

Now that you've determined where the problem is, just analyze the code.

The project as a whole uses Spring Boot, and the code is very simple, which is to call a method in Service in a Controller. Demo is roughly like this.

@ RestController@RequestMapping (value = "test") public class TestController {@ Autowired private TestService testService; @ GetMapping (value = "call") public Object getCallback () {return testService.getCallback ();}} @ Servicepublic class TestService {private String callback = "https://ip.com/token={token}"; public String getCallback () {Random random = new Random (); int number = random.nextInt System.out.println ("this random number is:" + number); callback = callback.replace ("{token}", String.valueOf (number)); return callback;}}

Seeing here, you must have found the cause of the problem. Although there are multiple requests, because Spring Bean is singleton mode by default, it is actually similar to the console program demonstrated earlier, with only one instance of TestService from beginning to end, so you can only replace {token} with a real access_token for the first time.

Corresponding to the actual service number scenario, it is no problem to get the access_token from redis to the specific URL when the API is called for the first time, but once the access_token expires (1 hour later), the access_token expiration occurs when you request this API again.

This violates a point of Spring singleton mode, which is Spring singleton mode, which is not suitable for storing stateful values. For example, callback here is a stateful value, which should be obtained with the progress of scheduled tasks.

For an introduction to the Spring or Spring Boot workflow, you can read two articles at the end of the article, including the Bean instantiation process.

Modification proposal

How to solve this problem?

In fact, it is very simple to keep callback from changing every time it is called. Each time you concatenate URL, assign callback to a local variable, and then operate on that variable.

Public String getCallback () {Random random = new Random (); int number = random.nextInt (100); System.out.println ("this random number is: + number); String tempCallback = callback; tempCallback = tempCallback.replace (" {token} ", String.valueOf (number)); return tempCallback;}

In addition, when it comes to the Spring singleton pattern, Spring itself supports several other patterns, corresponding to the singleton pattern is the prototype pattern, which regenerates the instance for each request. So, if you are sure that the Controller and Service do not need the singleton mode, you can add the @ Scope (value = "prototype") annotation.

@ RestController@RequestMapping (value = "test") @ Scope (value = "prototype") public class TestController {@ Autowired private TestService testService; @ GetMapping (value = "call") public Object getCallback () {return testService.getCallback ();}} @ Service@Scope (value = "prototype") public class TestService {private String callback = "https://ip.com/token={token}"; public String getCallback () {Random random = new Random () Int number = random.nextInt; System.out.println ("this random number is:" + number); callback = callback.replace ("{token}", String.valueOf (number)); return callback;}}

In this way, each time it is a new example, and naturally there is no such problem.

This is the end of the content of "how to understand Spring singles". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report