Java Web在线商城项目:最新技术实操指南
随着Java生态的不断发展,构建在线商城的技术栈也在持续更新。本文将采用2023-2024年主流的Java Web技术栈,提供一套完整的在线商城实操方案,包括项目搭建、核心功能实现和部署流程。
一、技术栈选型
本次实战采用当前企业级开发的主流技术组合:
- 后端框架:Spring Boot 3.2.x + Spring Security 6.x
- 数据访问:Spring Data JPA + Hibernate 6.x
- 前端技术:Vue 3 + Vite + Element Plus
- 数据库:MySQL 8.0
- API文档:SpringDoc OpenAPI (Swagger 3)
- 构建工具:Maven 3.9.x
- 部署环境:Docker + Nginx
选择理由:Spring Boot 3.x基于Jakarta EE 9+,带来了更好的性能和新特性;Vue 3的Composition API提供了更灵活的代码组织方式;容器化部署确保了环境一致性。
二、项目初始化
1. 创建Spring Boot后端项目
使用Spring Initializr创建项目(https://start.spring.io/):
- 项目类型:Maven Project
- 语言:Java
- Spring Boot版本:3.2.0
- 打包方式:Jar
- Java版本:17
- 依赖选择:
- Spring Web
- Spring Security
- Spring Data JPA
- MySQL Driver
- SpringDoc OpenAPI Starter WebMVC UI
生成项目后,添加核心依赖到pom.xml
:
<dependencies>
<!-- Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- OpenAPI (Swagger) -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.2.0</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2. 配置应用属性
在application.yml
中配置应用信息:
spring:
datasource:
url: jdbc:mysql://localhost:3306/onlinemall?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
format_sql: true
show-sql: true
server:
port: 8080
springdoc:
api-docs:
path: /api-docs
swagger-ui:
path: /swagger-ui.html
operationsSorter: method
三、核心功能实现
1. 数据库模型设计
使用JPA注解定义实体类,以商品(Product)和订单(Order)为例:
以上代码使用了Lombok的@Data注解简化了getter、setter等方法的编写,使用JPA注解定义了实体与数据库表的映射关系。@PrePersist和@PreUpdate注解用于自动维护创建时间和更新时间。
2. 数据访问层实现
创建Repository接口继承JpaRepository,Spring Data JPA会自动实现基本CRUD操作:
package com.example.mall.repository;
import com.example.mall.model.Product;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
// 按分类ID分页查询商品
Page<Product> findByCategoryId(Long categoryId, Pageable pageable);
// 按名称模糊查询商品
Page<Product> findByNameContainingIgnoreCase(String name, Pageable pageable);
}
Spring Data JPA支持通过方法名自动生成查询语句,如findByCategoryId
会自动生成按categoryId查询的SQL。
3. 服务层实现
以商品服务为例,实现业务逻辑:
package com.example.mall.service;
import com.example.mall.dto.ProductDTO;
import com.example.mall.exception.ResourceNotFoundException;
import com.example.mall.model.Product;
import com.example.mall.repository.ProductRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
public class ProductService {
private final ProductRepository productRepository;
// 获取所有商品(分页)
public Page<Product> getAllProducts(Pageable pageable) {
return productRepository.findAll(pageable);
}
// 根据ID获取商品
public Product getProductById(Long id) {
return productRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Product not found with id: " + id));
}
// 创建商品
@Transactional
public Product createProduct(ProductDTO productDTO) {
Product product = new Product();
product.setName(productDTO.getName());
product.setPrice(productDTO.getPrice());
product.setDescription(productDTO.getDescription());
product.setStock(productDTO.getStock());
product.setImageUrl(productDTO.getImageUrl());
product.setCategoryId(productDTO.getCategoryId());
return productRepository.save(product);
}
// 更新商品
@Transactional
public Product updateProduct(Long id, ProductDTO productDTO) {
Product product = getProductById(id);
product.setName(productDTO.getName());
product.setPrice(productDTO.getPrice());
product.setDescription(productDTO.getDescription());
product.setStock(productDTO.getStock());
product.setImageUrl(productDTO.getImageUrl());
product.setCategoryId(productDTO.getCategoryId());
return productRepository.save(product);
}
// 删除商品
@Transactional
public void deleteProduct(Long id) {
Product product = getProductById(id);
productRepository.delete(product);
}
// 按分类查询商品
public Page<Product> getProductsByCategory(Long categoryId, Pageable pageable) {
return productRepository.findByCategoryId(categoryId, pageable);
}
// 搜索商品
public Page<Product> searchProducts(String keyword, Pageable pageable) {
return productRepository.findByNameContainingIgnoreCase(keyword, pageable);
}
}
服务层使用@Transactional注解管理事务,通过构造函数注入Repository(@RequiredArgsConstructor),实现了商品的CRUD和查询功能。
4. 控制器实现
创建RESTful API控制器:
控制器中使用了SpringDoc的注解(@Tag、@Operation等)生成API文档,使用Spring Security的@PreAuthorize注解控制接口访问权限。PagedResponse是一个通用的分页响应DTO,用于统一API返回格式。
5. 前端Vue 3实现
创建商品列表组件示例:
<template>
<div class="product-list-container">
<el-input
v-model="searchKeyword"
placeholder="搜索商品..."
class="search-input"
@keyup.enter="searchProducts"
>
<template #append>
<el-button @click="searchProducts" icon="Search" />
</template>
</el-input>
<el-row :gutter="20" class="product-grid">
<el-col
:span="6"
v-for="product in products"
:key="product.id"
class="product-card"
>
<el-card :body-style="{ padding: '0px' }">
<img
:src="product.imageUrl || defaultProductImage"
:alt="product.name"
class="product-image"
>
<div class="product-info">
<h3 class="product-name">{
{ product.name }}</h3>
<p class="product-price">¥{
{ product.price.toFixed(2) }}</p>
<el-button
type="primary"
size="small"
class="add-to-cart-btn"
@click="addToCart(product)"
:disabled="product.stock <= 0"
>
{
{ product.stock > 0 ? '加入购物车' : '无库存' }}
</el-button>
</div>
</el-card>
</el-col>
</el-row>
<el-pagination
class="pagination"
@current-change="handlePageChange"
:current-page="currentPage"
:page-size="pageSize"
:total="totalItems"
layout="prev, pager, next, jumper, ->, total"
/>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import axios from 'axios';
// 状态定义
const products = ref([]);
const searchKeyword = ref('');
const currentPage = ref(1);
const pageSize = ref(12);
const totalItems = ref(0);
const defaultProductImage = 'https://picsum.photos/300/300';
// 方法定义
const fetchProducts = async () => {
try {
const response = await axios.get('/api/products', {
params: {
page: currentPage.value - 1,
size: pageSize.value,
sort: 'createdAt,desc'
}
});
products.value = response.data.content;
totalItems.value = response.data.totalElements;
} catch (error) {
ElMessage.error('获取商品列表失败: ' + (error.response?.data?.message || error.message));
}
};
const searchProducts = async () => {
currentPage.value = 1;
try {
const response = await axios.get('/api/products/search', {
params: {
keyword: searchKeyword.value,
page: 0,
size: pageSize.value
}
});
products.value = response.data.content;
totalItems.value = response.data.totalElements;
} catch (error) {
ElMessage.error('搜索商品失败: ' + (error.response?.data?.message || error.message));
}
};
const handlePageChange = (page) => {
currentPage.value = page;
if (searchKeyword.value) {
searchProducts();
} else {
fetchProducts();
}
};
const addToCart = async (product) => {
try {
await axios.post('/api/cart/items', {
productId: product.id,
quantity: 1
});
ElMessage.success('已添加到购物车');
} catch (error) {
ElMessage.error('添加到购物车失败: ' + (error.response?.data?.message || error.message));
}
};
// 初始化
onMounted(() => {
fetchProducts();
});
</script>
<style scoped>
.product-list-container {
padding: 20px;
}
.search-input {
margin-bottom: 20px;
width: 300px;
}
.product-grid {
margin-bottom: 20px;
}
.product-card {
margin-bottom: 20px;
}
.product-image {
width: 100%;
height: 200px;
object-fit: cover;
}
.product-info {
padding: 15px;
}
.product-name {
font-size: 16px;
margin-bottom: 10px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.product-price {
color: #ff4d4f;
font-weight: bold;
margin-bottom: 10px;
}
.add-to-cart-btn {
width: 100%;
}
.pagination {
text-align: center;
}
</style>
这个Vue组件实现了商品列表的展示、搜索、分页和加入购物车功能,使用了Element Plus组件库构建UI,通过axios与后端API交互。
四、安全配置
使用Spring Security 6.x配置认证授权:
package com.example.mall.config;
import com.example.mall.security.JwtAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final UserDetailsService userDetailsService;
private final JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**", "/api/products/**", "/swagger-ui/**", "/api-docs/**").permitAll()
.anyRequest().authenticated()
);
http.authenticationProvider(authenticationProvider());
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
配置使用JWT进行无状态认证,对公开接口(如商品查询)允许匿名访问,其他接口需要认证。使用BCrypt加密密码,增强安全性。
五、部署配置
使用Docker Compose实现容器化部署:
# docker-compose.yml
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: mall-mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: onlinemall
MYSQL_USER: malluser
MYSQL_PASSWORD: mallpassword
volumes:
- mysql-data:/var/lib/mysql
ports:
- "3306:3306"
networks:
- mall-network
backend:
build: ./backend
container_name: mall-backend
restart: always
depends_on:
- mysql
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/onlinemall?useSSL=false&serverTimezone=UTC
SPRING_DATASOURCE_USERNAME: malluser
SPRING_DATASOURCE_PASSWORD: mallpassword
ports:
- "8080:8080"
networks:
- mall-network
frontend:
build: ./frontend
container_name: mall-frontend
restart: always
ports:
- "80:80"
depends_on:
- backend
networks:
- mall-network
networks:
mall-network:
driver: bridge
volumes:
mysql-data:
这个配置文件定义了三个服务:MySQL数据库、后端服务和前端服务,通过Docker网络实现服务间通信,使用数据卷持久化MySQL数据。
六、总结
本文使用最新的Java Web技术栈实现了在线商城的核心功能,包括:
- 基于Spring Boot 3.x和Vue 3的前后端分离架构
- 使用Spring Data JPA简化数据库操作
- 集成Spring Security和JWT实现身份认证
- 使用Swagger生成API文档,便于前后端协作
- 容器化部署,简化环境配置和部署流程
实际项目中,还可以根据需求扩展更多功能,如:
- 集成Redis实现缓存和会话管理
- 添加Elasticsearch实现商品搜索
- 集成消息队列处理订单和库存变更
- 实现分布式事务保证数据一致性
- 添加监控和日志系统
通过本实操指南,你可以快速搭建一个现代化的Java Web在线商城,并根据业务需求进行扩展和优化。
Java Web 在线商城项目,Java Web 最新技术,在线商城项目实操指南,Java Web 项目开发,在线商城项目开发,Java Web 技术实操,在线商城实操指南,开发者 Java Web 学习,Java Web 高效开发,在线商城高效开发,Java Web 商城项目教程,在线商城最新技术,Java Web 项目实操,在线商城项目学习,Java Web 开发指南
代码获取方式
https://pan.quark.cn/s/14fcf913bae6