首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >java Web 项目完整案例实操指南之从入门到精通的项目开发长尾关键词解析与实操指南

java Web 项目完整案例实操指南之从入门到精通的项目开发长尾关键词解析与实操指南

原创
作者头像
啦啦啦191
发布2025-07-28 10:20:52
发布2025-07-28 10:20:52
6320
举报
文章被收录于专栏:Java开发Java开发

JavaWeb项目完整案例实操指南

一、技术选型与环境搭建

(一)技术栈升级

本次项目采用以下最新技术组合:

  • 后端:Spring Boot 3.0 + Spring Security 6.0 + Spring Data JPA + WebFlux
  • 前端:Vue 3 + Vite + TypeScript + Element Plus
  • 数据库:MySQL 8.0 + Redis 7.0
  • 容器化:Docker + Kubernetes 基础部署

(二)项目初始化

使用 Spring Initializr 创建项目,依赖选择:

代码语言:txt
复制
Spring Web
Spring Data JPA
Spring Security
Validation
MySQL Driver
Redis
Springdoc OpenAPI 3

配置 application.yml

代码语言:yaml
复制
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/emp_management?useSSL=false&serverTimezone=UTC
    username: root
    password: yourpassword
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
  redis:
    host: localhost
    port: 6379

二、API 设计与实现

(一)RESTful API 规范

采用以下接口设计标准:

代码语言:txt
复制
GET /api/v1/depts - 获取部门列表
POST /api/v1/depts - 创建部门
GET /api/v1/depts/{id} - 获取单个部门
PUT /api/v1/depts/{id} - 更新部门
DELETE /api/v1/depts/{id} - 删除部门

GET /api/v1/emps - 分页获取员工列表
POST /api/v1/emps - 创建员工
GET /api/v1/emps/{id} - 获取单个员工
PUT /api/v1/emps/{id} - 更新员工
DELETE /api/v1/emps/{id} - 删除员工

(二)部门管理实现

  1. 实体类
代码语言:java
复制
@Entity
@Table(name = "departments")
@Data
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @NotBlank(message = "部门名称不能为空")
    private String name;
    
    private LocalDateTime createTime;
    
    private LocalDateTime updateTime;
    
    @PrePersist
    public void prePersist() {
        this.createTime = LocalDateTime.now();
        this.updateTime = LocalDateTime.now();
    }
    
    @PreUpdate
    public void preUpdate() {
        this.updateTime = LocalDateTime.now();
    }
}
  1. Repository 层
代码语言:java
复制
@Repository
public interface DepartmentRepository extends JpaRepository<Department, Long> {
}
  1. Service 层
代码语言:java
复制
@Service
@Transactional
public class DepartmentServiceImpl implements DepartmentService {
    
    private final DepartmentRepository departmentRepository;
    
    public DepartmentServiceImpl(DepartmentRepository departmentRepository) {
        this.departmentRepository = departmentRepository;
    }
    
    @Override
    public List<Department> getAllDepartments() {
        return departmentRepository.findAll();
    }
    
    @Override
    public Department createDepartment(Department department) {
        return departmentRepository.save(department);
    }
    
    @Override
    public Department getDepartmentById(Long id) {
        return departmentRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Department not found with id: " + id));
    }
    
    @Override
    public Department updateDepartment(Long id, Department departmentDetails) {
        Department department = getDepartmentById(id);
        department.setName(departmentDetails.getName());
        return departmentRepository.save(department);
    }
    
    @Override
    public void deleteDepartment(Long id) {
        Department department = getDepartmentById(id);
        departmentRepository.delete(department);
    }
}
  1. Controller 层
代码语言:java
复制
@RestController
@RequestMapping("/api/v1/depts")
@Api(tags = "部门管理")
public class DepartmentController {
    
    private final DepartmentService departmentService;
    
    public DepartmentController(DepartmentService departmentService) {
        this.departmentService = departmentService;
    }
    
    @GetMapping
    @ApiOperation("获取所有部门")
    public ResponseEntity<List<Department>> getAllDepartments() {
        List<Department> departments = departmentService.getAllDepartments();
        return ResponseEntity.ok(departments);
    }
    
    @PostMapping
    @ApiOperation("创建部门")
    public ResponseEntity<Department> createDepartment(@Valid @RequestBody Department department) {
        Department savedDepartment = departmentService.createDepartment(department);
        return ResponseEntity.created(URI.create("/api/v1/depts/" + savedDepartment.getId()))
                .body(savedDepartment);
    }
    
