Home | 简体中文 | 繁体中文 | 杂文 | Github | 知乎专栏 | Facebook | Linkedin | Youtube | 打赏(Donations) | About
知乎专栏

44.7. Service 单例/多例模式

44.7.1. Service 是单例模式

Service 的变量是共享的,这是与 new Object 的区别。

同步执行

			
package cn.netkiller.service;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
@Slf4j
@Data
public class TestService {

    private String share;

}
			
			
			
    @GetMapping("get")
    public String get() {
        return testService.getShare();
    }

    @GetMapping("set")
    public String set(@RequestParam("value") String value) {
        testService.setShare(value);
        return testService.getShare();
    }			
			
			
			
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -s 'http://localhost:8080/test/set?value=aaa'
aaa⏎                                                                                                                                                                                                                                                
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -s 'http://localhost:8080/test/get'
aaa⏎                                                                                                                                                                                                                                                
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -s 'http://localhost:8080/test/set?value=bbb'
bbb⏎                                                                                                                                                                                                                                                
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -s 'http://localhost:8080/test/get'
bbb⏎                			
			
			

我们可以看到 Service 是 singleton 单例模式

			
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -s 'http://neo:chen@localhost:8080/actuator/beans' |jq '.contexts.[].beans.testService'
{
  "aliases": [],
  "scope": "singleton",
  "type": "cn.netkiller.service.TestService$$SpringCGLIB$$0",
  "resource": "file [/Users/neo/workspace/watch/target/classes/cn/netkiller/service/TestService.class]",
  "dependencies": []
}		
			
			

在多线程或者异步执行的情况会更糟

		
package cn.netkiller.service;

import cn.netkiller.domain.Chat;
import cn.netkiller.repository.ChatRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class TestService {

    private String test;

    @Async
    public void test1() {
        this.test = "Test 1";
    }

    @Async
    public void test2() {
        this.test = "Test 2";
    }

    @Async
    public void test() {
        log.info(this.test);
    }
}

		
			
		
    @Autowired
    private TestService testService;
    		
    @GetMapping("test")
    private Mono<String> test() {
        chatService.test();
        return Mono.just("OK");
    }
    		
    @GetMapping("/test1")
    public Mono<String> test1() {
        String test = "测试";
        chatService.test1();
        return Mono.just(test);
    }

    @GetMapping("/test2")
    public Mono<String> test2() {
        chatService.test2();
        return Mono.just("OK");
    }

		
			
		
2024-01-01T14:09:10.022+08:00  INFO 59782 --- [watch-development] [         task-1] cn.netkiller.service.TestService           : null
2024-01-01T14:09:24.694+08:00  INFO 59782 --- [watch-development] [         task-3] cn.netkiller.service.TestService           : Test 1
2024-01-01T14:10:04.394+08:00  INFO 59782 --- [watch-development] [         task-8] cn.netkiller.service.TestService           : Test 2
		
			

44.7.2. Service 多例实现

要实现 Service 多例模式很简单,只需要在 Service 根 Controller 中同时增加 @Scope("prototype") 即可

			
package cn.netkiller.service;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Scope;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
@Slf4j
@Data
@Scope("prototype")
public class TestService {

    private String share = "default";

    public TestService() {
        this.share = "init";
    }
}			
			
			
			
package cn.netkiller.controller;

import cn.netkiller.ai.AiChain;
import cn.netkiller.annotation.TokenPass;
import cn.netkiller.annotation.TokenVerification;
import cn.netkiller.component.StreamService;
import cn.netkiller.domain.Picture;
import cn.netkiller.domain.PicturePsychoanalysis;
import cn.netkiller.domain.embeddable.StreamTopic;
import cn.netkiller.service.AiService;
import cn.netkiller.service.PicturePsychoanalysisService;
import cn.netkiller.service.TestService;
import cn.netkiller.utils.ResponseJson;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.FluxSink;
import reactor.core.publisher.Mono;
import reactor.core.publisher.ParallelFlux;
import reactor.core.scheduler.Schedulers;

import java.io.IOException;
import java.security.Principal;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.IntStream;
import java.util.stream.Stream;

@RestController
@Slf4j
@RequestMapping("/test")
@Scope(value = "prototype")
public class TestController {
  
    @Autowired
    private TestService testService;

    @GetMapping("get")
    public String get() {
        return testService.getShare();
    }

    @GetMapping("set")
    public String set(@RequestParam("value") String value) {
        testService.setShare(value);
        return testService.getShare();
    }

}
			
			
			
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -s 'http://localhost:8080/test/get'
init⏎                                                                                                                                                                                                                                               
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -s 'http://localhost:8080/test/set?value=bbb'
bbb⏎                                                                                                                                                                                                                                                
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -s 'http://localhost:8080/test/get'
init⏎                                                                                                                                                                                                                                               
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -s 'http://localhost:8080/test/get'
init⏎                                                                                                                                                                                                                                               
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -s 'http://localhost:8080/test/set?value=aaa'
aaa⏎                                                                                                                                                                                                                                                
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -s 'http://localhost:8080/test/get'
init⏎ 
			
			

去掉构造方法

			
	package cn.netkiller.service;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Scope;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
@Slf4j
@Data
@Scope("prototype")
public class TestService {

    private String share = "default";

    public TestService() {
        this.share = "init";
    }
}			
			
			

测试结果

			
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -s 'http://localhost:8080/test/get'
default⏎                                                                                                                                                                                                                                            
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -s 'http://localhost:8080/test/set?value=aaa'
aaa⏎                                                                                                                                                                                                                                                
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -s 'http://localhost:8080/test/get'
default⏎ 	
			
			
			
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -s 'http://neo:chen@localhost:8080/actuator/beans' |jq '.contexts.[].beans.testController'
{
  "aliases": [],
  "scope": "prototype",
  "type": "cn.netkiller.controller.TestController$$SpringCGLIB$$0",
  "resource": "file [/Users/neo/workspace/watch/target/classes/cn/netkiller/controller/TestController.class]",
  "dependencies": [
    "picturePsychoanalysisService",
    "streamService",
    "testService",
    "aiService"
  ]
}
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -s 'http://neo:chen@localhost:8080/actuator/beans' |jq '.contexts.[].beans.testService'
{
  "aliases": [],
  "scope": "prototype",
  "type": "cn.netkiller.service.TestService$$SpringCGLIB$$0",
  "resource": "file [/Users/neo/workspace/watch/target/classes/cn/netkiller/service/TestService.class]",
  "dependencies": []
}