Build a Spring Boot 3 Web QA App with Vector Store and AI in Minutes
This guide walks through creating a Spring Boot 3 application that loads web pages into a vector database using Spring AI, configures AI models and embeddings, and exposes REST endpoints for storing documents and answering queries with retrieval‑augmented generation.
1. Introduction
This article demonstrates how to implement a web‑page question‑answering service using Spring Boot 3.4.2 and Spring AI. A given URL is loaded, its content stored in a vector database, and queries are answered by retrieving relevant chunks and sending them to a large language model.
2. Practical Example
2.1 Prepare Environment
Add the required Maven dependencies:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>
<!-- Ollama BGE‑M3 model for embeddings -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
<!-- Milvus vector store -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-milvus</artifactId>
</dependency>
<!-- Alibaba DashScope (optional) -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-autoconfigure-dashscope</artifactId>
<version>1.0.0.2</version>
</dependency>
<!-- Jsoup for web page parsing -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.18.3</version>
</dependency>Configure Spring AI properties (YAML style):
spring:
ai:
dashscope:
api-key: sk-xxxooo
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
chat:
options:
model: qwen-turbo
embedding:
enabled: false
---
spring:
ai:
ollama:
base-url: http://localhost:11111
embedding:
model: bge-m3:latest
---
spring:
ai:
vectorstore:
milvus:
client:
host: localhost
port: 19530
username: xxx
password: ooo
initialize-schema: true
databaseName: "mydb"
collectionName: "mydocs"
autoId: true
id-field-name: id
embeddingDimension: 1024Exclude conflicting auto‑configurations:
@SpringBootApplication(
exclude = {
OllamaChatAutoConfiguration.class,
DashScopeEmbeddingAutoConfiguration.class
}
)
public class SpringBootAiWebQaApplication {}2.2 Document Reader
Implement DocumentReader using Jsoup to fetch a page and convert it to a Spring AI Document:
public class WebDocumentReader implements DocumentReader {
private static final Logger logger = LoggerFactory.getLogger(WebDocumentReader.class);
private final String url;
public WebDocumentReader(String url) { this.url = url; }
@Override
public List<Document> get() {
try {
X509ExtendedTrustManager trustManager = createTrustManager();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{trustManager}, new SecureRandom());
org.jsoup.nodes.Document doc = Jsoup.connect(url)
.sslSocketFactory(sslContext.getSocketFactory())
.get();
Map<String, Object> metadata = Map.of(
"source", url,
"date", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now())
);
return List.of(new Document(doc.body().text(), metadata));
} catch (Exception e) {
logger.error("Failed to load [{}]: {}", this.url, e);
return List.of();
}
}
private X509ExtendedTrustManager createTrustManager() {
return new X509ExtendedTrustManager() {
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
// other methods omitted for brevity
};
}
}2.3 Write Documents to Vector Store
@Service
public class WebDocumentService {
private final VectorStore vectorStore;
public WebDocumentService(VectorStore vectorStore) { this.vectorStore = vectorStore; }
public void save(String url) {
WebDocumentReader reader = new WebDocumentReader(url);
List<Document> docs = reader.get();
TokenTextSplitter splitter = new TokenTextSplitter(1000, 400, 10, 5000, true);
List<Document> chunks = splitter.apply(docs);
this.vectorStore.add(chunks);
}
}Spring AI provides several text splitters, e.g., TokenTextSplitter, ContentFormatTransformer, KeywordMetadataEnricher, and SummaryMetadataEnricher.
2.4 Configure ChatClient
@Configuration
public class ChatConfig {
@Bean
ChatClient chatClient(ChatClient.Builder builder, VectorStore vectorStore) {
PromptTemplate promptTemplate = new PromptTemplate("""
Context:
---------------------
{context}
---------------------
Answer the query without prior knowledge.
Rules:
1. If the answer is not in the context, reply \"I don't know\".
2. Avoid phrases like \"According to the context...\".
Query: {query}
Answer:
""");
QueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder()
.promptTemplate(promptTemplate)
.build();
Advisor retrievalAdvisor = RetrievalAugmentationAdvisor.builder()
.queryAugmenter(queryAugmenter)
.documentRetriever(VectorStoreDocumentRetriever.builder()
.similarityThreshold(0.40)
.topK(1)
.vectorStore(vectorStore)
.build())
.build();
List<Advisor> advisors = List.of(new SimpleLoggerAdvisor(), retrievalAdvisor);
return builder.defaultAdvisors(advisors).build();
}
}The RetrievalAugmentationAdvisor performs similarity search in the vector store and feeds the retrieved text together with the user query to the LLM.
2.5 Controller Endpoints
@RestController
@RequestMapping("/web")
public class WebController {
private final WebDocumentService webDocumentService;
public WebController(WebDocumentService webDocumentService) { this.webDocumentService = webDocumentService; }
@GetMapping("/load")
public ResponseEntity<?> load(String url) {
this.webDocumentService.save(url);
return ResponseEntity.ok("success");
}
}
@RestController
@RequestMapping("/chat")
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient chatClient) { this.chatClient = chatClient; }
@GetMapping
public ResponseEntity<?> chat(String prompt) {
String answer = this.chatClient.prompt(prompt).call().content();
return ResponseEntity.ok(answer);
}
}2.6 Testing
1. Use the /web/load endpoint to store a web page (e.g., a CCTV news article) into Milvus. 2. Query the /chat endpoint with a question; the system retrieves the most relevant chunk and returns the AI‑generated answer. The console shows successful storage and retrieval.
All steps above constitute the complete tutorial for building a Spring Boot 3 web‑QA application powered by Spring AI and Milvus.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
