跳到主要内容
Java java
IntelliJ IDEA 项目配置与 Web 部署完全指南 综述由AI生成 IntelliJ IDEA 项目配置与 Web 部署指南涵盖了从环境搭建到生产部署的全流程。内容包括 IDE 基础设置、Maven 与 Gradle 项目管理、Web 模块创建、Tomcat 集成、Spring Boot 框架配置、数据库连接池优化、Docker 容器化部署及 CI/CD 流水线构建。此外还涉及多环境管理、应用监控、性能调优及安全最佳实践,旨在帮助开发者构建高效稳定的企业级 Java Web 应用。
极客零度 发布于 2026/2/8 更新于 2026/5/31 9K 浏览第一部分:IntelliJ IDEA 基础与环境配置
1.1 IntelliJ IDEA 概述与版本选择
IntelliJ IDEA 是 JetBrains 公司开发的 Java 集成开发环境,被公认为当前最智能、最高效的 Java IDE 之一。它提供强大的代码分析、智能提示、重构工具和丰富的插件生态系统。
版本选择策略:
Community Edition(社区版) :免费开源,支持 Java、Kotlin、Android 开发
Ultimate Edition(旗舰版) :商业许可,支持 Web 开发、企业框架、数据库工具等
对于 Web 开发,必须选择 Ultimate Edition,因为它包含:
对 JavaScript、TypeScript、HTML、CSS 的深度支持
框架支持(Spring、Spring Boot、Jakarta EE 等)
服务器集成(Tomcat、Jetty、GlassFish 等)
数据库工具和 SQL 支持
1.2 安装与初始配置
安装步骤:
从 JetBrains 官网下载对应操作系统的安装包
运行安装程序,选择安装路径
配置安装选项:
创建桌面快捷方式
关联文件类型(.java、.js 等)
添加到 PATH 环境变量
初始配置优化:
-Xms2048m
-Xmx4096m
-XX: ReservedCodeCacheSize=512 m
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
1.3 界面布局与核心概念
项目结构层级:
text
Project (项目) └── Module (模块)
└── Package (包)
└── Class (类)
关键界面区域:
项目工具窗口(Project Tool Window) :显示项目文件结构
编辑器(Editor) :代码编写和查看区域
导航栏(Navigation Bar) :快速定位文件路径
状态栏(Status Bar) :显示项目状态和 IDE 信息
工具窗口栏(Tool Window Bars) :侧面和底部的工具窗口
重要快捷键:
Ctrl+Shift+A
Ctrl+N:查找类
Ctrl+Shift+N:查找文件
Ctrl+Alt+L:格式化代码
Ctrl+Alt+O:优化导入
Shift+F10:运行当前配置
Shift+F9:调试当前配置
第二部分:项目创建与配置管理
2.1 新项目创建流程
基于 Maven 的项目创建:
文件 → 新建 → 项目
选择左侧的 Maven 选项
配置项目 SDK(Java Development Kit)
设置 GroupId、ArtifactId、Version
选择项目存储位置
<?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 >
<groupId > com.example</groupId >
<artifactId > my-web-app</artifactId >
<version > 1.0-SNAPSHOT</version >
<packaging > war</packaging >
<properties >
<maven.compiler.source > 11</maven.compiler.source >
<maven.compiler.target > 11</maven.compiler.target >
<project.build.sourceEncoding > UTF-8</project.build.sourceEncoding >
</properties >
</project >
基于 Gradle 的项目创建:
文件 → 新建 → 项目
选择 Gradle 选项
配置 DSL(Groovy 或 Kotlin)
设置 GroupId、ArtifactId、Version
// build.gradle 示例
plugins {
id 'java'
id 'war'
}
group = 'com.example'
version = '1.0-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
// 依赖配置
}
2.2 模块化项目结构
多模块项目配置: text
my-project/
├── pom.xml (父 pom)
├── my-web-module /
│ ├── pom.xml
│ └── src/
├── my-service-module /
│ ├── pom.xml
│ └── src/
└── my-dao-module /
├── pom.xml
└── src/
父 pom.xml 配置示例: <project >
<modelVersion > 4.0.0</modelVersion >
<groupId > com.example</groupId >
<artifactId > my-parent-project</artifactId >
<version > 1.0.0</version >
<packaging > pom</packaging >
<modules >
<module > my-web-module</module >
<module > my-service-module</module >
<module > my-dao-module</module >
</modules >
<dependencyManagement >
<dependencies >
<dependency >
<groupId > org.springframework</groupId >
<artifactId > spring-context</artifactId >
<version > 5.3.23</version >
</dependency >
</dependencies >
</dependencyManagement >
<build >
<pluginManagement >
</pluginManagement >
</build >
</project >
2.3 项目配置详解
项目结构配置(Project Structure): 访问方式:文件 → 项目结构 或 Ctrl+Shift+Alt+S
项目 SDK:指定 Java 版本
语言级别:设置语言特性兼容性
编译器输出路径
源代码目录标记:
Sources:源代码目录(蓝色)
Tests:测试代码目录(绿色)
Resources:资源文件目录
Test Resources:测试资源目录
Excluded:排除目录(橙色)
全局库:所有项目可用
项目库:当前项目可用
模块库:特定模块可用
为模块添加特定框架支持
配置框架特定参数
示例:Spring、Web、JPA 等
定义项目输出格式
配置打包方式和内容
设置依赖包含策略
编译器配置: 访问方式:文件 → 设置 → 构建、执行、部署 → 编译器
{
"自动构建项目" : true ,
"编译独立模块" : false ,
"使用外部构建" : false ,
"构建进程堆大小" : 700 ,
"共享构建进程 VM 选项" : "-Xmx512m" ,
"添加异常断言" : true ,
"生成无符号字节码" : false ,
"预编译检测注解" : true
}
运行/调试配置: 访问方式:运行 → 编辑配置 或 Shift+Alt+F10 → 0
Application:Java 应用程序
Maven:Maven 目标
Gradle:Gradle 任务
Spring Boot:Spring Boot 应用
Tomcat Server:本地 Tomcat 服务器
JavaScript Debug:JavaScript 调试
第三部分:Web 项目配置详解
3.1 Web 模块创建与配置
创建 Web 模块:
文件 → 新建 → 模块
选择 Java Enterprise
勾选 Web 应用程序
选择版本(Jakarta EE 9+ 或 Java EE 8)
勾选 创建 web.xml
目录结构: text
web-module /
├── src/
│ └── main/
│ ├── java/
│ ├── resources/
│ └── webapp/
│ ├── WEB -INF /
│ │ ├── web.xml
│ │ └── classes/
│ └── index.jsp
└── pom.xml
web.xml 配置示例: <?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns ="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version ="5.0" >
<display-name > My Web Application</display-name >
<context-param >
<param-name > appName</param-name >
<param-value > MyApplication</param-value >
</context-param >
<servlet >
<servlet-name > dispatcher</servlet-name >
<servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class >
<init-param >
<param-name > contextConfigLocation</param-name >
<param-value > /WEB-INF/spring-config.xml</param-value >
</init-param >
<load-on-startup > 1</load-on-startup >
</servlet >
<servlet-mapping >
<servlet-name > dispatcher</servlet-name >
<url-pattern > /</url-pattern >
</servlet-mapping >
<welcome-file-list >
<welcome-file > index.html</welcome-file >
<welcome-file > index.jsp</welcome-file >
</welcome-file-list >
</web-app >
3.2 Servlet 容器配置
内嵌 Servlet 容器配置(Spring Boot):
@Configuration
public class ServletConfig {
@Bean
public ServletWebServerFactory servletContainer () {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory ();
tomcat.addAdditionalTomcatConnectors(createSslConnector());
tomcat.setPort(8443 );
tomcat.setContextPath("/api" );
return tomcat;
}
private Connector createSslConnector () {
Connector connector = new Connector ("org.apache.coyote.http11.Http11NioProtocol" );
connector.setScheme("https" );
connector.setSecure(true );
connector.setPort(8443 );
return connector;
}
}
3.3 静态资源处理
Spring MVC 资源映射: @Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers (ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**" )
.addResourceLocations("classpath:/static/" , "classpath:/public/" )
.setCachePeriod(3600 )
.resourceChain(true )
.addResolver(new VersionResourceResolver ().addContentVersionStrategy("/**" ));
registry.addResourceHandler("/webjars/**" )
.addResourceLocations("classpath:/META-INF/resources/webjars/" );
}
@Override
public void configureDefaultServletHandling (DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
资源文件组织结构: text
src/main/resources/
├── static/
│ ├── css/
│ ├── js/
│ ├── images/
│ └── fonts/
├── templates/
│ ├── thymeleaf/
│ ├── freemarker/
│ └── jsp/
└── application.properties
第四部分:服务器集成与部署配置
4.1 本地服务器配置
Tomcat 服务器配置步骤:
从 Apache 官网下载 Tomcat
解压到合适目录
配置环境变量(可选)
运行 → 编辑配置
点击 + → Tomcat 服务器 → 本地
配置服务器:
名称:Local Tomcat
应用程序服务器:选择 Tomcat 安装目录
HTTP 端口:8080
JMX 端口:1099
{
"服务器配置" : {
"名称" : "Local Tomcat" ,
"类型" : "Tomcat 9.0.x" ,
"HTTP 端口" : 8080 ,
"JMX 端口" : 1099 ,
"启动前" : "构建工件" ,
"更新操作" : "重新部署" ,
"帧延迟" : 3000
} ,
"部署" : {
"工件" : "my-web-app:war exploded" ,
"上下文路径" : "/myapp" ,
"JRE" : "11" ,
"VM 选项" : "-Xms512m -Xmx1024m -Dspring.profiles.active=dev"
}
}
Tomcat 配置详情: <Server port ="8005" shutdown ="SHUTDOWN" >
<Service name ="Catalina" >
<Connector port ="8080" protocol ="HTTP/1.1" connectionTimeout ="20000" redirectPort ="8443" maxThreads ="200" minSpareThreads ="10" enableLookups ="false" acceptCount ="100" disableUploadTimeout ="true" compression ="on" compressionMinSize ="1024" compressableMimeType ="text/html,text/xml,text/plain,text/css,text/javascript,application/json" />
<Engine name ="Catalina" defaultHost ="localhost" >
<Host name ="localhost" appBase ="webapps" unpackWARs ="true" autoDeploy ="true" >
<Context path ="/myapp" docBase ="C:\projects\myapp" reloadable ="true" crossContext ="true" />
</Host >
</Engine >
</Service >
</Server >
4.2 热部署与热交换配置
配置热部署:
文件 → 设置 → 构建、执行、部署 → 部署
启用 自动上传更改的文件
设置上传间隔:默认 500 毫秒
2. Spring Boot DevTools 配置:
<dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-devtools</artifactId >
<scope > runtime</scope >
<optional > true</optional >
</dependency >
# application-dev.properties
spring.devtools.restart.enabled=true
spring.devtools.livereload.enabled=true
spring.devtools.restart.poll-interval=1000
spring.devtools.restart.quiet-period=400
spring.devtools.restart.exclude=static/**,public/**
安装 JRebel 插件
激活许可证
配置自动重新加载
4.3 多环境部署配置
配置文件结构: text
src/main/resources/
├── application.properties
├── application-dev.properties
├── application-test.properties
├── application-prod.properties
└── config/
├── logback-dev.xml
└── logback-prod.xml
多环境配置示例:
@Configuration
@PropertySource({
"classpath:application.properties",
"classpath:application-${spring.profiles.active}.properties"
})
public class EnvironmentConfig {
@Bean
@Profile("dev")
public DataSource devDataSource () {
}
@Bean
@Profile("prod")
public DataSource prodDataSource () {
}
}
运行配置示例:
{
"类型" : "Spring Boot" ,
"名称" : "MyApp Dev" ,
"主类" : "com.example.MyApplication" ,
"VM 选项" : "-Dspring.profiles.active=dev -Xms512m -Xmx1024m" ,
"程序实参" : "--server.port=8081" ,
"环境变量" : "SPRING_PROFILES_ACTIVE=dev;DB_HOST=localhost"
}
第五部分:框架集成配置
5.1 Spring/Spring Boot 配置
Spring Boot 项目创建:
文件 → 新建 → 项目
选择 Spring Initializr
配置项目元数据
选择依赖:
Spring Web
Spring Data JPA
Spring Security
Thymeleaf
等
application.properties 配置: # 服务器配置
server.port=8080
server.servlet.context-path=/api
server.servlet.session.timeout=30m
server.compression.enabled=true
server.compression.mime-types=text/html,text/xml,text/plain,text/css,text/javascript,application/json
# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=secret
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.maximum-pool-size=20
# JPA 配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
# 日志配置
logging.level.root=INFO
logging.level.com.example=DEBUG
logging.file.name=logs/myapp.log
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} - %msg%n
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
# 静态资源
spring.resources.static-locations=classpath:/static/,classpath:/public/,file:./uploads/
spring.resources.cache.period=3600
spring.mvc.static-path-pattern=/static/**
# 上传文件配置
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
spring.servlet.multipart.enabled=true
Spring 配置类示例: @Configuration
@EnableWebMvc
@ComponentScan("com.example")
@EnableTransactionManagement
@EnableJpaRepositories("com.example.repository")
@EntityScan("com.example.entity")
@EnableCaching
@EnableScheduling
public class AppConfig implements WebMvcConfigurer {
@Override
public void addInterceptors (InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor ())
.addPathPatterns("/**" )
.excludePathPatterns("/**/static/**" , "/error" );
}
@Bean
public LocaleResolver localeResolver () {
SessionLocaleResolver slr = new SessionLocaleResolver ();
slr.setDefaultLocale(Locale.US);
return slr;
}
@Bean
public MessageSource messageSource () {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource ();
messageSource.setBasename("messages" );
messageSource.setDefaultEncoding("UTF-8" );
messageSource.setCacheSeconds(3600 );
return messageSource;
}
@Bean
public CacheManager cacheManager () {
return new ConcurrentMapCacheManager ("products" , "users" );
}
}
5.2 数据库配置与连接
数据源配置: @Configuration
public class DatabaseConfig {
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource () {
return DataSourceBuilder.create().build();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory (
EntityManagerFactoryBuilder builder,
DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("com.example.entity" )
.persistenceUnit("main" )
.properties(jpaProperties())
.build();
}
@Bean
public PlatformTransactionManager transactionManager (
EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager (entityManagerFactory);
}
private Map<String, Object> jpaProperties () {
Map<String, Object> props = new HashMap <>();
props.put("hibernate.hbm2ddl.auto" , "validate" );
props.put("hibernate.dialect" , "org.hibernate.dialect.MySQL8Dialect" );
props.put("hibernate.show_sql" , true );
props.put("hibernate.format_sql" , true );
props.put("hibernate.use_sql_comments" , true );
return props;
}
@Bean
public Flyway flyway (DataSource dataSource) {
Flyway flyway = Flyway.configure()
.dataSource(dataSource)
.locations("classpath:db/migration" )
.baselineOnMigrate(true )
.outOfOrder(true )
.validateOnMigrate(true )
.load();
flyway.migrate();
return flyway;
}
}
在 IDEA 中配置数据库工具:
视图 → 工具窗口 → 数据库
点击 + → 数据源
选择数据库类型(MySQL、PostgreSQL 等)
配置连接参数:
主机:localhost
端口:3306
数据库:mydb
用户名/密码
测试连接
5.3 安全配置(Spring Security)
Security 配置类: @Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter () {
return new JwtAuthenticationFilter ();
}
@Bean
public PasswordEncoder passwordEncoder () {
return new BCryptPasswordEncoder ();
}
@Override
protected void configure (AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean () throws Exception {
return super .authenticationManagerBean();
}
@Override
protected void configure (HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/" , "/favicon.ico" , *.gif", /**/*.svg" , *.html", /**/*.css" ,
第六部分:构建工具配置
6.1 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 >
<groupId > com.example</groupId >
<artifactId > my-enterprise-app</artifactId >
<version > 1.0.0-RELEASE</version >
<packaging > war</packaging >
<name > My Enterprise Application</name >
<description > A comprehensive enterprise web application</description >
<url > http://www.example.com</url >
<parent >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-starter-parent</artifactId >
<version > 2.7.4</version >
<relativePath />
</parent >
<organization >
<name > Example Inc.</name >
<url > http://www.example.com</url >
</organization >
<licenses >
<license >
<name > Apache License, Version 2.0</name >
<url > https://www.apache.org/licenses/LICENSE-2.0.txt</url >
<distribution > repo</distribution >
</license >
</licenses >
<developers >
<developer >
<id > dev1</id >
<name > John Doe</name >
<email > [email protected] </email >
<roles >
<role > architect</role >
<role > developer</role >
</roles >
</developer >
</developers >
<properties >
<java.version > 11</java.version >
<maven.compiler.source > ${java.version}</maven.compiler.source >
<maven.compiler.target > ${java.version}</maven.compiler.target >
<project.build.sourceEncoding > UTF-8</project.build.sourceEncoding >
<project.reporting.outputEncoding > ${project.reporting.outputEncoding}</project.reporting.outputEncoding >
<spring.version > 5.3.23</spring.version >
<hibernate.version > 5.6.11.Final</hibernate.version >
<jackson.version > 2.13.4</jackson.version >
<logback.version > 1.2.11</logback.version >
<junit.version > 5.9.1</junit.version >
<maven-compiler-plugin.version > 3.10.1</maven-compiler-plugin.version >
<maven-war-plugin.version > 3.3.2</maven-war-plugin.version >
<maven-surefire-plugin.version > 3.0.0-M7</maven-surefire-plugin.version >
</properties >
<dependencyManagement >
<dependencies >
<dependency >
<groupId > org.springframework</groupId >
<artifactId > spring-framework-bom</artifactId >
<version > ${spring.version}</version >
<type > pom</type >
<scope > import</scope >
</dependency >
<dependency >
<groupId > com.fasterxml.jackson</groupId >
<artifactId > jackson-bom</artifactId >
<version > ${jackson.version}</version >
<type > pom</type >
<scope > import</scope >
</dependency >
</dependencies >
</dependencyManagement >
<dependencies >
<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 >
<dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-starter-cache</artifactId >
</dependency >
<dependency >
<groupId > com.mysql</groupId >
<artifactId > mysql-connector-j</artifactId >
<scope > runtime</scope >
</dependency >
<dependency >
<groupId > com.h2database</groupId >
<artifactId > h2</artifactId >
<scope > test</scope >
</dependency >
<dependency >
<groupId > org.projectlombok</groupId >
<artifactId > lombok</artifactId >
<scope > provided</scope >
</dependency >
<dependency >
<groupId > org.apache.commons</groupId >
<artifactId > commons-lang3</artifactId >
<version > 3.12.0</version >
</dependency >
<dependency >
<groupId > com.google.guava</groupId >
<artifactId > guava</artifactId >
<version > 31.1-jre</version >
</dependency >
<dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-starter-test</artifactId >
<scope > test</scope >
<exclusions >
<exclusion >
<groupId > org.junit.vintage</groupId >
<artifactId > junit-vintage-engine</artifactId >
</exclusion >
</exclusions >
</dependency >
<dependency >
<groupId > org.springframework.security</groupId >
<artifactId > spring-security-test</artifactId >
<scope > test</scope >
</dependency >
<dependency >
<groupId > com.tngtech.archunit</groupId >
<artifactId > archunit-junit5</artifactId >
<version > 1.0.0</version >
<scope > test</scope >
</dependency >
<dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-devtools</artifactId >
<scope > runtime</scope >
<optional > true</optional >
</dependency >
</dependencies >
<build >
<finalName > ${project.artifactId}-${project.version}</finalName >
<resources >
<resource >
<directory > src/main/resources</directory >
<filtering > true</filtering >
<includes >
<include > **/*.properties</include >
<include > **/*.xml</include >
<include > **/*.yml</include >
</includes >
</resource >
<resource >
<directory > src/main/resources</directory >
<filtering > false</filtering >
<excludes >
<exclude > **/*.properties</exclude >
<exclude > **/*.xml</exclude >
<exclude > **/*.yml</exclude >
</excludes >
</resource >
</resources >
<plugins >
<plugin >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-maven-plugin</artifactId >
<configuration >
<executable > true</executable >
<mainClass > com.example.Application</mainClass >
<layout > WAR</layout >
<classifier > exec</classifier >
<excludes >
<exclude >
<groupId > org.projectlombok</groupId >
<artifactId > lombok</artifactId >
</exclude >
</excludes >
</configuration >
<executions >
<execution >
<goals >
<goal > repackage</goal >
</goals >
</execution >
</executions >
</plugin >
<plugin >
<groupId > org.apache.maven.plugins</groupId >
<artifactId > maven-compiler-plugin</artifactId >
<version > ${maven-compiler-plugin.version}</version >
<configuration >
<source > ${java.version}</source >
<target > ${java.version}</target >
<encoding > ${project.build.sourceEncoding}</encoding >
<annotationProcessorPaths >
<path >
<groupId > org.projectlombok</groupId >
<artifactId > lombok</artifactId >
<version > ${lombok.version}</version >
</path >
</annotationProcessorPaths >
<compilerArgs >
<arg > -parameters</arg >
</compilerArgs >
</configuration >
</plugin >
<plugin >
<groupId > org.apache.maven.plugins</groupId >
<artifactId > maven-war-plugin</artifactId >
<version > ${maven-war-plugin.version}</version >
<configuration >
<failOnMissingWebXml > false</failOnMissingWebXml >
<warSourceDirectory > src/main/webapp</warSourceDirectory >
<warName > ${project.artifactId}</warName >
<outputDirectory > ${project.build.directory}</outputDirectory >
<webResources >
<resource >
<directory > src/main/webapp</directory >
</resource >
</webResources >
</configuration >
</plugin >
<plugin >
<groupId > org.apache.maven.plugins</groupId >
<artifactId > maven-surefire-plugin</artifactId >
<version > ${maven-surefire-plugin.version}</version >
<configuration >
<skipTests > false</skipTests >
<includes >
<include > **/*Test.java</include >
<include > **/*Tests.java</include >
</includes >
<excludes >
<exclude > **/*IntegrationTest.java</exclude >
<exclude > **/*IT.java</exclude >
</excludes >
<systemPropertyVariables >
<java.util.logging.manager > org.jboss.logmanager.LogManager</java.util.logging.manager >
</systemPropertyVariables >
</configuration >
</plugin >
<plugin >
<groupId > org.jacoco</groupId >
<artifactId > jacoco-maven-plugin</artifactId >
<version > 0.8.8</version >
<executions >
<execution >
<goals >
<goal > prepare-agent</goal >
</goals >
</execution >
<execution >
<id > report</id >
<phase > test</phase >
<goals >
<goal > report</goal >
</goals >
</execution >
<execution >
<id > check</id >
<goals >
<goal > check</goal >
</goals >
<configuration >
<rules >
<rule >
<element > BUNDLE</element >
<limits >
<limit >
<counter > LINE</counter >
<value > COVEREDRATIO</value >
<minimum > 0.80</minimum >
</limit >
</limits >
</rule >
</rules >
</configuration >
</execution >
</executions >
</plugin >
<plugin >
<groupId > org.apache.maven.plugins</groupId >
<artifactId > maven-dependency-plugin</artifactId >
<version > 3.3.0</version >
<executions >
<execution >
<id > analyze</id >
<goals >
<goal > analyze</goal >
</goals >
<configuration >
<failOnWarning > true</failOnWarning >
</configuration >
</execution >
<execution >
<id > copy-dependencies</id >
<phase > package</phase >
<goals >
<goal > copy-dependencies</goal >
</goals >
<configuration >
<outputDirectory > ${project.build.directory}/lib</outputDirectory >
<overWriteReleases > false</overWriteReleases >
<overWriteSnapshots > false</overWriteSnapshots >
<overWriteIfNewer > true</overWriteIfNewer >
</configuration >
</execution >
</executions >
</plugin >
<plugin >
<groupId > org.apache.maven.plugins</groupId >
<artifactId > maven-clean-plugin</artifactId >
<version > 3.2.0</version >
<configuration >
<filesets >
<fileset >
<directory > ${project.build.directory}</directory >
<includes >
<include > **/*</include >
</includes >
<followSymlinks > false</followSymlinks >
</fileset >
<fileset >
<directory > ${basedir}/logs</directory >
</fileset >
<fileset >
<directory > ${basedir}/temp</directory >
</fileset >
</filesets >
</configuration >
</plugin >
<plugin >
<groupId > org.apache.maven.plugins</groupId >
<artifactId > maven-resources-plugin</artifactId >
<version > 3.3.0</version >
<configuration >
<encoding > ${project.build.sourceEncoding}</encoding >
<nonFilteredFileExtensions >
<nonFilteredFileExtension > pdf</nonFilteredFileExtension >
<nonFilteredFileExtension > jar</nonFilteredFileExtension >
<nonFilteredFileExtension > zip</nonFilteredFileExtension >
</nonFilteredFileExtensions >
</configuration >
</plugin >
<plugin >
<groupId > org.apache.maven.plugins</groupId >
<artifactId > maven-enforcer-plugin</artifactId >
<version > 3.1.0</version >
<executions >
<execution >
<id > enforce-versions</id >
<goals >
<goal > enforce</goal >
</goals >
<configuration >
<rules >
<requireJavaVersion >
<version > [11,17)</version >
</requireJavaVersion >
<requireMavenVersion >
<version > [3.6,)</version >
</requireMavenVersion >
<banDuplicatePomDependencyVersions />
<dependencyConvergence />
<requireReleaseDeps >
<message > No Snapshots Allowed!</message >
</requireReleaseDeps >
</rules >
</configuration >
</execution >
</executions >
</plugin >
<plugin >
<groupId > org.codehaus.mojo</groupId >
<artifactId > versions-maven-plugin</artifactId >
<version > 2.13.0</version >
<configuration >
<generateBackupPoms > false</generateBackupPoms >
</configuration >
</plugin >
</plugins >
</build >
<profiles >
<profile >
<id > development</id >
<activation >
<activeByDefault > true</activeByDefault >
</activation >
<properties >
<spring.profiles.active > dev</spring.profiles.active >
<build.profile.id > dev</build.profile.id >
</properties >
<build >
<resources >
<resource >
<directory > src/main/resources</directory >
<filtering > true</filtering >
</resource >
</resources >
</build >
</profile >
<profile >
<id > testing</id >
<properties >
<spring.profiles.active > test</spring.profiles.active >
<build.profile.id > test</build.profile.id >
</properties >
<build >
<plugins >
<plugin >
<groupId > org.apache.maven.plugins</groupId >
<artifactId > maven-surefire-plugin</artifactId >
<configuration >
<skipTests > false</skipTests >
<includes >
<include > **/*Test.java</include >
<include > **/*Tests.java</include >
<include > **/*IT.java</include >
</includes >
</configuration >
</plugin >
</plugins >
</build >
</profile >
<profile >
<id > production</id >
<properties >
<spring.profiles.active > prod</spring.profiles.active >
<build.profile.id > prod</build.profile.id >
</properties >
<build >
<plugins >
<plugin >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-maven-plugin</artifactId >
<configuration >
<executable > true</executable >
<jvmArguments >
-Xms512m -Xmx2048m -XX:MaxMetaspaceSize=512m -XX:+UseG1GC -XX:+UseStringDeduplication
</jvmArguments >
</configuration >
</plugin >
</plugins >
</build >
</profile >
<profile >
<id > docker</id >
<build >
<plugins >
<plugin >
<groupId > com.spotify</groupId >
<artifactId > dockerfile-maven-plugin</artifactId >
<version > 1.4.13</version >
<configuration >
<repository > ${project.artifactId}</repository >
<tag > ${project.version}</tag >
<buildArgs >
<JAR_FILE > target/${project.build.finalName}.jar</JAR_FILE >
</buildArgs >
</configuration >
<executions >
<execution >
<id > default</id >
<phase > install</phase >
<goals >
<goal > build</goal >
</goals >
</execution >
</executions >
</plugin >
</plugins >
</build >
</profile >
</profiles >
<reporting >
<plugins >
<plugin >
<groupId > org.apache.maven.plugins</groupId >
<artifactId > maven-project-info-reports-plugin</artifactId >
<version > 3.4.1</version >
</plugin >
<plugin >
<groupId > org.apache.maven.plugins</groupId >
<artifactId > maven-javadoc-plugin</artifactId >
<version > 3.4.1</version >
<configuration >
<show > private</show >
<nohelp > true</nohelp >
<additionalOptions >
<additionalOption > -Xdoclint:none</additionalOption >
</additionalOptions >
</configuration >
</plugin >
<plugin >
<groupId > org.jacoco</groupId >
<artifactId > jacoco-maven-plugin</artifactId >
<version > 0.8.8</version >
<reportSets >
<reportSet >
<reports >
<report > report</report >
</reports >
</reportSet >
</reportSets >
</plugin >
</plugins >
</reporting >
<repositories >
<repository >
<id > central</id >
<name > Central Repository</name >
<url > https://repo.maven.apache.org/maven2</url >
<layout > default</layout >
<snapshots >
<enabled > false</enabled >
</snapshots >
</repository >
<repository >
<id > spring-milestones</id >
<name > Spring Milestones</name >
<url > https://repo.spring.io/milestone</url >
<snapshots >
<enabled > false</enabled >
</snapshots >
</repository >
<repository >
<id > spring-snapshots</id >
<name > Spring Snapshots</name >
<url > https://repo.spring.io/snapshot</url >
<snapshots >
<enabled > true</enabled >
</snapshots >
</repository >
</repositories >
<pluginRepositories >
<pluginRepository >
<id > central</id >
<name > Central Plugin Repository</name >
<url > https://repo.maven.apache.org/maven2</url >
<layout > default</layout >
<snapshots >
<enabled > false</enabled >
</snapshots >
</pluginRepository >
</pluginRepositories >
</project >
6.2 Gradle 配置
build.gradle 示例: plugins {
id 'java'
id 'war'
id 'org.springframework.boot' version '2.7.4'
id 'io.spring.dependency-management' version '1.0.14.RELEASE'
id 'jacoco'
id 'com.palantir.docker' version '0.34.0'
}
group = 'com.example'
version = '1.0.0-RELEASE'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
developmentOnly runtimeClasspath {
extendsFrom developmentOnly
}
}
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
}
ext {
set('springCloudVersion', "2021.0.4")
set('testcontainersVersion', "1.17.4")
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}"
}
}
dependencies {
// Spring Boot Starters
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'org.springframework.boot:spring-boot-starter-websocket'
// Spring Cloud
implementation 'org.springframework.cloud:spring-cloud-starter-config'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j'
// Database
runtimeOnly 'com.mysql:mysql-connector-j'
runtimeOnly 'com.h2database:h2'
// Security
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
// Utilities
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation 'org.apache.commons:commons-lang3:3.12.0'
implementation 'com.google.guava:guava:31.1-jre'
implementation 'org.modelmapper:modelmapper:3.1.0'
// Cache
implementation 'com.github.ben-manes.caffeine:caffeine:3.1.2'
// Monitoring
implementation 'io.micrometer:micrometer-registry-prometheus'
// Documentation
implementation 'org.springdoc:springdoc-openapi-ui:1.6.12'
// Development
developmentOnly 'org.springframework.boot:spring-boot-devtools'
// Testing
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.testcontainers:mysql'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'com.tngtech.archunit:archunit-junit5:1.0.0'
testImplementation 'org.awaitility:awaitility:4.2.0'
}
tasks.named('test') {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
exceptionFormat "full"
}
systemProperty 'spring.profiles.active', 'test'
finalizedBy jacocoTestReport
}
jacoco {
toolVersion = "0.8.8"
}
jacocoTestReport {
dependsOn test
reports {
xml.required = true
html.required = true
csv.required = false
}
afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.collect {
fileTree(dir: it, exclude: [
'**/config/**',
'**/entity/**',
'**/dto/**',
'**/*Application*',
'**/exception/**'
])
}))
}
}
jacocoTestCoverageVerification {
violationRules {
rule {
limit {
minimum = 0.80
}
}
rule {
element = 'CLASS'
includes = ['com.example.service.*']
limit {
counter = 'BRANCH'
value = 'COVEREDRATIO'
minimum = 0.80
}
}
}
}
check.dependsOn jacocoTestCoverageVerification
bootJar {
archiveClassifier = 'boot'
launchScript()
}
war {
archiveClassifier = 'war'
enabled = true
}
springBoot {
buildInfo()
}
docker {
name "${project.name}:${project.version}"
files bootJar.archiveFile
buildArgs(['JAR_FILE': bootJar.archiveFileName.get()])
}
task copyDependencies(type: Copy) {
from configurations.runtimeClasspath into "${buildDir}/libs/dependencies"
}
build.dependsOn copyDependencies
// 自定义任务
task generateVersionProperties {
doLast {
def propertiesFile = file("$buildDir/resources/main/version.properties")
propertiesFile.parentFile.mkdirs()
def properties = new Properties()
properties.setProperty('version', project.version.toString())
properties.setProperty('build.time', new Date().format("yyyy-MM-dd HH:mm:ss"))
properties.setProperty('java.version', System.getProperty('java.version'))
properties.store(propertiesFile.newWriter(), null)
}
}
processResources.dependsOn generateVersionProperties
// 多环境配置
bootRun {
systemProperties = System.properties
}
// 性能优化
tasks.withType(JavaCompile) {
options.compilerArgs += ['-parameters', '-Xlint:unchecked', '-Xlint:deprecation']
options.encoding = 'UTF-8'
options.incremental = true
}
// 资源过滤
processResources {
filesMatching('**/application*.yml') {
filter org.apache.tools.ant.filters.ReplaceTokens, tokens: [
'app.version': project.version
]
}
}
第七部分:部署与持续集成
7.1 Docker 容器化部署
Dockerfile 配置: # 多阶段构建 Dockerfile
# 第一阶段:构建阶段
FROM maven:3.8.6-eclipse-temurin-11 AS build
WORKDIR /app
# 复制 pom.xml 和下载依赖
COPY pom.xml .
RUN mvn dependency:go-offline -B
# 复制源代码并构建
COPY src ./src
RUN mvn clean package -DskipTests
# 第二阶段:运行阶段
FROM eclipse-temurin:11-jre-alpine
# 添加非 root 用户
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
WORKDIR /app
# 复制构建产物
COPY --from=build /app/target/*.war app.war
# 配置 JVM 参数
ENV JAVA_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+ParallelRefProcEnabled -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp -XX:+ExitOnOutOfMemoryError"
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1
# 暴露端口
EXPOSE 8080
# 启动应用
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app/app.war"]
Docker Compose 配置: version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: mysql_db
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: ${DB_NAME}
MYSQL_USER: ${DB_USER}
MYSQL_PASSWORD: ${DB_PASSWORD}
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./config/mysql/init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- app-network
healthcheck:
test: ["CMD" , "mysqladmin" , "ping" , "-h" , "localhost" ]
timeout: 20s
retries: 10
redis:
image: redis:7-alpine
container_name: redis_cache
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
networks:
- app-network
healthcheck:
test: ["CMD" , "redis-cli" , "ping" ]
interval: 10s
timeout: 5s
retries: 5
app:
build:
context: .
dockerfile: Dockerfile
container_name: web_app
environment:
SPRING_PROFILES_ACTIVE: docker
DB_HOST: mysql
DB_PORT: 3306
REDIS_HOST: redis
REDIS_PORT: 6379
ports:
- "8080:8080"
- "5005:5005"
volumes:
- ./logs:/app/logs
- ./uploads:/app/uploads
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app-network
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1'
memory: 1024M
reservations:
cpus: '0.5'
memory: 512M
nginx:
image: nginx:1.23-alpine
container_name: web_nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./ssl:/etc/nginx/ssl:ro
- ./static:/usr/share/nginx/static:ro
depends_on:
- app
networks:
- app-network
prometheus:
image: prom/prometheus:latest
container_name: prometheus_monitor
ports:
- "9090:9090"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--storage.tsdb.retention.time=200h'
- '--web.enable-lifecycle'
networks:
- app-network
grafana:
image: grafana/grafana:latest
container_name: grafana_dashboard
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
depends_on:
- prometheus
networks:
- app-network
networks:
app-network:
driver: bridge
ipam:
config:
- subnet: 172.20 .0 .0 /16
volumes:
mysql_data:
redis_data:
prometheus_data:
grafana_data:
7.2 持续集成/持续部署(CI/CD)
GitHub Actions 配置: name: Java CI/CD Pipeline
on:
push:
branches: [ main , develop ]
pull_request:
branches: [ main ]
release:
types: [ published ]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: testdb
ports:
- 3306 :3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
redis:
image: redis:7-alpine
ports:
- 6379 :6379
options: >-
--health-cmd="redis-cli ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Build and Test
run: mvn -B clean test
env:
SPRING_PROFILES_ACTIVE: test
DB_HOST: localhost
DB_PORT: 3306
REDIS_HOST: localhost
REDIS_PORT: 6379
- name: Upload Test Results
uses: actions/upload-artifact@v3
if: always()
with:
name: test-results
path: target/surefire-reports/
- name: Upload Coverage Report
uses: codecov/codecov-action@v3
with:
file: ./target/site/jacoco/jacoco.xml
fail_ci_if_error: true
build:
runs-on: ubuntu-latest
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn -B clean package -DskipTests
- name: Log in to Container Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix={{branch}}-
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Generate Deployment Package
run: |
mkdir -p deployment
cp target/*.war deployment/
cp Dockerfile deployment/
cp docker-compose.yml deployment/
cp -r config deployment/
tar -czf deployment-package.tar.gz deployment/
- name: Upload Deployment Package
uses: actions/upload-artifact@v3
with:
name: deployment-package
path: deployment-package.tar.gz
deploy:
runs-on: ubuntu-latest
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
environment:
name: production
url: https://your-domain.com
steps:
- name: Download Deployment Package
uses: actions/download-artifact@v3
with:
name: deployment-package
- name: Setup SSH
uses: webfactory/[email protected]
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Deploy to Server
env:
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
run: |
tar -xzf deployment-package.tar.gz
scp -o StrictHostKeyChecking=no -r deployment/* ${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/
ssh -o StrictHostKeyChecking=no ${DEPLOY_USER}@${DEPLOY_HOST} "
cd ${DEPLOY_PATH} && docker-compose down && docker-compose pull && docker-compose up -d --build && docker system prune -af
"
- name: Verify Deployment
run: |
sleep 30
curl --retry 5 --retry-delay 10 --max-time 30 \n https://your-domain.com/actuator/health
- name: Notify Success
uses: 8398a7/action-slack@v3
with:
channel: '#deployments'
status: ${{ job.status }}
author_name: GitHub Actions
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
if: always()
7.3 云平台部署
AWS 部署配置(使用 AWS CDK): import * as cdk from '@aws-cdk/core' ;
import * as ec2 from '@aws-cdk/aws-ec2' ;
import * as ecs from '@aws-cdk/aws-ecs' ;
import * as ecs_patterns from '@aws-cdk/aws-ecs-patterns' ;
import * as rds from '@aws-cdk/aws-rds' ;
import * as elasticache from '@aws-cdk/aws-elasticache' ;
import * as secretsmanager from '@aws-cdk/aws-secretsmanager' ;
export class WebAppStack extends cdk.Stack {
constructor (scope : cdk.Construct , id : string , props ?: cdk.StackProps ) {
super (scope, id, props);
const vpc = new ec2.Vpc (this , 'WebAppVPC' , {
maxAzs : 2 ,
natGateways : 1 ,
subnetConfiguration : [
{ cidrMask : 24 , name : 'Public' , subnetType : ec2.SubnetType .PUBLIC },
{ cidrMask : 24 , name : 'Private' , subnetType : ec2.SubnetType .PRIVATE_WITH_NAT },
{ cidrMask : 28 , name : 'Isolated' , subnetType : ec2.SubnetType .PRIVATE_ISOLATED },
]
});
const databaseSecret = new secretsmanager.Secret (this , 'DatabaseSecret' , {
secretName : 'webapp-db-secret' ,
generateSecretString : {
secretStringTemplate : JSON .stringify ({ username : 'admin' }),
generateStringKey : 'password' ,
excludePunctuation : true ,
passwordLength : 16 ,
},
});
const database = new rds.DatabaseInstance (this , 'Database' , {
engine : rds.DatabaseInstanceEngine .mysql ({ version : rds.MysqlEngineVersion .VER_8_0_28 }),
instanceType : ec2.InstanceType .of (ec2.InstanceClass .T3 , ec2.InstanceSize .MICRO ),
vpc,
vpcSubnets : { subnetType : ec2.SubnetType .PRIVATE_ISOLATED },
credentials : rds.Credentials .fromSecret (databaseSecret),
storageEncrypted : true ,
backupRetention : cdk.Duration .days (7 ),
deletionProtection : false ,
databaseName : 'webappdb' ,
});
const redisSubnetGroup = new elasticache.CfnSubnetGroup (this , 'RedisSubnetGroup' , {
description : 'Subnet group for Redis' ,
subnetIds : vpc.selectSubnets ({ subnetType : ec2.SubnetType .PRIVATE_ISOLATED }).subnetIds ,
});
const redis = new elasticache.CfnCacheCluster (this , 'RedisCache' , {
cacheNodeType : 'cache.t3.micro' ,
engine : 'redis' ,
numCacheNodes : 1 ,
clusterName : 'webapp-redis' ,
vpcSecurityGroupIds : [],
cacheSubnetGroupName : redisSubnetGroup.ref ,
});
const cluster = new ecs.Cluster (this , 'WebAppCluster' , {
vpc,
containerInsights : true ,
});
const fargateService = new ecs_patterns.ApplicationLoadBalancedFargateService (this , 'WebAppService' , {
cluster,
memoryLimitMiB : 1024 ,
cpu : 512 ,
desiredCount : 2 ,
taskImageOptions : {
image : ecs.ContainerImage .fromRegistry ('ghcr.io/your-org/your-app:latest' ),
containerPort : 8080 ,
environment : {
SPRING_PROFILES_ACTIVE : 'prod' ,
DB_HOST : database.dbInstanceEndpointAddress ,
DB_PORT : database.dbInstanceEndpointPort ,
REDIS_HOST : redis.attrRedisEndpointAddress ,
REDIS_PORT : redis.attrRedisEndpointPort ,
},
secrets : {
DB_USERNAME : ecs.Secret .fromSecretsManager (databaseSecret, 'username' ),
DB_PASSWORD : ecs.Secret .fromSecretsManager (databaseSecret, 'password' ),
},
},
publicLoadBalancer : true ,
});
const scaling = fargateService.service .autoScaleTaskCount ({
minCapacity : 2 ,
maxCapacity : 10 ,
});
scaling.scaleOnCpuUtilization ('CpuScaling' , {
targetUtilizationPercent : 70 ,
scaleInCooldown : cdk.Duration .seconds (60 ),
scaleOutCooldown : cdk.Duration .seconds (60 ),
});
scaling.scaleOnMemoryUtilization ('MemoryScaling' , {
targetUtilizationPercent : 70 ,
scaleInCooldown : cdk.Duration .seconds (60 ),
scaleOutCooldown : cdk.Duration .seconds (60 ),
});
database.connections .allowFrom (fargateService.service , ec2.Port .tcp (3306 ));
new cdk.CfnOutput (this , 'LoadBalancerDNS' , {
value : fargateService.loadBalancer .loadBalancerDnsName ,
});
}
}
第八部分:监控与优化
8.1 应用监控配置
Spring Boot Actuator 配置: # Actuator 配置
management.endpoints.web.exposure.include=health,info,metrics,env,beans,loggers,prometheus
management.endpoints.web.exposure.exclude=threaddump,heapdump
management.endpoint.health.show-details=always
management.endpoint.health.probes.enabled=true
management.health.db.enabled=true
management.health.redis.enabled=true
management.health.diskspace.enabled=true
management.metrics.export.prometheus.enabled=true
management.metrics.distribution.percentiles-histogram.http.server.requests=true
management.metrics.enable.jvm=true
management.metrics.enable.logback=true
management.metrics.enable.process=true
management.metrics.enable.system=true
# 自定义健康指示器
management.health.custom.enabled=true
# 日志级别管理
management.endpoint.loggers.enabled=true
自定义健康检查: @Component
public class CustomHealthIndicator implements HealthIndicator {
private final DatabaseService databaseService;
private final CacheService cacheService;
private final ExternalService externalService;
public CustomHealthIndicator (DatabaseService databaseService, CacheService cacheService, ExternalService externalService) {
this .databaseService = databaseService;
this .cacheService = cacheService;
this .externalService = externalService;
}
@Override
public Health health () {
Health.Builder builder = Health.up();
try {
databaseService.checkConnection();
builder.withDetail("database" , "connected" );
} catch (Exception e) {
builder.down()
.withDetail("database" , "disconnected" )
.withDetail("databaseError" , e.getMessage());
}
try {
cacheService.ping();
builder.withDetail("cache" , "connected" );
} catch (Exception e) {
builder.down()
.withDetail("cache" , "disconnected" )
.withDetail("cacheError" , e.getMessage());
}
try {
externalService.checkStatus();
builder.withDetail("externalService" , "available" );
} catch (Exception e) {
builder.withDetail("externalService" , "unavailable" )
.withDetail("externalServiceError" , e.getMessage());
}
File file = new File ("." );
long freeSpace = file.getFreeSpace();
long totalSpace = file.getTotalSpace();
double freePercentage = (double ) freeSpace / totalSpace * 100 ;
builder.withDetail("disk.free" , formatBytes(freeSpace))
.withDetail("disk.total" , formatBytes(totalSpace))
.withDetail("disk.freePercentage" , String.format("%.2f%%" , freePercentage));
if (freePercentage < 10 ) {
builder.status(Status.DOWN).withDetail("disk" , "critical" );
} else if (freePercentage < 20 ) {
builder.status(Status.DOWN).withDetail("disk" , "warning" );
}
return builder.build();
}
private String formatBytes (long bytes) {
if (bytes < 1024 ) return bytes + " B" ;
int exp = (int ) (Math.log(bytes) / Math.log(1024 ));
String pre = "KMGTPE" .charAt(exp - 1 ) + "i" ;
return String.format("%.1f %sB" , bytes / Math.pow(1024 , exp), pre);
}
}
指标监控配置: @Configuration
@EnableMetrics
public class MetricsConfig {
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags () {
return registry -> registry.config()
.commonTags("application" , "webapp" , "environment" , "production" );
}
@Bean
public TimedAspect timedAspect (MeterRegistry registry) {
return new TimedAspect (registry);
}
@Bean
public CountedAspect countedAspect (MeterRegistry registry) {
return new CountedAspect (registry);
}
@Bean
public MeterBinder cacheMetrics () {
return new MeterBinder () {
@Override
public void bindTo (MeterRegistry registry) {
Gauge.builder("cache.size" , CacheManager.getCache("users" )::getSize)
.description("The number of entries in the cache" )
.tags("cache" , "users" )
.register(registry);
}
};
}
}
8.2 性能优化配置
JVM 优化参数: # 生产环境 JVM 参数
-Xms512m # 初始堆大小
-Xmx2048m # 最大堆大小
-XX:MaxMetaspaceSize=512m # 最大元空间大小
-XX:+UseG1GC # 使用 G1 垃圾回收器
-XX:MaxGCPauseMillis=200 # 目标最大 GC 暂停时间
-XX:ParallelGCThreads=4 # 并行 GC 线程数
-XX:ConcGCThreads=2 # 并发 GC 线程数
-XX:+UseStringDeduplication # 字符串去重
-XX:+HeapDumpOnOutOfMemoryError # OOM 时生成堆转储
-XX:HeapDumpPath=/tmp/heapdump.hprof # 堆转储路径
-XX:+PrintGCDetails # 打印 GC 详情
-XX:+PrintGCDateStamps
-Xloggc:/tmp/gc.log # GC 日志文件
-XX:+UseCompressedOops # 使用压缩普通对象指针
-XX:+UseCompressedClassPointers # 使用压缩类指针
-XX:+AlwaysPreTouch # 启动时预接触内存页
-Djava.security.egd=file:/dev/./urandom # 随机数生成优化
-Dsun.net.inetaddr.ttl=60 # DNS 缓存 TTL
Web 服务器优化: @Configuration
public class WebServerConfig {
@Bean
public TomcatServletWebServerFactory servletContainer () {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory ();
factory.addConnectorCustomizers(connector -> {
connector.setProperty("maxThreads" , "200" );
connector.setProperty("minSpareThreads" , "20" );
connector.setProperty("maxConnections" , "10000" );
connector.setProperty("acceptCount" , "100" );
connector.setProperty("connectionTimeout" , "20000" );
connector.setProperty("maxKeepAliveRequests" , "100" );
connector.setProperty("keepAliveTimeout" , "30000" );
connector.setProperty("compression" , "on" );
connector.setProperty("compressionMinSize" , "2048" );
connector.setProperty("compressableMimeType" , "text/html,text/xml,text/plain,text/css,text/javascript,application/json" );
connector.setProperty("useBodyEncodingForURI" , "true" );
connector.setProperty("URIEncoding" , "UTF-8" );
});
return factory;
}
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter () {
FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean <>();
registrationBean.setFilter(new CorsFilter ());
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registrationBean;
}
@Bean
public FilterRegistrationBean<GzipFilter> gzipFilter () {
FilterRegistrationBean<GzipFilter> registrationBean = new FilterRegistrationBean <>();
registrationBean.setFilter(new GzipFilter ());
registrationBean.addUrlPatterns("/*" );
registrationBean.setOrder(Ordered.LOWEST_PRECEDENCE);
return registrationBean;
}
}
数据库连接池优化: # HikariCP 连接池配置
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.idle-timeout=600000
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-test-query=SELECT 1
spring.datasource.hikari.pool-name=WebAppPool
spring.datasource.hikari.auto-commit=false
spring.datasource.hikari.leak-detection-threshold=60000
spring.datasource.hikari.initialization-fail-timeout=1
8.3 日志配置优化
Logback 配置: <?xml version="1.0" encoding="UTF-8" ?>
<configuration scan ="true" scanPeriod ="30 seconds" >
<property name ="LOG_PATH" value ="./logs" />
<property name ="LOG_ARCHIVE" value ="${LOG_PATH}/archive" />
<property name ="APP_NAME" value ="webapp" />
<appender name ="CONSOLE" >
<encoder >
<pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern >
<charset > UTF-8</charset >
</encoder >
<filter >
<level > INFO</level >
</filter >
</appender >
<appender name ="FILE" >
<file > ${LOG_PATH}/${APP_NAME}.log</file >
<encoder >
<pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern >
<charset > UTF-8</charset >
</encoder >
<rollingPolicy >
<fileNamePattern > ${LOG_ARCHIVE}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log</fileNamePattern >
<timeBasedFileNamingAndTriggeringPolicy >
<maxFileSize > 100MB</maxFileSize >
</timeBasedFileNamingAndTriggeringPolicy >
<maxHistory > 30</maxHistory >
<totalSizeCap > 3GB</totalSizeCap >
</rollingPolicy >
</appender >
<appender name ="ERROR_FILE" >
<file > ${LOG_PATH}/${APP_NAME}-error.log</file >
<encoder >
<pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern >
<charset > UTF-8</charset >
</encoder >
<filter >
<level > ERROR</level >
</filter >
<rollingPolicy >
<fileNamePattern > ${LOG_ARCHIVE}/${APP_NAME}-error.%d{yyyy-MM-dd}.log</fileNamePattern >
<maxHistory > 90</maxHistory >
</rollingPolicy >
</appender >
<appender name ="ACCESS_FILE" >
<file > ${LOG_PATH}/access.log</file >
<encoder >
<pattern > %msg%n</pattern >
<charset > UTF-8</charset >
</encoder >
<rollingPolicy >
<fileNamePattern > ${LOG_ARCHIVE}/access.%d{yyyy-MM-dd}.log</fileNamePattern >
<maxHistory > 30</maxHistory >
</rollingPolicy >
</appender >
<appender name ="ASYNC_FILE" >
<appender-ref ref ="FILE" />
<queueSize > 512</queueSize >
<discardingThreshold > 0</discardingThreshold >
<includeCallerData > true</includeCallerData >
<neverBlock > true</neverBlock >
</appender >
<logger name ="com.example" level ="DEBUG" additivity ="false" >
<appender-ref ref ="ASYNC_FILE" />
<appender-ref ref ="CONSOLE" />
<appender-ref ref ="ERROR_FILE" />
</logger >
<logger name ="org.springframework" level ="INFO" />
<logger name ="org.hibernate" level ="WARN" />
<logger name ="org.hibernate.SQL" level ="DEBUG" />
<logger name ="org.hibernate.type.descriptor.sql" level ="TRACE" />
<logger name ="com.zaxxer.hikari" level ="INFO" />
<logger name ="org.apache.http" level ="INFO" />
<logger name ="ACCESS_LOG" level ="INFO" additivity ="false" >
<appender-ref ref ="ACCESS_FILE" />
</logger >
<root level ="INFO" >
<appender-ref ref ="CONSOLE" />
<appender-ref ref ="ASYNC_FILE" />
<appender-ref ref ="ERROR_FILE" />
</root >
</configuration >
第九部分:调试与故障排除
9.1 远程调试配置
IDEA 远程调试配置:
运行 → 编辑配置
点击 + → 远程 JVM 调试
配置参数:
名称:Remote Debug
主机:localhost
端口:5005
命令行实参:-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
生产环境远程调试:
java -agentlib:jdwp=transport=dt_socket,server=y,suspend =n,address=*:5005 \n -jar your-application.jar
Docker 容器远程调试: # Dockerfile 中添加调试支持
ENV JAVA_TOOL_OPTIONS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"
EXPOSE 5005
9.2 性能分析工具
VisualVM 集成:
安装 VisualVM 插件
配置 JMX 连接:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9010
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
连接并监控应用性能
IDEA 内置分析工具:
CPU Profiler :分析 CPU 使用情况
Memory Profiler :分析内存使用情况
Async Profiler :异步性能分析
9.3 常见问题与解决方案
问题 1:应用启动缓慢
类路径扫描过多
数据库连接初始化慢
缓存预热时间长
@Configuration
@EnableJpaRepositories(
basePackages = "com.example.repository",
considerNestedRepositories = true
)
@EntityScan(basePackages = "com.example.entity")
@ComponentScan(
basePackages = "com.example",
excludeFilters = @ComponentScan.Filter(
type = FilterType.REGEX,
pattern = "com.example.config.*Test.*"
)
)
public class FastStartupConfig {
@Bean
public CommandLineRunner cacheWarmup (CacheService cacheService) {
return args -> {
CompletableFuture.runAsync(cacheService::warmupCache);
};
}
}
问题 2:内存泄漏
Eclipse Memory Analyzer (MAT)
YourKit Java Profiler
JProfiler
静态集合持有对象引用
未关闭的资源(连接、流等)
线程局部变量未清理
@Component
public class MemoryLeakPrevention {
@PreDestroy
public void cleanup () {
CacheManager.clearAll();
ThreadLocal.remove();
}
@Bean
public ServletListenerRegistrationBean<HttpSessionListener> sessionListener () {
return new ServletListenerRegistrationBean <>(new HttpSessionListener () {
@Override
public void sessionDestroyed (HttpSessionEvent se) {
HttpSession session = se.getSession();
session.removeAttribute("largeObject" );
}
});
}
}
问题 3:数据库连接泄漏
SHOW PROCESSLIST;
SHOW STATUS LIKE 'Threads_connected' ;
@Configuration
public class ConnectionLeakDetection {
@Bean
public DataSource dataSource () {
HikariDataSource dataSource = new HikariDataSource ();
dataSource.setLeakDetectionThreshold(60000 );
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate (DataSource dataSource) {
return new JdbcTemplate (dataSource);
}
@Bean
public ConnectionValidator connectionValidator () {
return new ConnectionValidator ();
}
}
@Component
class ConnectionValidator {
@Scheduled(fixedDelay = 300000)
public void validateConnections () {
}
}
第十部分:最佳实践与总结
10.1 项目结构最佳实践
推荐的包结构: text
com.example.application
├── Application.java
├── config/
│ ├── WebConfig.java
│ ├── SecurityConfig.java
│ ├── DatabaseConfig.java
│ └── CacheConfig.java
├── controller/
│ ├── api/
│ │ ├── v1/
│ │ └── v2/
│ └── web/
├── service/
│ ├── impl/
│ ├── dto/
│ └── mapper/
├── repository/
│ ├── entity/
│ ├── dao/
│ └── jpa/
├── domain/
│ ├── model/
│ ├── event/
│ └── service/
├── infrastructure/
│ ├── cache/
│ ├── message/
│ └── external/
├── common/
│ ├── exception/
│ ├── constant/
│ ├── util/
│ └── annotation/
└── aspect/
├── log /
├── metrics/
└── transaction/
10.2 代码质量保障
代码检查工具集成:
<plugin >
<groupId > org.apache.maven.plugins</groupId >
<artifactId > maven-checkstyle-plugin</artifactId >
<version > 3.2.0</version >
<configuration >
<configLocation > checkstyle.xml</configLocation >
<encoding > UTF-8</encoding >
<consoleOutput > true</consoleOutput >
<failsOnError > true</failsOnError >
<linkXRef > false</linkXRef >
</configuration >
<executions >
<execution >
<id > validate</id >
<phase > validate</phase >
<goals >
<goal > check</goal >
</goals >
</execution >
</executions >
</plugin >
<plugin >
<groupId > com.github.spotbugs</groupId >
<artifactId > spotbugs-maven-plugin</artifactId >
<version > 4.7.2.1</version >
<configuration >
<effort > Max</effort >
<threshold > Low</threshold >
<failOnError > true</failOnError >
</configuration >
<executions >
<execution >
<goals >
<goal > check</goal >
</goals >
</execution >
</executions >
</plugin >
<plugin >
<groupId > org.apache.maven.plugins</groupId >
<artifactId > maven-pmd-plugin</artifactId >
<version > 3.19.0</version >
<configuration >
<rulesets >
<ruleset > rulesets/java/quickstart.xml</ruleset >
</rulesets >
<failOnViolation > true</failOnViolation >
</configuration >
<executions >
<execution >
<goals >
<goal > check</goal >
</goals >
</execution >
</executions >
</plugin >
测试策略:
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
@Tag("integration")
class ApplicationIntegrationTests {
@Test
@DisplayName("完整的应用启动测试")
void contextLoads () {
}
@Test
@DisplayName("REST API 集成测试")
void testRestApi () {
}
}
@WebMvcTest
@AutoConfigureMockMvc
class ControllerTests {
@Test
@DisplayName("控制器单元测试")
void testController () {
}
}
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class RepositoryTests {
@Test
@DisplayName("仓库层测试")
void testRepository () {
}
}
@ExtendWith(MockitoExtension.class)
class ServiceTests {
@Test
@DisplayName("服务层单元测试")
void testService () {
}
}
10.3 安全最佳实践
安全配置检查清单:
依赖安全扫描 :
mvn org.owasp:dependency-check-maven:check
密码安全 :
@Bean
public PasswordEncoder passwordEncoder () {
return new BCryptPasswordEncoder (12 );
}
HTTPS 强制 :
@Configuration
public class HttpsConfig {
@Bean
public SecurityFilterChain filterChain (HttpSecurity http) throws Exception {
http.requiresChannel()
.requestMatchers(r -> r.getHeader("X-Forwarded-Proto" ) != null )
.requiresSecure();
return http.build();
}
}
安全头部配置 :
@Bean
public SecurityFilterChain securityHeaders (HttpSecurity http) throws Exception {
http.headers()
.contentSecurityPolicy("default-src 'self'" )
.and()
.xssProtection()
.and()
.frameOptions().deny()
.and()
.httpStrictTransportSecurity()
.includeSubDomains(true )
.maxAgeInSeconds(31536000 );
return http.build();
}
10.4 总结 IntelliJ IDEA 作为一款强大的 Java 开发工具,配合合理的项目配置和部署策略,可以显著提高 Web 应用的开发效率和运行稳定性。本指南涵盖了从项目创建到生产部署的全过程,包括:
环境配置 :优化 IDE 设置,提高开发效率
项目结构 :合理的模块化和包组织
框架集成 :Spring Boot、数据库、安全等配置
构建工具 :Maven 和 Gradle 的深度配置
部署策略 :本地部署、容器化、云平台部署
监控优化 :性能监控、日志管理、故障排除
最佳实践 :代码质量、安全、测试策略
通过遵循这些最佳实践,您可以构建出高性能、高可用、易维护的企业级 Web 应用。记住,配置管理是一个持续优化的过程,需要根据具体业务需求和运行环境不断调整和完善。
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online