    // 其他接口方法省略...
}

三、安全认证与授权

(一)JWT 认证实现

  1. 添加依赖
代码语言:xml
复制
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
  1. JWT 工具类
代码语言:java
复制
@Component
public class JwtUtils {
    
    private static final String SECRET_KEY = "your-secret-key-here";
    private static final long EXPIRATION_TIME = 86400000; // 24小时
    
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, SECRET_KEY)
                .compact();
    }
    
    public String extractUsername(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getSubject();
    }
    
    public boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
    
    private boolean isTokenExpired(String token) {
        final Date expiration = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getExpiration();
        return expiration.before(new Date());
    }
}
  1. Spring Security 配置
代码语言:java
复制
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Autowired
    private JwtRequestFilter jwtRequestFilter;
    
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.csrf().disable()
                .authorizeRequests().antMatchers("/api/v1/auth/**").permitAll()
                .anyRequest().authenticated().and()
                .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        
        httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

四、前端实现

(一)Vue 3 项目初始化

代码语言:bash
复制
npm init vite@latest emp-management-frontend -- --template vue-ts
cd emp-management-frontend
npm install
npm install element-plus @element-plus/icons-vue axios

(二)API 调用封装

代码语言:typescript
复制
// src/services/axios.ts
import axios from 'axios';

const service = axios.create({
  baseURL: 'http://localhost:8080/api/v1',
  timeout: 5000
});

