用来加载 URL 中?之后的参数
@GetMapping("/notice")public boolean isNeedToNoticeUser(@RequestParam("email") String email) {return userNoticeService.isNeedToNoticeUser(email);}
用来加载 POST/PUT 请求的复杂请求体
@PostMapping("/status")public Response jobStatus(@RequestBody @Validated SenderJobDTO jobDTO) {ConsumerJobVO vo = senderJobService.jobStatus(jobDTO);return Response.ok().data(vo);}
用来加载 URL 路径中的参数
@GetMapping("/user/{id}")@ResponseBody()public User findUserById(@PathVariable("id") String id){return userRepo.findById(id);}
API 的参数通过;分割。比如:这个请求/books/reviews;isbn=1234;topN=5
; 就可以如下面这样,使用@MatrixVariable
来加载 URL 中用;分割的参数
@GetMapping("/books/reviews")@ResponseBody()public List<BookReview> getBookReviews(@MatrixVariable String isbn, @MatrixVariable Integer topN) {return bookReviewsLogic.getTopNReviewsByIsbn(isbn, topN);}
用来加载请求头中的数据
@GetMapping("/user")@ResponseBody()public List<User> getUserList(@RequestHeader("Authorization") String authToken) {return userRepo.findAll();}
服务端读取 Cookie 数据
@GetMapping("/user")@ResponseBody()public List<User> getUserList(@CookieValue(name = "SessionId") String sessionId) {return userRepo.findAll();}
常用的有两种方式:@Value 和@ConfigurationProperties
Feature | @ConfigurationProperties | @Value |
---|---|---|
Relaxed binding | Yes | Limited (see note below) |
Meta-data support | Yes | No |
SpEL evaluation | No | Yes |
首先需要在配置类上增加@Configuration
带默认值的
@Value("${kafka.producer.retry:1}")private int retry;
使用 Spring Expression Language (SpEL)
@Value("#{'${actuator.send.business.type:eims,item}'.split(',')}")List<String> specifyType;
该方式可以将相应的配置封装在具体类内,对代码组织和封装友好,推进使用这种方式
@ConfigurationProperties(prefix = "my.server")public class MyServerProperties {private String name="test";@NotNullprivate String bootstrapServers;private Host host;// getters/setters ...// 嵌入式结构public static class Host {private String ip;private int port;// getters/setters ...}}
my.server:name: testbootstrap_servers: xxxxx:9092host:ip: xxxport: 8081
扩展:
为了让 spring 容器可以识别该配置类,有多种方式,这里我推荐在启动类上添加注解@ConfigurationPropertiesScan({"top.trumandu.config"})
配置参数
Spring(relaxed binding )使用一些宽松的绑定属性规则。因此,以下变体都将绑定到 hostName 属性上:
mail.hostName=localhostmail.hostname=localhostmail.host_name=localhostmail.host-name=localhostmail.HOST_NAME=localhost
为了让 ide 可以识别我们自定义的配置文件,可以添加 spring-boot-configuration-processor,这样就可以自动生成additional-spring-configuration-metadata.json
, 进而进行提示。
classpath root
> classpath/config
> current directory
> current directory/config/
> current directory/../config
默认使用 Logback,通常我们只用如下配置即可快速使用.
logging:level:root: "warn"org.springframework.web: "debug"org.hibernate: "error"
当无法满足一些日志场景时,Logback 扩展可以通过在src/main/resources
创建logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?><configuration><!--<jmxConfigurator/> --><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{ISO8601} %-5level [%thread] %logger{0}: %msg%n</pattern></encoder></appender><appender name="FILE"class="ch.qos.logback.core.rolling.RollingFileAppender"><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>logs/info.%d{yyyy-MM-dd}.log</fileNamePattern><maxHistory>7</maxHistory></rollingPolicy><encoder><pattern>%d{ISO8601} %-5level [%thread] %logger{0}: %msg%n</pattern></encoder></appender><appender name="ERROR_FILE"class="ch.qos.logback.core.rolling.RollingFileAppender"><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>logs/error.%d{yyyy-MM-dd}.log</fileNamePattern><maxHistory>7</maxHistory></rollingPolicy><encoder><pattern>%d{ISO8601} %-5level [%thread] %logger{0}: %msg%n</pattern></encoder><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>error</level></filter></appender><logger name="top.trumandu" level="info" /><logger name="org.apache" level="error" /><logger name="org.I0Itec.zkclient" level="error" /><root level="info"><appender-ref ref="STDOUT" /><appender-ref ref="FILE" /><appender-ref ref="ERROR_FILE" /></root></configuration>
可以实现ApplicationRunner
和CommandLineRunner
接口
@Componentpublic class MyCommandLineRunner implements CommandLineRunner {@Overridepublic void run(String... args) {// Do something...}}
优先级可以通过@Ordered
实现
首先启动定时注解配置
@EnableScheduling
通过以下代码可以快速实现定时 job
@Componentpublic class ManualGcJob {// 每两秒运行一次@Scheduled(cron = "0/2 * * * * ?")public void scheduleJvmGc(){System.out.println("manual gc");}}
不仅通过 corn 表达式,也可以通过 fixedDelay 和 fixedRate 配置
支持配置文件配置参数
@Scheduled(fixedDelayString = "${category.fixed.delay:300000}", initialDelayString = "${category.fixed.delay:300000}")public void updateCategory() {System.out.println("update category");}
程序硬编码指定
@Scheduled(fixedDelay = 24 * 60 * 60 * 1000, initialDelay = 24 * 60 * 60 * 1000)public void scheduleJvmGc() {System.out.println("manual gc");}
fixedDelay 和 fixedRate 区别:fixedDelay 需要等上一个任务完成后,再延迟相应时间才运行。
spring boot scheduling 相关配置如下:
spring:task:scheduling:thread-name-prefix: "scheduling-"pool:size: 2
springboot 默认定时调度线程池只有一个,强烈建议使用的时候更新该 pool.size 大小
如果使用 RestTemplate,强烈建议配置超时时间,编码等配置
@Configurationpublic class RestTemplateConfig {/*** 第三方请求要求的默认编码*/private final Charset thirdRequest = StandardCharsets.UTF_8;@Beanpublic RestTemplate restTemplate(ClientHttpRequestFactory factory) {RestTemplate restTemplate = new RestTemplate(factory);// 处理请求中文乱码问题List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();for (HttpMessageConverter<?> messageConverter : messageConverters) {if (messageConverter instanceof StringHttpMessageConverter) {((StringHttpMessageConverter) messageConverter).setDefaultCharset(thirdRequest);}if (messageConverter instanceof MappingJackson2HttpMessageConverter) {((MappingJackson2HttpMessageConverter) messageConverter).setDefaultCharset(thirdRequest);}if (messageConverter instanceof AllEncompassingFormHttpMessageConverter) {((AllEncompassingFormHttpMessageConverter) messageConverter).setCharset(thirdRequest);}}return restTemplate;}@Beanpublic ClientHttpRequestFactory simpleClientHttpRequestFactory() {SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();factory.setConnectTimeout(15000);factory.setReadTimeout(5000);return factory;}}
在 spring 中可以使用@Async
来声明方法异步执行,前提需要启用异步配置@EnableAsync
使用该功能需要注意三个问题:
//无效代码案例@Servicepublic class PurchaseService {public void purchase(){sendEmail();}@Asyncpublic void sendEmail(){// Asynchronous code}}//正确代码@Servicepublic class EmailService {@Asyncpublic void sendEmail(){// Asynchronous code}}@Servicepublic class PurchaseService {public void purchase(){emailService.sendEmail();}@Autowiredprivate EmailService emailService;}
//错误案例@Servicepublic class EmailService {@Asyncpublic void sendEmail() throws Exception{throw new Exception("Oops, cannot send email!");}}@Servicepublic class PurchaseService {@Autowiredprivate EmailService emailService;public void purchase(){try{emailService.sendEmail();}catch (Exception e){System.out.println("Caught exception: " + e.getMessage());}}}//正确案例@Servicepublic class EmailService {@Asyncpublic Future<String> sendEmail() throws Exception{throw new Exception("Oops, cannot send email!");}}@Servicepublic class PurchaseService {@Autowiredprivate EmailService emailService;public void purchase(){try{Future<String> future = emailService.sendEmail();String result = future.get();System.out.println("Result: " + result);}catch (Exception e){System.out.println("Caught exception: " + e.getMessage());}}}
spring:task:execution:thread-name-prefix: email-sync-taskpool:core-size: 10max-size: 20queue-capacity: 10keep-alive: 10s
或者
@Beanpublic ThreadPoolTaskExecutor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(2);executor.setMaxPoolSize(2);executor.setQueueCapacity(500);executor.setThreadNamePrefix("MyAsyncThread-");executor.setRejectedExecutionHandler((r, executor1) -> log.warn("Task rejected, thread pool is full and queue is also full"));executor.initialize();return executor;}
@Valid
和 @Validated
都是 Spring 框架中用于启用数据验证的注解
@Valid
:只能用于单一的 Bean Validation 校验,无法进行分组校验。@Validated
:不仅支持基本校验,还支持通过分组来进行不同场景下的定制化校验。
public class UserDto {@NotNull(message = "Name is required")private String name;@Email(message = "Invalid email address")private String email;}// 在 Controller 中使用 @Valid@PostMapping("/user")public ResponseEntity<String> createUser(@Valid @RequestBody UserDto user, BindingResult result) {if (result.hasErrors()) {return ResponseEntity.badRequest().body(result.getFieldErrors().toString());}return ResponseEntity.ok("User created successfully");}
public class UserDto {@NotNull(message = "Name is required", groups = Create.class)private String name;@NotNull(message = "Email is required", groups = {Create.class, Update.class})@Email(message = "Invalid email format", groups = {Create.class, Update.class})private String email;@NotNull(message = "Password is required for new users", groups = Create.class)@Size(min = 8, message = "Password must be at least 8 characters", groups = Create.class)private String password;public interface Create {}public interface Update {}}// 在 Controller 中使用 @Validated 来指定分组@PostMapping("/createUser")public ResponseEntity<String> createUser(@Validated(UserDto.Create.class) @RequestBody UserDto user, BindingResult result) {if (result.hasErrors()) {return ResponseEntity.badRequest().body(result.getFieldErrors().toString());}return ResponseEntity.ok("User created successfully");}@PutMapping("/updateUser")public ResponseEntity<String> updateUser(@Validated(UserDto.Update.class) @RequestBody UserDto user, BindingResult result) {if (result.hasErrors()) {return ResponseEntity.badRequest().body(result.getFieldErrors().toString());}return ResponseEntity.ok("User updated successfully");}
Create 场景:用户在创建时,name 和 password 都必须被验证,而 email 需要验证格式。 Update 场景:在更新时,只验证 email 和 name,且不需要验证 password。