IntelliJ IDEA 项目配置与Web部署完全指南
第一部分: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环境变量
初始配置优化:
json
// 配置示例:idea.properties 关键参数 -Xms2048m // 初始堆内存 -Xmx4096m // 最大堆内存 -XX:ReservedCodeCacheSize=512m // 代码缓存大小 -Dsun.io.useCanonCaches=false // 禁用规范缓存 -Djava.net.preferIPv4Stack=true // 优先使用IPv4
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
<!-- 生成的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-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
groovy
// 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配置示例:
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
1. 项目设置:
- 项目SDK:指定Java版本
- 语言级别:设置语言特性兼容性
- 编译器输出路径
2. 模块设置:
- 源代码目录标记:
- Sources:源代码目录(蓝色)
- Tests:测试代码目录(绿色)
- Resources:资源文件目录
- Test Resources:测试资源目录
- Excluded:排除目录(橙色)
3. 库管理:
- 全局库:所有项目可用
- 项目库:当前项目可用
- 模块库:特定模块可用
4. 构面(Facets):
- 为模块添加特定框架支持
- 配置框架特定参数
- 示例:Spring、Web、JPA等
5. 工件(Artifacts):
- 定义项目输出格式
- 配置打包方式和内容
- 设置依赖包含策略
编译器配置:
访问方式:文件 → 设置 → 构建、执行、部署 → 编译器
json
// 推荐的编译器配置: { "自动构建项目": 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/ # Java源代码 │ ├── resources/ # 资源文件 │ └── webapp/ # Web资源 │ ├── WEB-INF/ │ │ ├── web.xml # 部署描述符 │ │ └── classes/ │ └── index.jsp # 首页 └── pom.xml
web.xml配置示例:
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> <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映射 --> <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):
java
// 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资源映射:
java
@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服务器配置步骤:
1. 下载和配置Tomcat:
- 从Apache官网下载Tomcat
- 解压到合适目录
- 配置环境变量(可选)
2. 在IDEA中集成Tomcat:
- 运行 → 编辑配置
- 点击 + → Tomcat服务器 → 本地
- 配置服务器:
- 名称:Local Tomcat
- 应用程序服务器:选择Tomcat安装目录
- HTTP端口:8080
- JMX端口:1099
3. 部署配置:
json
// Tomcat部署配置示例 { "服务器配置": { "名称": "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.xml配置示例片段:
xml
<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 热部署与热交换配置
配置热部署:
1. IDEA设置:
- 文件 → 设置 → 构建、执行、部署 → 部署
- 启用 自动上传更改的文件
- 设置上传间隔:默认500毫秒
2. Spring Boot DevTools配置:
xml
<!-- pom.xml依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency>
properties
# 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/**
3. JRebel配置(商业工具):
- 安装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 # 生产日志配置
多环境配置示例:
java
// Spring Boot多环境配置 @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() { // 生产环境数据源 } }
运行配置示例:
json
// 开发环境运行配置 { "类型": "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配置:
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配置类示例:
java
@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 数据库配置与连接
数据源配置:
java
@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配置类:
java
@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", "/**/*.png", "/**/*.gif", "/**/*.svg", "/**/*.jpg", "/**/*.html", "/**/*.css", "/**/*.js") .permitAll() .antMatchers("/api/auth/**") .permitAll() .antMatchers("/api/user/checkUsernameAvailability", "/api/user/checkEmailAvailability") .permitAll() .antMatchers(HttpMethod.GET, "/api/polls/**", "/api/users/**") .permitAll() .anyRequest() .authenticated(); // 添加JWT过滤器 http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); // 添加CORS过滤器 http.addFilterBefore(corsFilter(), JwtAuthenticationFilter.class); } @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("http://localhost:3000"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }
第六部分:构建工具配置
6.1 Maven深度配置
完整pom.xml示例:
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>UTF-8</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> <!-- Spring BOM --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-framework-bom</artifactId> <version>${spring.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- Jackson BOM --> <dependency> <groupId>com.fasterxml.jackson</groupId> <artifactId>jackson-bom</artifactId> <version>${jackson.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <!-- Database --> <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> <!-- Utilities --> <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> <!-- Testing --> <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> <!-- Dev Tools --> <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> <!-- Spring Boot Maven Plugin --> <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> <!-- Compiler 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> <!-- War 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> <!-- Surefire Plugin for Testing --> <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> <!-- JaCoCo for Code Coverage --> <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> <!-- Dependency Plugin for Analysis --> <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> <!-- Clean 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> <!-- Resources 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> <!-- Enforcer Plugin for Rules --> <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> <!-- Versions 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示例:
groovy
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
# 多阶段构建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配置:
yaml
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配置:
yaml
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 \ 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):
typescript
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); // 创建VPC 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, } ] }); // 创建RDS MySQL实例 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', }); // 创建ElastiCache Redis集群 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: [/* security group IDs */], cacheSubnetGroupName: redisSubnetGroup.ref, }); // 创建ECS集群 const cluster = new ecs.Cluster(this, 'WebAppCluster', { vpc, containerInsights: true, }); // 创建Fargate服务 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), }); // 允许ECS访问数据库 database.connections.allowFrom(fargateService.service, ec2.Port.tcp(3306)); // 输出负载均衡器URL new cdk.CfnOutput(this, 'LoadBalancerDNS', { value: fargateService.loadBalancer.loadBalancerDnsName, }); } }
第八部分:监控与优化
8.1 应用监控配置
Spring Boot Actuator配置:
properties
# 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
自定义健康检查:
java
@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); } }
指标监控配置:
java
@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优化参数:
properties
# 生产环境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服务器优化:
java
@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; } }
数据库连接池优化:
properties
# 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
<?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调试
- 配置参数:properties名称: Remote Debug 主机: localhost 端口: 5005 命令行实参: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
生产环境远程调试:
bash
# 启动应用时添加JVM参数 java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 \ -jar your-application.jar
Docker容器远程调试:
dockerfile
# Dockerfile中添加调试支持 ENV JAVA_TOOL_OPTIONS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005" EXPOSE 5005
9.2 性能分析工具
VisualVM集成:
- 安装VisualVM插件
- 配置JMX连接:properties-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:应用启动缓慢
可能原因:
- 类路径扫描过多
- 数据库连接初始化慢
- 缓存预热时间长
解决方案:
java
@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
常见内存泄漏场景:
- 静态集合持有对象引用
- 未关闭的资源(连接、流等)
- 线程局部变量未清理
预防措施:
java
@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:数据库连接泄漏
检测方法:
sql
-- 监控数据库连接 SHOW PROCESSLIST; SHOW STATUS LIKE 'Threads_connected';
预防措施:
java
@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) // 每5分钟执行一次 public void validateConnections() { // 验证和清理无效连接 } }
第十部分:最佳实践与总结
10.1 项目结构最佳实践
推荐的包结构:
text
com.example.application ├── Application.java # 主启动类 ├── config/ # 配置类 │ ├── WebConfig.java │ ├── SecurityConfig.java │ ├── DatabaseConfig.java │ └── CacheConfig.java ├── controller/ # 控制器层 │ ├── api/ # REST API │ │ ├── v1/ # API版本v1 │ │ └── v2/ # API版本v2 │ └── web/ # Web控制器 ├── service/ # 服务层 │ ├── impl/ # 服务实现 │ ├── dto/ # 数据传输对象 │ └── mapper/ # 对象映射器 ├── repository/ # 数据访问层 │ ├── entity/ # 实体类 │ ├── dao/ # 数据访问对象 │ └── jpa/ # JPA仓库 ├── domain/ # 领域层 │ ├── model/ # 领域模型 │ ├── event/ # 领域事件 │ └── service/ # 领域服务 ├── infrastructure/ # 基础设施层 │ ├── cache/ # 缓存 │ ├── message/ # 消息队列 │ └── external/ # 外部服务集成 ├── common/ # 公共模块 │ ├── exception/ # 异常处理 │ ├── constant/ # 常量定义 │ ├── util/ # 工具类 │ └── annotation/ # 自定义注解 └── aspect/ # 切面 ├── log/ # 日志切面 ├── metrics/ # 指标切面 └── transaction/ # 事务切面
10.2 代码质量保障
代码检查工具集成:
xml
<!-- checkstyle配置 --> <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> <!-- spotbugs配置 --> <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> <!-- PMD配置 --> <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>
测试策略:
java
// 分层测试策略 @SpringBootTest @AutoConfigureMockMvc @ActiveProfiles("test") @Tag("integration") class ApplicationIntegrationTests { @Test @DisplayName("完整的应用启动测试") void contextLoads() { // 验证Spring上下文加载 } @Test @DisplayName("REST API集成测试") void testRestApi() { // 完整的API测试 } } @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 安全最佳实践
安全配置检查清单:
- 依赖安全扫描:bashmvn org.owasp:dependency-check-maven:check
- 密码安全:java@Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(12); // 使用高强度加密 }
- HTTPS强制:java@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(); } }
- 安全头部配置:java@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应用。记住,配置管理是一个持续优化的过程,需要根据具体业务需求和运行环境不断调整和完善。