// 请求拦截器
service.interceptors.request.use(
  config => {
    const token = localStorage.getItem('token');
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`;
    }
    return config;
  },
  error => {
    console.log(error);
    return Promise.reject(error);
  }
);

// 响应拦截器
service.interceptors.response.use(
  response => {
    return response.data;
  },
  error => {
    console.log('err' + error);
    return Promise.reject(error);
  }
);

export default service;

(三)部门管理页面

代码语言:vue
复制
<!-- src/views/Department.vue -->
<template>
  <div class="department-page">
    <el-card>
      <template #header>
        <div class="header">
          <span>部门管理</span>
          <el-button type="primary" @click="handleCreate">创建部门</el-button>
        </div>
      </template>
      <el-table :data="departments" stripe>
        <el-table-column prop="id" label="ID"></el-table-column>
        <el-table-column prop="name" label="部门名称"></el-table-column>
        <el-table-column prop="createTime" label="创建时间"></el-table-column>
        <el-table-column label="操作">
          <template #default="scope">
            <el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
            <el-button size="small" type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="currentPage"
        :page-sizes="[10, 20, 30]"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total">
      </el-pagination>
    </el-card>
    
    <!-- 创建/编辑对话框 -->
    <el-dialog :visible.sync="dialogVisible" title="部门管理">
      <template #content>
        <el-form :model="formData" ref="formRef" label-width="80px">
          <el-form-item label="部门名称" prop="name">
            <el-input v-model="formData.name" placeholder="请输入部门名称"></el-input>
          </el-form-item>
        </el-form>
      </template>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="handleSave">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script lang="ts" setup>
import { ref, reactive, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import axios from '@/services/axios';

// 数据定义
const departments = ref([]);
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(0);
const dialogVisible = ref(false);
const formData = reactive({
  id: null,
  name: ''
});
const formRef = ref(null);

// 获取部门列表
const fetchDepartments = async () => {
  try {
    const response = await axios.get('/depts', {
      params: {
        page: currentPage.value - 1,
        size: pageSize.value
      }
    });
    departments.value = response.content;
    total.value = response.totalElements;
  } catch (error) {
    ElMessage.error('获取部门列表失败');
  }
};

// 创建部门
const handleCreate = () => {
  formData.id = null;
  formData.name = '';
  dialogVisible.value = true;
};

// 编辑部门
const handleEdit = (row) => {
  formData.id = row.id;
  formData.name = row.name;
  dialogVisible.value = true;
};

// 保存部门
const handleSave = async () => {
  try {
    if (formData.id) {
      // 更新部门
      await axios.put(`/depts/${formData.id}`, formData);
      ElMessage.success('更新部门成功');
    } else {
      // 创建部门
      await axios.post('/depts', formData);
      ElMessage.success('创建部门成功');
    }
    dialogVisible.value = false;
    fetchDepartments();
  } catch (error) {
    ElMessage.error('操作失败');
  }
};

// 删除部门
const handleDelete = async (id) => {
  try {
    await axios.delete(`/depts/${id}`);
    ElMessage.success('删除部门成功');
    fetchDepartments();
  } catch (error) {
    ElMessage.error('删除部门失败');
  }
};

// 分页相关
const handleSizeChange = (newSize) => {
  pageSize.value = newSize;
  fetchDepartments();
};

const handleCurrentChange = (newPage) => {
  currentPage.value = newPage;
  fetchDepartments();
};

onMounted(() => {
  fetchDepartments();
});
</script>

<style scoped>
.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
</style>

五、Docker 容器化部署

(一)创建 Dockerfile

代码语言:dockerfile
复制
# 后端服务
FROM openjdk:17-jdk-alpine
VOLUME /tmp
COPY target/emp-management-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

# 前端服务
FROM node:16-alpine as build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM nginx:1.21.0-alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

(二)Nginx 配置

代码语言:nginx
复制
server {
    listen 80;
    server_name localhost;
    
    location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
        try_files $uri $uri/ /index.html;
    }
    
    location /api/ {
        proxy_pass http://backend:8080/api/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

(三)Docker Compose 配置

代码语言:yaml
复制
version: '3'

services:
  backend:
    build: ./backend
    ports:
      - "8080:8080"
    depends_on:
      - db
      - redis
    environment:
      - SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/emp_management?useSSL=false&serverTimezone=UTC
      - SPRING_DATASOURCE_USERNAME=root
      - SPRING_DATASOURCE_PASSWORD=yourpassword
      - SPRING_REDIS_HOST=redis
      - SPRING_REDIS_PORT=6379

  frontend:
    build: ./frontend
    ports:
      - "80:80"
    depends_on:
      - backend

  db:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=yourpassword
      - MYSQL_DATABASE=emp_management
    volumes:
      - mysql-data:/var/lib/mysql

  redis:
    image: redis:7.0
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data

volumes:
  mysql-data:
  redis-data:

六、测试与监控

(一)单元测试

使用 JUnit 5 和 Mockito 编写测试用例:

代码语言:java
复制
@SpringBootTest
@AutoConfigureMockMvc
class DepartmentControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private DepartmentService departmentService;

    @Test
    void testGetAllDepartments() throws Exception {
        List<Department> departments = Arrays.asList(
                new Department(1L, "技术部", LocalDateTime.now(), LocalDateTime.now()),
                new Department(2L, "市场部", LocalDateTime.now(), LocalDateTime.now())
        );
        
        when(departmentService.getAllDepartments()).thenReturn(departments);
        
        mockMvc.perform(get("/api/v1/depts"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.size()").value(2));
    }
}

(二)集成监控

添加 Micrometer 和 Actuator:

代码语言:xml
复制
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

配置 application.yml

代码语言:yaml
复制
management:
  endpoints:
    web:
      exposure:
        include: "*"
  metrics:
    export:
      prometheus:
        enabled: true

以上就是一个完整的 JavaWeb 项目实操指南,包含了从环境搭建到容器化部署的全过程。项目采用了前后端分离架构,使用了最新的技术栈,实现了部门和员工管理等基本功能,并提供了安全认证、分页查询、异常处理等企业级应用必备的功能。


java Web 项目开发,java 项目完整案例,java 实操指南,java 从入门到精通,Web 项目开发案例,java 项目开发教程,java Web 实战案例,java 项目实操步骤,Web 开发长尾关键词,java 项目案例解析,java Web 入门教程,java 项目开发指南,Web 项目实操案例,java 开发热门关键词,java Web 项目实战

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JavaWeb项目完整案例实操指南
    • 一、技术选型与环境搭建
      • (一)技术栈升级
      • (二)项目初始化
    • 二、API 设计与实现
      • (一)RESTful API 规范
      • (二)部门管理实现
    • 三、安全认证与授权
      • (一)JWT 认证实现
    • 四、前端实现
      • (一)Vue 3 项目初始化
      • (二)API 调用封装
      • (三)部门管理页面
    • 五、Docker 容器化部署
      • (一)创建 Dockerfile
      • (二)Nginx 配置
      • (三)Docker Compose 配置
    • 六、测试与监控
      • (一)单元测试
      • (二)集成监控
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档