Java Web 在线商城项目最新技术实操指南帮助开发者高效完成商城项目开发

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 本项目基于Spring Boot 3.2与Vue 3构建现代化在线商城,涵盖技术选型、核心功能实现、安全控制与容器化部署,助开发者掌握最新Java Web全栈开发实践。

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技术栈实现了在线商城的核心功能,包括:

  1. 基于Spring Boot 3.x和Vue 3的前后端分离架构
  2. 使用Spring Data JPA简化数据库操作
  3. 集成Spring Security和JWT实现身份认证
  4. 使用Swagger生成API文档,便于前后端协作
  5. 容器化部署,简化环境配置和部署流程

实际项目中,还可以根据需求扩展更多功能,如:

  • 集成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


相关文章
|
9天前
|
人工智能 运维 安全
|
7天前
|
人工智能 异构计算
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
|
8天前
|
机器学习/深度学习 人工智能 自然语言处理
B站开源IndexTTS2,用极致表现力颠覆听觉体验
在语音合成技术不断演进的背景下,早期版本的IndexTTS虽然在多场景应用中展现出良好的表现,但在情感表达的细腻度与时长控制的精准性方面仍存在提升空间。为了解决这些问题,并进一步推动零样本语音合成在实际场景中的落地能力,B站语音团队对模型架构与训练策略进行了深度优化,推出了全新一代语音合成模型——IndexTTS2 。
672 23
|
8天前
|
人工智能 测试技术 API
智能体(AI Agent)搭建全攻略:从概念到实践的终极指南
在人工智能浪潮中,智能体(AI Agent)正成为变革性技术。它们具备自主决策、环境感知、任务执行等能力,广泛应用于日常任务与商业流程。本文详解智能体概念、架构及七步搭建指南,助你打造专属智能体,迎接智能自动化新时代。
|
14天前
|
人工智能 JavaScript 测试技术
Qwen3-Coder入门教程|10分钟搞定安装配置
Qwen3-Coder 挑战赛简介:无论你是编程小白还是办公达人,都能通过本教程快速上手 Qwen-Code CLI,利用 AI 轻松实现代码编写、文档处理等任务。内容涵盖 API 配置、CLI 安装及多种实用案例,助你提升效率,体验智能编码的乐趣。
1103 110
|
人工智能 数据可视化 数据挖掘
Quick BI 体验&征文有奖!
瓴羊生态推出Quick BI 征文激励计划,鼓励用户分享数据分析实践经验与技术洞察,征集高质量原创文章。内容围绕AI功能体验与BI案例实践,设季奖、年奖及参与奖,优秀作者可获现金奖励、产品内测资格及官方认证形象。投稿截止至2026年3月31日。
Quick BI 体验&征文有奖!