Spring Boot
pom xml格式
2.x版本的格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>maven_demo</artifactId> <version>0.0.1-SNAPSHOT</version>
<name>maven_demo</name> <description>maven_demo</description> <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-boot.version>2.6.13</spring-boot.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<build> <plugins>
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin>
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <configuration> <mainClass>org.example.maven_demo.MavenDemoApplication</mainClass> <skip>true</skip> </configuration> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
</project>
|
3.x版本的格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.5.6</version> <relativePath/> </parent>
<groupId>com.xw</groupId> <artifactId>consultant</artifactId> <version>0.0.1-SNAPSHOT</version> <name>consultant</name> <description>consultant</description>
<url/>
<licenses> <license/> </licenses>
<developers> <developer/> </developers>
<scm> <connection/> <developerConnection/> <tag/> <url/> </scm>
<properties> <java.version>17</java.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
</project>
|
配置文件
默认配置文件
放在src/main/resources下,名称必须为application.yml
使用环境变量
1 2 3
| db: host: ${DB_HOST:localhost}
|
日志配置文件
名称为logback-spring.xml,用来定义日志级别、日志格式、日志输出位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
| <?xml version="1.0" encoding="UTF-8"?> <configuration scan="true" scanPeriod="10 seconds">
<contextName>logback</contextName> <property name="log.path" value="F:/yygh_log" />
<property name="CONSOLE_LOG_PATTERN" value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> <encoder> <Pattern>${CONSOLE_LOG_PATTERN}</Pattern> <charset>UTF-8</charset> </encoder> </appender>
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/log_info.log</file> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15</maxHistory> </rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender>
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/log_warn.log</file> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15</maxHistory> </rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>warn</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender>
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/log_error.log</file> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15</maxHistory> </rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender>
<springProfile name="dev"> <logger name="com.guli" level="INFO" />
<root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="WARN_FILE" /> <appender-ref ref="ERROR_FILE" /> </root> </springProfile>
<springProfile name="pro">
<root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="DEBUG_FILE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="ERROR_FILE" /> <appender-ref ref="WARN_FILE" /> </root> </springProfile>
</configuration>
|
不同环境下的配置
最上面的为默认配置,通过---隔开不同环境的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| server: port: 8080
---
spring: config: activate: on-profile: native
server: port: 8081
---
spring: config: activate: on-profile: test
server: port: 8082
---
spring: config: activate: on-profile: production
server: port: 8083
|
读取配置
通过@Value注解一个个读取配置
1 2 3 4 5 6 7 8 9 10
| @Value("${app.name}") private String name;
@Value("${app.port:8080}") private int port;
@Value("${app.servers[0]}") private String firstServer;
|
通过@ConfigurationProperties一次性读取多个配置
1 2 3 4 5 6
| @Configuration @ConfigurationProperties("storage.local") public class StorageConfiguration { ... }
|
打包项目
通过spring-boot-maven-plugin插件来打包
1 2 3 4 5 6 7 8
| <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
|
mvn clean:只清理,不构建
mvn package:直接打包,但不会删除旧文件
mvn clean install :清理 + 构建 + 把包安装到本地仓库(~/.m2/repository),方便别的项目依赖
mvn clean deploy :清理 + 构建 + 部署到远程仓库
mvn clean package:打包项目
打包完项目会在target/目录下生成xxx.jar(包含依赖)和xxx.jar.original(不包含依赖)
可通过java -jar xxx.jar直接运行
常用注解
启动类上
@SpringBootApplication:启动Spring Boot应用程序,标注在启动类上
注解包含了
@SpringBootConfiguration:本质上就是 @Configuration,表示这是一个配置类
@EnableAutoConfiguration:开启自动配置,会根据项目中的 classpath 依赖 和 定义的配置,自动配置 Spring 应用所需要的 Bean
@ComponentScan:负责扫描Bean,默认会扫描启动类所在包及其子包下的组件
@EnableTransactionManagement:开启事务管理@Transactional
@MapperScan:MyBatis 项目里常用,指定 Mapper 接口包路径
@ComponentScan:指定扫描的包,自动发现并注册组件
@Import:显式导入类/配置到容器
配置类上
@Configuration:声明当前类是一个配置类,配合@Bean使用
@Bean:用于方法上,把返回值对象交给 Spring 容器管理
方法的返回值就是要注册到容器中的对象,默认 Bean 名称是方法名
@Lazy:bean 会在第一次使用才创建
方法的参数会被容器解析,相当于@Autowired自动注入
@Conditional:条件装配,根据条件判断是否加载这个Bean
@ConditionalOnClass:某个类存在时才生效
@ConditionalOnMissingBean:容器里没有某个 Bean 时才加载
@ConditionalOnProperty:配置文件里存在某属性时才生效
….
@ConfigurationProperties:把配置文件里的属性绑定到类的属性里
@Profile:只在指定环境下加载Bean
监控运行状态
Actuator 是 Spring Boot 提供的一个监控和管理工具。它可以暴露 应用运行状态、指标、健康信息、日志级别动态修改等 端点(Endpoints)。对生产环境运维和监控非常有用
引入依赖
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
|
添加配置
1 2 3 4 5 6 7
|
management: endpoints: web: exposure: include: info, health, beans, env, metrics
|
访问
1
| http://localhost:8080/actuator/health
|
使用过滤器
1.实现Filter接口
可以重写Filter中的三个方法
| name |
说明 |
| init(FilterConfig) |
创建filter时会调用一次 |
| doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) |
拦截客户端请求时调用 |
| destroy() |
销毁filter时会调用一次 |
2.注册拦截器
方式1: 注解注册
通过@WebFilter(filterName, urlPatterns)注解来说明这是一个过滤器
| 参数 |
说明 |
| filterName |
过滤器名字 |
| urlPatterns |
拦截的路径 |
然后在主启动类上使用@ServletComponentScan注解开启扫描
加上这个注解可以扫描@Servlet、@Filter、@Listener
方式2: 配置类方式注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Configuration public class FilterConfig { @Autowired private Filter1 filter;
@Bean public FilterRegistrationBean filter() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(filter); registration.setName("filter"); registration.addUrlPatterns("/*"); registration.setOrder(1); return registration; } }
|
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| @WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*") public class LoginCheckFilter implements Filter { public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse;
String requestURI = request.getRequestURI();
String[] urls = new String[]{ "/employee/login", "/employee/logout", "/backend/**", "/front/**", "/common/**" }; if (check(urls, requestURI)) { filterChain.doFilter(request, response); return; } else if (request.getSession().getAttribute("employee") != null) { Long id = (Long) request.getSession().getAttribute("employee"); BaseContext.setCurrentId(id); filterChain.doFilter(request, response); return; } else { response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN"))); return; } }
private boolean check(String[] urls, String requestURI) { for (String url : urls) { if (PATH_MATCHER.match(url, requestURI)) { return true; } } return false; } }
|
自定义异常处理器
案例
自定义异常类
基础异常类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Data public class BaseException extends Exception{ private final Integer code;
public BaseException(Integer code, String message) { super(message); this.code = code; }
public BaseException(ResultStatus status) { super(status.getDescription()); this.code = status.getCode(); } }
|
根据业务场景派生不同异常
1 2 3 4 5 6 7 8
|
public class UnauthorizedException extends BaseException{ public UnauthorizedException(String message) { super(ResultStatus.UNAUTHORIZED.getCode(), message); } }
|
添加全局异常处理类
添加@RestControllerAdvice注解来统一捕获所有异常
@RestControllerAdvice = @ControllerAdvice + @ResponseBody
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BaseException.class) public CommonResult<?> handleBaseException(BaseException e) { return CommonResult.fail(e.getCode(), e.getMessage()); }
@ExceptionHandler(NullPointerException.class) public CommonResult<?> handleNullPointerException(NullPointerException e) { return CommonResult.fail(500, "空指针异常"); }
@ExceptionHandler(Exception.class) public CommonResult<?> handleException(Exception e) { return CommonResult.fail(500, "服务器内部错误: " + e.getMessage()); } }
|
业务代码中抛出异常
1 2 3 4 5 6 7 8
| @GetMapping("/test") public CommonResult<String> test() throws BaseException { if(true) { throw new BaseException(666, "testException"); }
return CommonResult.success("test"); }
|