首页 > 编程语言 >使用Spring AI和LLM生成Java测试代码

使用Spring AI和LLM生成Java测试代码

时间:2024-10-27 16:32:22浏览次数:1  
标签:Java String AI Spring 测试 LLM test class name

image

背景

      AIDocumentLibraryChat 项目已扩展至生成测试代码(Java 代码已通过测试)。该项目可为公开的 Github 项目生成测试代码。只需提供要测试的类的网址,该类就会被加载、分析导入,项目中的依赖类也会被加载。这样,LLM 就有机会在为测试生成模拟时考虑导入的源类。可以提供 testUrl,为 LLM 提供一个示例,以便生成测试。我们已使用 Ollama 对 granite-codedeepseek-coder-v2codestral 模型进行了测试。

image

目的是测试 LLM 对开发人员创建测试的帮助有多大。

实施

配置


要选择 LLM 模型,需要更新 application-ollama.properties 文件

spring.ai.ollama.base-url=${OLLAMA-BASE-URL:http://localhost:11434}
spring.ai.ollama.embedding.enabled=false
spring.ai.embedding.transformer.enabled=true
document-token-limit=150
embedding-token-limit=500
spring.liquibase.change-log=classpath:/dbchangelog/db.changelog-master-ollama.xml

...

# generate code
#spring.ai.ollama.chat.model=granite-code:20b
#spring.ai.ollama.chat.options.num-ctx=8192

spring.ai.ollama.chat.options.num-thread=8
spring.ai.ollama.chat.options.keep_alive=1s

#spring.ai.ollama.chat.model=deepseek-coder-v2:16b
#spring.ai.ollama.chat.options.num-ctx=65536

spring.ai.ollama.chat.model=codestral:22b
spring.ai.ollama.chat.options.num-ctx=32768

spring.ai.olama.chat.model "选择要使用的 LLM 代码模型。

spring.ollama.chat.options.num-ctx "设置上下文窗口中的令牌数量。上下文窗口包含请求所需的令牌和响应所需的令牌。

如果 Ollama 没有选择正确的内核数量,可以使用 “spring.olama.chat.options.num-thread”。spring.olama.chat.options.keep_alive "设置了保留上下文窗口的秒数。

Controller


Controller是获取信号源和生成测试的接口:

@RestController
@RequestMapping("rest/code-generation")
public class CodeGenerationController {
   private final CodeGenerationService codeGenerationService;

  public CodeGenerationController(CodeGenerationService
     codeGenerationService) {
     this.codeGenerationService = codeGenerationService;
   }

  @GetMapping("/test")
   public String getGenerateTests(@RequestParam("url") String url,
     @RequestParam(name = "testUrl", required = false) String testUrl) {
     return this.codeGenerationService.generateTest(URLDecoder.decode(url,
       StandardCharsets.UTF_8),
     Optional.ofNullable(testUrl).map(myValue -> URLDecoder.decode(myValue,
       StandardCharsets.UTF_8)));
   }

  @GetMapping("/sources")
   public GithubSources getSources(@RequestParam("url") String url,
     @RequestParam(name="testUrl", required = false) String testUrl) {
     var sources = this.codeGenerationService.createTestSources(
       URLDecoder.decode(url, StandardCharsets.UTF_8), true);
     var test = Optional.ofNullable(testUrl).map(myTestUrl ->
       this.codeGenerationService.createTestSources(
         URLDecoder.decode(myTestUrl, StandardCharsets.UTF_8), false))
           .orElse(new GithubSource("none", "none", List.of(), List.of()));
     return new GithubSources(sources, test);
   }
}

代码生成控制器 “有一个 ”getSources(...) "方法。该方法获取要生成测试的类的 URL 和可选的 testUrl,以及可选的示例测试。它对请求参数进行解码,并调用 “createTestSources(...) ”方法。该方法会返回 “GithubSources”,其中包含要测试的类的源代码、其在项目中的依赖关系以及测试示例。

getGenerateTests(...) “方法获取测试类的 ”url “和可选的 ”testUrl “以进行 url 解码,并调用 ”CodeGenerationService “的 ”generateTests(...) "方法。

Services


CodeGenerationService "从 Github 收集类,并为被测类生成测试代码。

带有提示的服务看起来像这样:

@Service
public class CodeGenerationService {
   private static final Logger LOGGER = LoggerFactory
     .getLogger(CodeGenerationService.class);
   private final GithubClient githubClient;
   private final ChatClient chatClient;
   private final String ollamaPrompt = """
     You are an assistant to generate spring tests for the class under test.
     Analyse the classes provided and generate tests for all methods. Base 
     your tests on the example.
     Generate and implement the test methods. Generate and implement complete 
     tests methods.
     Generate the complete source of the test class.
                     
     Generate tests for this class:
     {classToTest}

    Use these classes as context for the tests:
     {contextClasses}

    {testExample}
   """;   
   private final String ollamaPrompt1 = """
     You are an assistant to generate a spring test class for the source
     class.
     1. Analyse the source class
     2. Analyse the context classes for the classes used by the source class
     3. Analyse the class in test example to base the code of the generated
        test class on it.
     4. Generate a test class for the source class and use the context classes
        as sources for creating the test class.
     5. Use the code of the test class as test example.
     6. Generate tests for each of the public methods of the source class.
     Generate the complete source code of the test class implementing the
     tests.       

    {testExample}

    Use these context classes as extension for the source class:
     {contextClasses}
            
     Generate the complete source code of the test class implementing the 
     tests.
     Generate tests for this source class:
     {classToTest}   
   """;
   @Value("${spring.ai.ollama.chat.options.num-ctx:0}")
   private Long contextWindowSize;

  public CodeGenerationService(GithubClient githubClient, ChatClient
     chatClient) {
     this.githubClient = githubClient;
     this.chatClient = chatClient;
   }

这是带有 “GithubClient ”和 “ChatClient ”的 “CodeGenerationService”。GithubClient 用于从公开可用的资源库加载源代码,而 ChatClient 是 Spring AI 接口,用于访问 AI/LLM。

ollamaPrompt "是 IBM Granite LLM 的提示符,上下文窗口有 8k 个 token。classToTest}(测试类)用被测类的源代码代替。{contextClasses}“可以替换为被测类的从属类,”{testExample}"是可选项,可以替换为测试类,作为代码生成的示例。

olamaPrompt2 "是 Deepseek Coder V2 和 Codestral LLM 的提示符。这些 LLM 可以 “理解 ”或使用思维链提示,其上下文窗口超过 32k 个词组。{...}“占位符的工作原理与 ”olamaPrompt "相同。由于上下文窗口较长,因此可以为代码生成添加上下文类。

Spring 注入了 “contextWindowSize ”属性,用于控制 LLM 的上下文窗口是否足够大,以便在提示符中添加“{contextClasses}”。

我们也可抽取提示词,完善后,单独测试下

https://lxblog.com/qianwen/share?shareId=6ebef3fc-1c25-4abf-aa11-7122e540e964

示例提示词

You are an assistant to generate a spring test class for the source class.
1. Analyse the source class
2. Analyse the context classes for the classes used by the source class
3. Analyse the class in test example to base the code of the generated test class on it.
4. Generate a test class for the source class and use the context classes as sources for creating the test class.
5. Use the code of the test class as test example.
6. Generate tests for each of the public methods of the source class.

Your additional guidelines:
1.Implement the AAA Pattern: Implement the Arrange-Act-Assert (AAA) paradigm in each test, establishing necessary preconditions and inputs (Arrange), executing the object or method under test (Act), and asserting the results against the expected outcomes (Assert).
2.Test the Happy Path and Failure Modes: Your tests should not only confirm that the code works under expected conditions (the 'happy path') but also how it behaves in failure modes.
3.Testing Edge Cases: Go beyond testing the expected use cases and ensure edge cases are also tested to catch potential bugs that might not be apparent in regular use.
4.Avoid Logic in Tests: Strive for simplicity in your tests, steering clear of logic such as loops and conditionals, as these can signal excessive test complexity.
5.Leverage TypeScript's Type System: Leverage static typing to catch potential bugs before they occur, potentially reducing the number of tests needed.
6.Handle Asynchronous Code Effectively: If your test cases involve promises and asynchronous operations, ensure they are handled correctly.
7.Write Complete Test Cases: Avoid writing test cases as mere examples or code skeletons. You have to write a complete set of tests. They should effectively validate the functionality under test.

Generate the complete source code of the test class implementing the tests.                       

{
@ExtendWith(MockitoExtension.class)
public class MockitoHelloTest {

  @Mock
   RecordDao mockDao;

  @Mock
   NotificationService mockNotification;

  @Mock
   SequenceGenerator mockGenerator;

  @InjectMocks
   RecordService service;

  @Test
   public void testSaveRecord() {

    Record record = new Record();
     record.setName("Test Record");
     when(mockGenerator.getNext()).thenReturn(100L);
     when(mockDao.saveRecord(record)).thenReturn(record);
     Record savedRecord = service.saveRecord(record);
     verify(mockGenerator, times(1)).getNext();
     verify(mockDao, times(1)).saveRecord(any(Record.class));
     assertEquals("Test Record", savedRecord.getName());
     assertEquals(100L, savedRecord.getId());
   }
}
}

Use these context classes as extension for the source class:
{

package com.app.login.domain;

@Entity
@Table(name = "app_user")
@Getter
@Setter
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;

    @NotNull
     @Pattern(regexp = Constants.LOGIN_REGEX)
     @Size(min = 1, max = 50)
     @Column(length = 50, unique = true, nullable = false)
     private String login;

    @JsonIgnore
     @NotNull
     @Size(min = 60, max = 60)
     @Column(name = "password_hash", length = 60)
     private String password;

    @Size(max = 50)
     @Column(name = "first_name", length = 50)
     private String firstName;

    @Size(max = 50)
     @Column(name = "last_name", length = 50)
     private String lastName;

    @Size(min = 5, max = 100)
     @Column(length = 100, unique = true)
     private String email;

    @Column(name = "created_date")
     private Instant createdDate = Instant.now();

    @NotNull
     @Column(nullable = false)
     private boolean activated = false;

    @Size(min = 2, max = 5)
     @Column(name = "lang_key", length = 5)
     private String langKey;

    @Size(max = 256)
     @Column(name = "image_url", length = 256)
     private String imageUrl;

    @Size(max = 20)
     @Column(name = "activation_key", length = 20)
     @JsonIgnore
     private String activationKey;

    @Size(max = 20)
     @Column(name = "reset_key", length = 20)
     @JsonIgnore
     private String resetKey;

    @Column(name = "reset_date")
     private Instant resetDate = null;

    @Size(min = 7, max = 15)
     @Column(name = "ip_address", length = 15)
     String ipAddress;

    @JsonIgnore
     @ManyToMany
     @JoinTable(name = "app_user_authority", joinColumns = { @JoinColumn(name = "user_id", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "authority_name", referencedColumnName = "name") })
     @BatchSize(size = 20)
     private Set<Authority> authorities = new HashSet<>();


     @Override
     public boolean equals(Object o) {
         if (this == o) {
             return true;
         }
         if (o == null || getClass() != o.getClass()) {
             return false;
         }

        User user = (User) o;

        return login.equals(user.login);
     }

    @Override
     public int hashCode() {
         return login.hashCode();
     }

}


@Entity
@Table(name = "app_authority")
public class Authority implements Serializable {

    private static final long serialVersionUID = 1L;

    @NotNull
     @Size(min = 0, max = 50)
     @Id
     @Column(length = 50)
     private String name;

    public String getName() {
         return name;
     }

    public void setName(String name) {
         this.name = name;
     }

    @Override
     public boolean equals(Object o) {
         if (this == o) {
             return true;
         }
         if (o == null || getClass() != o.getClass()) {
             return false;
         }

        Authority authority = (Authority) o;

        return !(name != null ? !name.equals(authority.name) : authority.name != null);
     }

    @Override
     public int hashCode() {
         return name != null ? name.hashCode() : 0;
     }
}


package com.app.login.repository;
public interface UserRepository extends JpaRepository<User, Long> {

    Optional<User> findOneByActivationKey(String activationKey);

    Optional<User> findOneByResetKey(String resetKey);

    Optional<User> findOneByEmail(String email);

    Optional<User> findOneByLogin(String login);

    @EntityGraph(attributePaths = "authorities")
     User findOneWithAuthoritiesById(Long id);

    @EntityGraph(attributePaths = "authorities")
     Optional<User> findOneWithAuthoritiesByLogin(String login);

    Page<User> findAllByLoginNot(Pageable pageable, String login);

    List<User> findAllByIpAddressAndCreatedDateBetween(String ipAddress, Instant startDate, Instant currentDate);
}

package com.app.login.security;
public class UserNotActivatedException extends AuthenticationException {

    private static final long serialVersionUID = 1L;

    public UserNotActivatedException(String message) {
         super(message);
     }

    public UserNotActivatedException(String message, Throwable t) {
         super(message, t);
     }
}

}

Generate the complete source code of the test class implementing the tests.
Generate tests for this source class:

package com.app.login.security;

import com.app.login.domain.User;
import com.app.login.repository.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;


@Component("userDetailsService")
@Slf4j
public class DomainUserDetailsServiceImpl implements UserDetailsService {

    private final UserRepository userRepository;

    public DomainUserDetailsServiceImpl(UserRepository userRepository) {
         this.userRepository = userRepository;
     }

    @Override
     @Transactional(rollbackFor = Exception.class)
     public UserDetails loadUserByUsername(final String login) {
         if (login==null){
             throw  new UsernameNotFoundException("User login is null");
         }
         log.debug("Authenticating {}", login);
         String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
         Optional<User> userFromDatabase = userRepository.findOneWithAuthoritiesByLogin(lowercaseLogin);
         return userFromDatabase.map(user -> {
             if (!user.getActivated()) {
                 throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated");
             }
             List<GrantedAuthority> grantedAuthorities = user.getAuthorities()
                 .stream()
                 .map(authority -> new SimpleGrantedAuthority(authority.getName()))
                 .collect(Collectors.toList());
             return new org.springframework.security.core.userdetails.User(lowercaseLogin, user.getPassword(), grantedAuthorities);
         })
             .orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the " + "database"));
     }
}

}

