最近在技术社区看到不少同行在讨论Spring Boot 3的新特性,正好手头有个从零开始的后台管理系统项目,决定采用Spring Boot 3+Lan技术栈的组合进行开发。这套技术组合在实际业务场景中表现如何?会遇到哪些典型问题?今天我就把整个实践过程整理成文,分享给有类似需求的开发者。
这个项目是一个典型的企业级后台管理系统,需要处理用户权限、数据报表、工作流等常见业务场景。选择Spring Boot 3作为基础框架,主要是看中了它对Java 17的全面支持、GraalVM原生镜像的增强能力,以及性能优化方面的改进。而Lan技术栈(这里指代前端技术组合)则负责构建响应式的用户界面。
Spring Boot 3于2022年11月正式发布,与2.x版本相比有几个显著改进:
在实际项目中,这些特性带来的直接好处是:
Lan技术栈在这个项目中具体指:
选择这套组合主要基于:
项目采用典型的前后端分离架构:
code复制├── backend/ # Spring Boot 3后端
│ ├── src/main/java # 业务代码
│ └── src/main/resources # 配置文件
└── frontend/ # Lan前端
├── public/ # 静态资源
└── src/ # 前端源码
前后端通过REST API通信,使用JWT进行身份认证。这种架构的优势在于:
推荐使用以下工具组合:
注意:Spring Boot 3强制要求Java 17,使用Java 11或8会导致启动失败
使用Spring Initializr初始化项目:
bash复制# 通过curl命令创建项目骨架
curl https://start.spring.io/starter.zip \
-d type=gradle-project \
-d language=java \
-d bootVersion=3.0.0 \
-d baseDir=backend \
-d groupId=com.example \
-d artifactId=demo \
-d name=demo \
-d description=Demo%20project%20for%20Spring%20Boot \
-d packageName=com.example.demo \
-d packaging=jar \
-d javaVersion=17 \
-d dependencies=web,data-jpa,security,lombok \
-o demo.zip
关键依赖说明:
使用Vite创建Vue 3项目:
bash复制npm create vite@latest frontend --template vue-ts
cd frontend
npm install ant-design-vue axios pinia
关键配置调整:
vite.config.ts中配置代理,解决开发环境跨域问题tsconfig.json,添加路径别名方便引用Spring Security配置类示例:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
}
JWT工具类关键方法:
java复制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() + 1000 * 60 * 60 * 24)) // 24小时
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
}
封装axios请求拦截器:
javascript复制// src/utils/request.ts
const service = axios.create({
baseURL: import.meta.env.VITE_API_URL,
timeout: 5000
})
service.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
}, error => {
return Promise.reject(error)
})
登录页面关键逻辑:
vue复制<script setup lang="ts">
const login = async () => {
try {
const res = await loginApi(formState)
localStorage.setItem('token', res.data.token)
router.push('/dashboard')
} catch (error) {
message.error('登录失败')
}
}
</script>
使用Spring Data JPA定义实体:
java复制@Entity
@Table(name = "sys_user")
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false)
private String password;
@ManyToMany(fetch = FetchType.EAGER)
private Set<Role> roles = new HashSet<>();
}
Spring Data JPA仓库接口:
java复制public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);
}
服务层添加事务注解:
java复制@Service
@RequiredArgsConstructor
@Transactional
public class UserService {
private final UserRepository userRepository;
public User createUser(UserCreateDTO dto) {
User user = new User();
// 属性拷贝...
return userRepository.save(user);
}
}
典型的服务层结构:
java复制public interface UserService {
UserDTO getUserById(Long id);
Page<UserDTO> listUsers(UserQueryDTO query);
Long createUser(UserCreateDTO dto);
void updateUser(UserUpdateDTO dto);
void deleteUser(Long id);
}
使用MapStruct处理对象转换:
java复制@Mapper(componentModel = "spring")
public interface UserConverter {
UserDTO toDTO(User user);
User fromCreateDTO(UserCreateDTO dto);
@Mapping(target = "password", ignore = true)
void updateFromDTO(UserUpdateDTO dto, @MappingTarget User user);
}
REST控制器示例:
java复制@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getById(@PathVariable Long id) {
return ResponseEntity.ok(userService.getUserById(id));
}
@PostMapping
public ResponseEntity<Long> create(@RequestBody UserCreateDTO dto) {
return ResponseEntity.ok(userService.createUser(dto));
}
}
使用SpringDoc OpenAPI生成API文档:
java复制@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info().title("后台管理系统API")
.version("1.0")
.contact(new Contact().name("开发者").email("dev@example.com")))
.externalDocs(new ExternalDocumentation()
.description("项目Wiki")
.url("https://wiki.example.com"));
}
}
访问地址:http://localhost:8080/swagger-ui.html
封装前端API模块:
typescript复制// src/api/user.ts
import request from '@/utils/request'
export function getUserList(params: UserQueryParams) {
return request.get<PageResult<UserVO>>('/api/users', { params })
}
export function createUser(data: UserCreateDTO) {
return request.post<number>('/api/users', data)
}
跨域问题:
日期格式问题:
大文件上传:
spring.servlet.multipart.max-file-sizeGradle构建配置要点:
gradle复制tasks.named('bootJar') {
launchScript()
archiveFileName = "${project.name}.jar"
}
tasks.named('bootBuildImage') {
builder = 'paketobuildpacks/builder:base'
imageName = "${project.name}:${project.version}"
}
构建命令:
bash复制./gradlew bootJar # 生成可执行jar
./gradlew bootBuildImage # 构建Docker镜像
Vite生产构建:
bash复制npm run build
输出目录:dist/,包含静态资源文件
后端Dockerfile示例:
dockerfile复制FROM eclipse-temurin:17-jre
COPY build/libs/*.jar app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
前端Nginx配置:
nginx复制server {
listen 80;
server_name example.com;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080;
}
}
JVM参数调优:
bash复制java -Xms512m -Xmx1024m -XX:+UseG1GC -jar app.jar
数据库连接池配置:
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 20
connection-timeout: 30000
缓存集成:
java复制@Cacheable(value = "users", key = "#id")
public UserDTO getUserById(Long id) {
// ...
}
代码分割:
javascript复制const UserList = () => import('@/views/user/List.vue')
CDN引入:
html复制<script src="https://cdn.jsdelivr.net/npm/vue@3.2.47/dist/vue.global.min.js"></script>
图片懒加载:
vue复制<img v-lazy="imageUrl" alt="">
Spring Boot Actuator配置:
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
Logback配置示例:
xml复制<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
集成Prometheus和Grafana:
添加依赖:
gradle复制implementation 'io.micrometer:micrometer-registry-prometheus'
配置Prometheus采集:
yaml复制scrape_configs:
- job_name: 'spring'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['localhost:8080']
包路径变化:
javax → jakarta配置属性变更:
server.servlet.context-path → server.servlet.context.pathspring.config.import替代部分配置方式测试框架调整:
API设计规范:
{ code, message, data }Mock数据方案:
接口文档维护:
数据库访问优化:
@EntityGraph解决N+1问题saveAll替代循环保存缓存使用原则:
前端渲染优化:
服务拆分原则:
技术选型建议:
后端实现:
java复制@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(Locale.US);
return slr;
}
前端实现:
javascript复制// 使用vue-i18n
const i18n = createI18n({
locale: 'zh',
messages: {
en: { hello: 'Hello' },
zh: { hello: '你好' }
}
})
表单设计器:
工作流引擎:
报表工具:
现象:Spring Boot应用启动时报Unable to find main class
解决方案:
build.gradle中是否配置了正确的mainClass现象:出现HikariPool-1 - Connection is not available错误
排查步骤:
spring.datasource.hikari.leak-detection-threshold=2000现象:刷新页面后出现404
原因:Vue路由使用history模式,但后端未配置
解决方案:
现象:返回403状态码
排查步骤:
Authorization: Bearer <token>代码规范:
./gradlew checkGit分支策略:
CI/CD流程:
文档维护:
这套技术栈在实际项目中表现稳定,特别是在快速迭代的业务场景中,前后端分离的架构让团队能够并行开发。Spring Boot 3的性能改进确实带来了可观的提升,特别是在启动时间和内存占用方面。对于刚开始接触这套技术组合的开发者,建议先从核心模块(如用户认证、数据持久化)入手,逐步扩展到其他业务功能。