本文目录导读:

- 项目结构
- Maven配置 (pom.xml)
- 主应用程序
- 实体类设计
- 请求/响应DTO
- Repository层
- 安全配置
- 服务层
- 异步消息服务
- Redis缓存服务
- Controller层
- 异常处理
- 配置文件
- 测试类
- Docker Compose 配置
- 特性说明
我将为您设计一个完整的Java系统整合案例,展示现代Java企业级应用的核心技术栈。
项目结构
java-integration-demo/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/com/example/integration/
│ │ │ ├── config/
│ │ │ ├── controller/
│ │ │ ├── service/
│ │ │ ├── repository/
│ │ │ ├── entity/
│ │ │ ├── dto/
│ │ │ ├── security/
│ │ │ └── exception/
│ │ └── resources/
│ └── test/
Maven配置 (pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.8</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>integration-demo</artifactId>
<version>1.0.0</version>
<name>System Integration Demo</name>
<properties>
<java.version>11</java.version>
<jjwt.version>0.11.5</jjwt.version>
<lombok.version>1.18.24</lombok.version>
</properties>
<dependencies>
<!-- Spring Boot Starters -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- RabbitMQ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- Database -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jjwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jjwt.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>${jjwt.version}</version>
<scope>runtime</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- Swagger/OpenAPI -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
主应用程序
package com.example.integration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableCaching
@EnableAsync
@EnableScheduling
public class IntegrationDemoApplication {
public static void main(String[] args) {
SpringApplication.run(IntegrationDemoApplication.class, args);
}
}
实体类设计
package com.example.integration.entity;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;
@Data
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
private String password;
@Column(name = "full_name")
private String fullName;
@Column(name = "phone")
private String phone;
@Column(name = "status")
private boolean enabled = true;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles = new HashSet<>();
@Column(name = "last_login_time")
private LocalDateTime lastLoginTime;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
}
package com.example.integration.entity;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import javax.persistence.*;
@Data
@Entity
@Table(name = "roles")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String name;
@Column(name = "description")
private String description;
}
请求/响应DTO
package com.example.integration.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import javax.validation.constraints.*;
public class UserDTO {
@Data
public static class RegisterRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 50, message = "用户名长度必须在3-50之间")
private String username;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
@NotBlank(message = "密码不能为空")
@Size(min = 6, max = 100, message = "密码长度必须在6-100之间")
private String password;
@NotBlank(message = "确认密码不能为空")
private String confirmPassword;
@Size(max = 100, message = "姓名长度不能超过100")
private String fullName;
@Pattern(regexp = "^$|^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
}
@Data
public static class LoginRequest {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
private String password;
}
@Data
public static class UserResponse {
private Long id;
private String username;
private String email;
private String fullName;
private String phone;
private boolean enabled;
private Set<String> roles;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public static UserResponse fromEntity(User user) {
UserResponse response = new UserResponse();
response.setId(user.getId());
response.setUsername(user.getUsername());
response.setEmail(user.getEmail());
response.setFullName(user.getFullName());
response.setPhone(user.getPhone());
response.setEnabled(user.isEnabled());
response.setRoles(user.getRoles().stream()
.map(Role::getName)
.collect(Collectors.toSet()));
response.setCreatedAt(user.getCreatedAt());
response.setUpdatedAt(user.getUpdatedAt());
return response;
}
}
@Data
public static class AuthResponse {
private String token;
private String refreshToken;
private String tokenType = "Bearer";
private long expiresIn;
private UserResponse user;
}
}
Repository层
package com.example.integration.repository;
import com.example.integration.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
Optional<User> findByEmail(String email);
Boolean existsByUsername(String username);
Boolean existsByEmail(String email);
@Query("SELECT u FROM User u WHERE u.enabled = true AND u.lastLoginTime < :before")
List<User> findInactiveUsers(@Param("before") LocalDateTime before);
@Modifying
@Query("UPDATE User u SET u.lastLoginTime = :now WHERE u.id = :id")
void updateLastLoginTime(@Param("id") Long id, @Param("now") LocalDateTime now);
}
安全配置
package com.example.integration.security;
import com.example.integration.security.jwt.JwtAuthenticationEntryPoint;
import com.example.integration.security.jwt.JwtAuthenticationFilter;
import com.example.integration.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true
)
public class SecurityConfig {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/actuator/health", "/actuator/info").permitAll()
.antMatchers("/v2/api-docs", "/configuration/**", "/swagger*/**",
"/webjars/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
.antMatchers("/api/users/**").hasAnyRole("ADMIN")
.anyRequest().authenticated();
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
}
服务层
package com.example.integration.service;
import com.example.integration.dto.UserDTO;
import com.example.integration.entity.User;
import com.example.integration.entity.Role;
import com.example.integration.exception.BadRequestException;
import com.example.integration.exception.ResourceNotFoundException;
import com.example.integration.repository.UserRepository;
import com.example.integration.repository.RoleRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
@Slf4j
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private RedisService redisService;
@Autowired
private RabbitMQProducer rabbitMQProducer;
@Transactional
public UserDTO.UserResponse register(UserDTO.RegisterRequest request) {
// 验证密码一致性
if (!request.getPassword().equals(request.getConfirmPassword())) {
throw new BadRequestException("两次输入的密码不一致");
}
// 检查用户名和邮箱唯一性
if (userRepository.existsByUsername(request.getUsername())) {
throw new BadRequestException("用户名已存在");
}
if (userRepository.existsByEmail(request.getEmail())) {
throw new BadRequestException("邮箱已被注册");
}
// 创建用户
User user = new User();
user.setUsername(request.getUsername());
user.setEmail(request.getEmail());
user.setPassword(passwordEncoder.encode(request.getPassword()));
user.setFullName(request.getFullName());
user.setPhone(request.getPhone());
// 分配默认角色
Role userRole = roleRepository.findByName("ROLE_USER")
.orElseThrow(() -> new RuntimeException("默认角色不存在"));
user.setRoles(Set.of(userRole));
try {
user = userRepository.save(user);
log.info("新用户注册成功: {}", user.getUsername());
// 发送欢迎邮件(异步)
rabbitMQProducer.sendWelcomeEmail(user.getEmail(), user.getUsername());
return UserDTO.UserResponse.fromEntity(user);
} catch (DataIntegrityViolationException e) {
log.error("用户注册失败: {}", e.getMessage());
throw new BadRequestException("用户信息不符合要求");
}
}
@Transactional(readOnly = true)
public UserDTO.UserResponse getUserById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("用户不存在: " + id));
return UserDTO.UserResponse.fromEntity(user);
}
@Transactional(readOnly = true)
public Page<UserDTO.UserResponse> getAllUsers(Pageable pageable) {
return userRepository.findAll(pageable)
.map(UserDTO.UserResponse::fromEntity);
}
@Transactional
public UserDTO.UserResponse updateUser(Long id, UserDTO.RegisterRequest request) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("用户不存在: " + id));
if (request.getFullName() != null) {
user.setFullName(request.getFullName());
}
if (request.getPhone() != null) {
user.setPhone(request.getPhone());
}
if (request.getPassword() != null && !request.getPassword().isEmpty()) {
user.setPassword(passwordEncoder.encode(request.getPassword()));
}
user = userRepository.save(user);
log.info("用户信息更新: {}", user.getUsername());
// 清理缓存
redisService.delete("user:" + id);
return UserDTO.UserResponse.fromEntity(user);
}
@Transactional
public void deleteUser(Long id) {
if (!userRepository.existsById(id)) {
throw new ResourceNotFoundException("用户不存在: " + id);
}
userRepository.deleteById(id);
redisService.delete("user:" + id);
log.info("用户已删除: {}", id);
}
}
异步消息服务
package com.example.integration.service;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class RabbitMQProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
@Async
public void sendWelcomeEmail(String email, String username) {
try {
// 发送邮件消息到队列
rabbitTemplate.convertAndSend(
RabbitMQConfig.EMAIL_EXCHANGE,
RabbitMQConfig.EMAIL_ROUTING_KEY,
new EmailMessage(email, username)
);
log.info("欢迎邮件消息已发送至队列");
} catch (Exception e) {
log.error("发送邮件消息失败: {}", e.getMessage());
}
}
@Data
public static class EmailMessage {
private String email;
private String username;
public EmailMessage(String email, String username) {
this.email = email;
this.username = username;
}
}
}
Redis缓存服务
package com.example.integration.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class RedisService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void set(String key, Object value, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, value, timeout, unit);
}
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
public void delete(String key) {
redisTemplate.delete(key);
}
public boolean hasKey(String key) {
return redisTemplate.hasKey(key);
}
public void increment(String key) {
redisTemplate.opsForValue().increment(key);
}
}
Controller层
package com.example.integration.controller;
import com.example.integration.dto.UserDTO;
import com.example.integration.service.UserService;
import com.example.integration.service.auth.AuthenticationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
@RestController
@RequestMapping("/api")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private AuthenticationService authenticationService;
@PostMapping("/auth/register")
public ResponseEntity<UserDTO.AuthResponse> register(@Valid @RequestBody UserDTO.RegisterRequest request) {
UserDTO.UserResponse user = userService.register(request);
UserDTO.AuthResponse authResponse = authenticationService.generateAuthResponse(user);
return ResponseEntity.ok(authResponse);
}
@PostMapping("/auth/login")
public ResponseEntity<UserDTO.AuthResponse> login(@Valid @RequestBody UserDTO.LoginRequest request) {
return ResponseEntity.ok(authenticationService.login(request));
}
@GetMapping("/users/{id}")
@PreAuthorize("hasRole('ADMIN') or authentication.principal.id == #id")
public ResponseEntity<UserDTO.UserResponse> getUser(@PathVariable Long id) {
return ResponseEntity.ok(userService.getUserById(id));
}
@GetMapping("/users")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Page<UserDTO.UserResponse>> getAllUsers(
@PageableDefault(size = 10, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) {
return ResponseEntity.ok(userService.getAllUsers(pageable));
}
@PutMapping("/users/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<UserDTO.UserResponse> updateUser(
@PathVariable Long id,
@Valid @RequestBody UserDTO.RegisterRequest request) {
return ResponseEntity.ok(userService.updateUser(id, request));
}
@DeleteMapping("/users/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.ok().build();
}
}
异常处理
package com.example.integration.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.persistence.EntityNotFoundException;
import javax.validation.ConstraintViolationException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.BAD_REQUEST.value(),
"参数校验失败",
errors,
LocalDateTime.now()
);
return ResponseEntity.badRequest().body(errorResponse);
}
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) {
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
ex.getMessage(),
null,
LocalDateTime.now()
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
}
@ExceptionHandler(BadRequestException.class)
public ResponseEntity<ErrorResponse> handleBadRequest(BadRequestException ex) {
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.BAD_REQUEST.value(),
ex.getMessage(),
null,
LocalDateTime.now()
);
return ResponseEntity.badRequest().body(errorResponse);
}
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<ErrorResponse> handleAccessDenied(AccessDeniedException ex) {
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.FORBIDDEN.value(),
"没有权限访问此资源",
null,
LocalDateTime.now()
);
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(errorResponse);
}
@ExceptionHandler(AuthenticationException.class)
public ResponseEntity<ErrorResponse> handleAuthentication(AuthenticationException ex) {
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.UNAUTHORIZED.value(),
"认证失败: " + ex.getMessage(),
null,
LocalDateTime.now()
);
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(errorResponse);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
log.error("系统异常", ex);
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"服务器内部错误,请稍后重试",
null,
LocalDateTime.now()
);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
}
@Data
public static class ErrorResponse {
private int status;
private String message;
private Map<String, String> errors;
private LocalDateTime timestamp;
public ErrorResponse(int status, String message, Map<String, String> errors, LocalDateTime timestamp) {
this.status = status;
this.message = message;
this.errors = errors;
this.timestamp = timestamp;
}
}
}
配置文件
# application.yml
spring:
application:
name: integration-demo
datasource:
url: jdbc:postgresql://localhost:5432/integration_db
username: postgres
password: root
driver-class-name: org.postgresql.Driver
hikari:
maximum-pool-size: 10
minimum-idle: 5
connection-timeout: 30000
jpa:
hibernate:
ddl-auto: none
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
show-sql: false
redis:
host: localhost
port: 6379
timeout: 5000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
cache:
type: redis
redis:
time-to-live: 60000ms
cache-null-values: false
server:
port: 8080
servlet:
context-path: /api
tomcat:
max-threads: 200
min-spare-threads: 20
jwt:
secret: ${JWT_SECRET:yoursecretkeyyoursecretkeyyoursecretkeyyoursecretkey}
expiration: 86400000 # 24小时
refresh-expiration: 604800000 # 7天
logging:
level:
com.example.integration: DEBUG
org.springframework.security: WARN
org.hibernate.SQL: WARN
file:
name: logs/integration-app.log
max-size: 10MB
management:
endpoints:
web:
exposure:
include: health,info,metrics,beans,configprops
endpoint:
health:
show-details: always
springdoc:
swagger-ui:
path: /swagger-ui.html
api-docs:
path: /v3/api-docs
测试类
package com.example.integration.controller;
import com.example.integration.dto.UserDTO;
import com.example.integration.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Test
public void testRegister() throws Exception {
UserDTO.RegisterRequest request = new UserDTO.RegisterRequest();
request.setUsername("testuser");
request.setEmail("test@example.com");
request.setPassword("password123");
request.setConfirmPassword("password123");
mockMvc.perform(post("/api/auth/register")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.token").exists())
.andExpect(jsonPath("$.user.email").value("test@example.com"));
}
@Test
public void testLogin() throws Exception {
UserDTO.LoginRequest request = new UserDTO.LoginRequest();
request.setUsername("testuser");
request.setPassword("password123");
mockMvc.perform(post("/api/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.token").exists());
}
@Test
public void testGetAllUsers() throws Exception {
mockMvc.perform(get("/api/users")
.header("Authorization", "Bearer " + getToken()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.content").isArray());
}
private String getToken() throws Exception {
// 登录获取token
UserDTO.LoginRequest request = new UserDTO.LoginRequest();
request.setUsername("admin");
request.setPassword("admin123");
String response = mockMvc.perform(post("/api/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andReturn()
.getResponse()
.getContentAsString();
return objectMapper.readTree(response).get("token").asText();
}
}
Docker Compose 配置
# docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:15
container_name: integration-postgres
environment:
POSTGRES_DB: integration_db
POSTGRES_USER: postgres
POSTGRES_PASSWORD: root
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- integration-network
redis:
image: redis:7-alpine
container_name: integration-redis
ports:
- "6379:6379"
command: redis-server --appendonly yes
volumes:
- redis_data:/data
networks:
- integration-network
rabbitmq:
image: rabbitmq:3-management
container_name: integration-rabbitmq
ports:
- "5672:5672"
- "15672:15672"
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
volumes:
- rabbitmq_data:/var/lib/rabbitmq
networks:
- integration-network
app:
build: .
container_name: integration-app
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/integration_db
SPRING_DATASOURCE_USERNAME: postgres
SPRING_DATASOURCE_PASSWORD: root
SPRING_REDIS_HOST: redis
SPRING_RABBITMQ_HOST: rabbitmq
depends_on:
- postgres
- redis
- rabbitmq
networks:
- integration-network
networks:
integration-network:
driver: bridge
volumes:
postgres_data:
redis_data:
rabbitmq_data:
特性说明
功能特性
- 用户认证授权:JWT + Spring Security
- 数据持久化:Spring Data JPA + PostgreSQL
- 缓存优化:Redis 分布式缓存
- 异步消息:RabbitMQ 消息队列
- 接口文档:Swagger/OpenAPI
- 监控管理:Actuator
- 参数校验:Bean Validation
- 统一异常处理:全局异常处理器
技术栈
- 开发语言:Java 11
- 框架:Spring Boot 2.7
- 持久化:JPA/Hibernate + PostgreSQL
- 缓存:Redis
- 消息队列:RabbitMQ
- API文档:Swagger 3.0
- 安全:Spring Security + JWT
- 测试:JUnit 5 + MockMvc
启动步骤
- 启动 Docker Compose:
docker-compose up -d - 运行应用:
mvn spring-boot:run - 访问 Swagger UI: http://localhost:8080/api/swagger-ui.html
优化建议
- 性能:添加 Nginx 反向代理和负载均衡
- 安全:启用 HTTPS,限制请求频率
- 监控:集成 Prometheus + Grafana
- 部署:使用 Kubernetes 进行编排
- CI/CD:添加自动化部署流水线
这个整合案例提供了完整的现代化Java企业级架构,可以根据具体需求进一步扩展功能。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播