方法 “createTestSources(...) ”收集并返回 AI/LLM 提示的源代码:

public GithubSource createTestSources(String url, final boolean
   referencedSources) {
   final var myUrl = url.replace("
https://github.com",
     GithubClient.GITHUB_BASE_URL).replace("/blob", "");
   var result = this.githubClient.readSourceFile(myUrl);
   final var isComment = new AtomicBoolean(false);
   final var sourceLines = result.lines().stream().map(myLine ->
       myLine.replaceAll("[\t]", "").trim())
     .filter(myLine -> !myLine.isBlank()).filter(myLine ->
       filterComments(isComment, myLine)).toList();
   final var basePackage = List.of(result.sourcePackage()
     .split("\\.")).stream().limit(2)
     .collect(Collectors.joining("."));
   final var dependencies = this.createDependencies(referencedSources, myUrl,
     sourceLines, basePackage);
   return new GithubSource(result.sourceName(), result.sourcePackage(),
     sourceLines, dependencies);
}

private List<GithubSource> createDependencies(final boolean
   referencedSources, final String myUrl, final List<String> sourceLines,
   final String basePackage) {
   return sourceLines.stream().filter(x -> referencedSources)
     .filter(myLine -> myLine.contains("import"))
     .filter(myLine -> myLine.contains(basePackage))
     .map(myLine -> String.format("%s%s%s",
       myUrl.split(basePackage.replace(".", "/"))[0].trim(),
     myLine.split("import")[1].split(";")[0].replaceAll("\\.",
           "/").trim(), myUrl.substring(myUrl.lastIndexOf('.'))))
     .map(myLine -> this.createTestSources(myLine, false)).toList();
}

private boolean filterComments(AtomicBoolean isComment, String myLine) {
   var result1 = true;
   if (myLine.contains("/*") || isComment.get()) {
     isComment.set(true);
     result1 = false;
   }
   if (myLine.contains("*/")) {
     isComment.set(false);
     result1 = false;
   }
   result1 = result1 && !myLine.trim().startsWith("//");
   return result1;
}

方法 “createTestSources(...) ”使用 Github 源代码 “url”,并根据 “referencedSources ”的值提供项目中依赖类的源代码 “GithubSource ”记录。

为此,我们创建了 “myUrl ”来获取类的原始源代码。然后使用 “githubClient ”以字符串形式读取源文件。然后使用 “filterComments(...) ”方法将源代码字符串转换为不带格式和注释的源代码行。

要读取项目中的依赖类,需要使用基础包。例如,在 “ch.xxx.aidoclibchat.usecase.service ”包中,基础包是 “ch.xxx"。方法 “createDependencies(...) ”用于为基础软件包中的依赖类创建 “GithubSource ”记录。使用 “basePackage ”参数筛选出类,然后递归调用 “createTestSources(...) ”方法,并将参数 “referencedSources ”设为 false 以停止递归。这就是依赖类 “GithubSource ”记录的创建过程。

generateTest(...) "方法用于使用 AI/LLM 创建被测类的测试源:

public String generateTest(String url, Optional<String> testUrlOpt) {
   var start = Instant.now();
   var githubSource = this.createTestSources(url, true);
   var githubTestSource = testUrlOpt.map(testUrl ->
     this.createTestSources(testUrl, false))
       .orElse(new GithubSource(null, null, List.of(), List.of()));
   String contextClasses = githubSource.dependencies().stream()
     .filter(x -> this.contextWindowSize >= 16 * 1024)
     .map(myGithubSource -> myGithubSource.sourceName() + ":"  +
       System.getProperty("line.separator")   
       + myGithubSource.lines().stream()
         .collect(Collectors.joining(System.getProperty("line.separator")))
       .collect(Collectors.joining(System.getProperty("line.separator")));
   String testExample = Optional.ofNullable(githubTestSource.sourceName())
     .map(x -> "Use this as test example class:" +
       System.getProperty("line.separator") + 
       githubTestSource.lines().stream()
         .collect(Collectors.joining(System.getProperty("line.separator"))))
     .orElse("");
   String classToTest = githubSource.lines().stream()
     .collect(Collectors.joining(System.getProperty("line.separator")));
   LOGGER.debug(new PromptTemplate(this.contextWindowSize >= 16 * 1024 ?
     this.ollamaPrompt1 : this.ollamaPrompt, Map.of("classToTest",
       classToTest, "contextClasses", contextClasses, "testExample",
       testExample)).createMessage().getContent());
   LOGGER.info("Generation started with context window: {}", 
     this.contextWindowSize);
   var response = chatClient.call(new PromptTemplate(
     this.contextWindowSize >= 16 * 1024 ? this.ollamaPrompt1 : 
       this.ollamaPrompt, Map.of("classToTest", classToTest, "contextClasses",
       contextClasses, "testExample", testExample)).create());
   if((Instant.now().getEpochSecond() - start.getEpochSecond()) >= 300) {
     LOGGER.info(response.getResult().getOutput().getContent());
   }
   LOGGER.info("Prompt tokens: " +
     response.getMetadata().getUsage().getPromptTokens());
   LOGGER.info("Generation tokens: " +
     response.getMetadata().getUsage().getGenerationTokens());
   LOGGER.info("Total tokens: " +
     response.getMetadata().getUsage().getTotalTokens());
   LOGGER.info("Time in seconds: {}", (Instant.now().toEpochMilli() -
     start.toEpochMilli()) / 1000.0);
   return response.getResult().getOutput().getContent();
}

为此,我们使用 “createTestSources(...) ”方法创建包含源代码行的记录。然后创建字符串 “contextClasses ”来替换提示符中的“{contextClasses}”占位符。如果上下文窗口小于 16k 字节,则字符串为空,以便为被测类和测试示例类提供足够的字节。然后创建可选的 “testExample ”字符串,以替换提示符中的“{testExample}”占位符。如果没有提供 “testUrl”,则字符串为空。然后创建 “classToTest ”字符串来替换提示符中的“{classToTest}”占位符。

调用 “chatClient ”将提示发送到人工智能/LLM。提示将根据 “contextWindowSize ”属性中上下文窗口的大小进行选择。PromptTemplate "会用准备好的字符串替换占位符。

响应 "用于记录提示标记、生成标记和总标记的数量,以便检查上下文窗口边界是否得到遵守。然后记录生成测试源的时间,并返回测试源。如果生成测试源的时间超过 5 分钟,则会记录测试源,以防止浏览器超时。


通过测试,两种模型都能生成 Spring 控制器测试和 Spring 服务测试。测试网址如下

http://localhost:8080/rest/code-generation/test?url=https://github.com/Angular2Guy/MovieManager/blob/master/backend/src/main/java/ch/xxx/moviemanager/adapter/controller/ActorController.java&testUrl=https://github.com/Angular2Guy/MovieManager/blob/master/backend/src/test/java/ch/xxx/moviemanager/adapter/controller/MovieControllerTest.java

http://localhost:8080/rest/code-generation/test?url=https://github.com/Angular2Guy/MovieManager/blob/master/backend/src/main/java/ch/xxx/moviemanager/usecase/service/ActorService.java&testUrl=https://github.com/Angular2Guy/MovieManager/blob/master/backend/src/test/java/ch/xxx/moviemanager/usecase/service/MovieServiceTest.java

Ollama 上的 “granite-code:20b ”LLM 的上下文窗口只有 8k 个 token。这对于提供 “contextClasses ”并有足够的标记进行响应来说太小了。这意味着 LLM 只能使用被测类和测试示例。

Ollama 上的 “deepseek-coder-v2:16b ”和 “codestral:22b ”LLM 的上下文窗口超过 32k 个 token。这使得 “contextClasses”(上下文类)可以添加到提示中,从而使模型可以与思维链提示一起工作。

测试结果


Granite-Code LLM 能够为 Spring 服务测试生成错误但有用的基础。没有一个测试成功,但缺失的部分可以用缺失的上下文类来解释。Spring Controller 测试则不太理想。它遗漏了太多代码,无法作为有用的基础。在中等配置的笔记本电脑上生成测试花费了 10 多分钟。

Deepseek-Coder-V2 LLM 能够创建一个 Spring 服务测试,其中大部分测试都能正常工作。这是一个很好的工作基础,缺失的部分很容易修复。Spring Controller 测试的错误较多,但也是一个有用的起点。在中等配置的笔记本电脑 CPU 上生成测试不到十分钟。

Codestral LLM 能够创建一个 Spring 服务测试,只有一个测试失败。这个更复杂的测试需要一些修正。Spring Controller 测试只有 1 个失败的测试用例,但这是因为缺少一个配置调用,导致测试成功而没有进行测试。两个生成的测试都是一个很好的起点。在中等配置的笔记本电脑 CPU 上生成测试花费了半个多小时。

结论


Deepseek-Coder-V2 和 Codestral LLM 可以帮助编写 Spring 应用程序的测试。Codestal 是更好的模型,但需要更多的处理能力和内存。这两个模型都需要 GPU 加速,才能用于生产。即使有可用的上下文类,LLM 也无法正确创建非琐碎代码。LLM 所能提供的帮助非常有限,因为 LLM 并不理解代码。代码对 LLM 来说只是字符,如果不了解语言语法,结果就不会令人印象深刻。开发人员必须能够修复测试中的所有错误,并添加断言等缺失部分。这意味着它只是节省了一些键入测试的时间。

Github Copilot 的使用体验与 Granite-Code LLM 类似。在 2024 年 9 月,上下文窗口太小,无法很好地生成代码,代码自动补全建议也经常被忽略。

LLM 是否有帮助 -> 是的

LLM 是否节省大量时间 -> 不是



今天先到这儿,希望对云原生,技术领导力, 企业管理,系统架构设计与评估,团队管理, 项目管理, 产品管理,信息安全,团队建设 有参考作用 , 您可能感兴趣的文章:
构建创业公司突击小团队
国际化环境下系统架构演化
微服务架构设计
视频直播平台的系统架构演化
微服务与Docker介绍
Docker与CI持续集成/CD
互联网电商购物车架构演变案例
互联网业务场景下消息队列架构
互联网高效研发团队管理演进之一
消息系统架构设计演进
互联网电商搜索架构演化之一
企业信息化与软件工程的迷思
企业项目化管理介绍
软件项目成功之要素
人际沟通风格介绍一
精益IT组织与分享式领导
学习型组织与企业
企业创新文化与等级观念
组织目标与个人目标
初创公司人才招聘与管理
人才公司环境与企业文化
企业文化、团队文化与知识共享
高效能的团队建设
项目管理沟通计划
构建高效的研发与自动化运维
某大型电商云平台实践
互联网数据库架构设计思路
IT基础架构规划方案一(网络系统规划)
餐饮行业解决方案之客户分析流程
餐饮行业解决方案之采购战略制定与实施流程
餐饮行业解决方案之业务设计流程
供应链需求调研CheckList
企业应用之性能实时度量系统演变

如有想了解更多软件设计与架构, 系统IT,企业信息化, 团队管理 资讯,请关注我的微信订阅号:

image_thumb2_thumb_thumb_thumb_thumb[1]

作者:Petter Liu
出处:http://www.cnblogs.com/wintersun/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 该文章也同时发布在我的独立博客中-Petter Liu Blog。

标签:Java,String,AI,Spring,测试,LLM,test,class,name
From: https://www.cnblogs.com/wintersun/p/18508577

相关文章

  • 基于springboot+vue的高校就业管理系统,
    基于springboot+vue的高校就业管理系统,分为管理员:测试账号:10086/123学生:测试账号:10087/123  包含个人信息、查看企业岗位信息、简历信息管理、我的应聘企业:测试账号:10070/123  包含企业信息、岗位企业信息管理、查看学生简历信息、应聘信息管理辅导员:测试账号:100......
  • 【开题报告】基于Springboot+vue爱心捐赠系统(程序+源码+论文) 计算机毕业设计
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在当今社会,随着经济的快速发展和人民生活水平的不断提升,越来越多的人开始关注并参与到公益事业中来。然而,传统的捐赠方式往往存在信息不对称、流程繁......
  • Java学习十六—掌握注解:让编程更简单
    一、关于注解1.1简介Java注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。​初学者可以这样理解注解:想像代码具有生命,注解就是对于代码中某些鲜活个体的贴上去......
  • SpringCloud Alibaba 06 (配置中心 Nacos Config)
    目录了解微服务常用的概念了解项目架构演变掌握微服务环境搭建掌握微服务治理组件-NacosDiscovery掌握微服务负载均衡调度组件-Ribbon掌握微服务远程调度组件-Feign掌握微服务流控容错组件-Sentinel掌握微服务网关组件-Gateway掌握微服务链路追踪组件-Sleuth&Zipkin掌握......
  • SpringCloud Alibaba 01 (微服务的概念理解,微服务的环境搭建,微服务的服务注册中心Nacos
    目录了解微服务常用的概念了解项目架构演变掌握微服务环境搭建掌握微服务治理组件-NacosDiscovery掌握微服务负载均衡调度组件-Ribbon掌握微服务远程调度组件-Feign掌握微服务流控容错组件-Sentinel掌握微服务网关组件-Gateway掌握微服务链路追踪组件-Sleuth&Zi......
  • java+vue计算机毕设高校毕业生就业竞争力分析及应用【开题+程序+论文+源码】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着高等教育的普及与深化,高校毕业生数量逐年增加,就业市场竞争日益激烈。在这一背景下,高校毕业生的就业竞争力成为了社会关注的焦点。面对多元化的就......
  • 【开题报告】基于Springboot+vue医院药品管理系统(程序+源码+论文) 计算机毕业设计
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在现代医疗体系中,医院药品管理是保证医疗质量和安全的重要环节。随着医疗技术的不断进步和医疗需求的日益增长,医院药品的种类和数量不断增加,药品管理......
  • Java的引用是什么
    Java中的引用是一种关联,可以让我们通过引用操作或访问对象。Java有四种类型的引用:强引用、软引用、弱引用和虚引用,它们在对象的生命周期、垃圾回收等方面有着不同的作用。理解引用的特性对于写出优质的Java代码至关重要。强引用是最常见的引用。如果一个对象具有强引用,那么垃圾收......
  • (附项目源码)Java开发语言,springboot 校园羽毛球馆预约管理系统,计算机毕设程序开发+文案
    摘  要随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。校园羽毛球馆预约管理系统,主要的模块包括管理员;首页、网站管理(轮播图、场馆公告)人员管理(管理员、普通用户、教练用户)内容管理(体育新闻、新闻分类列表)模......
  • (附项目源码)Java开发语言,springboot 测评成绩统计及分析模块的设计与实现 39,计算机毕设
    摘 要测评成绩统计及分析模块是综合素质测评系统的设计与实现中的关键部分。通过该模块,可以对学生的各项测评成绩进行统计和分析,从而了解学生的综合素质水平。这对于高校和学生来说都具有重要的研究意义和实践价值。传统的考试成绩只能反映学生的学习能力,而综合素质测评系......