SpringBoot-CheatSheet
Spring Boot CheatSheet
可以在 Spring Initializr 动态地选择需要的组件,对于
// @SpringBootApplication 整合了 @Configuration + @ComponentScan + @EnableAutoConfiguration,其会自动进行组件扫描与配置
@SpringBootApplication
public class FooApplication {
public static void main(String[] args) {
// Bootstrap the application
SpringApplication.run(FooApplication.class, args);
}
}
- @Configuration: Marks a class as a config class using Spring’s Java based configuration
- @ComponentScan: Enables component-scanning so that web controller classes can be automatically registered as beans in the Spring application context
- @EnableAutoConfiguration: Configures the application based on the dependencies
依赖声明与注入
依赖声明
注解声明
@Component
public class MyComponent {}
在
在
因此,当你的一个类被
最后,如果你不知道要在项目的业务层采用
@Configuration
public class TestConfig {
@Bean(name="helloClient")
public HessianProxyFactoryBean helloClient() {
HessianProxyFactoryBean factory = new HessianProxyFactoryBean();
...
return factory;
}
}
Conditional Configuration | 条件化配置
Class conditions allow us to specify that a configuration bean will be included if a specified class is present.
@Configuration
@ConditionalOnClass(DataSource.class)
public class MySQLAutoconfiguration {
//...
}
也可以根据某个
@Bean
@ConditionalOnBean(name = "dataSource")
@ConditionalOnMissingBean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
...
return em;
}
还可以根据是否存在某个属性配置来决定是否需要创建某个
@Bean
@ConditionalOnProperty(
name = "usemysql",
havingValue = "local")
@ConditionalOnMissingBean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
...
return dataSource;
}
// Defining Condition that checks if the JdbcTemplate is available on the classpath
//
// Conditions are used by the auto-configuration mechanism of Spring Boot
// There are several configuration classes in the spring-boot-autoconfigure.jar
// which contribute to the configuration if specific conditions are met
public class JdbcTemplateCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
try {
context.getClassLoader().loadClass("org.springframework.jdbc.core.JdbcTemplate");
return true;
} catch (Exception e) {
return false;
}
}
}
// Use a custom condition class to decide whether a Bean should be created or not
@Conditional(JdbcTemplateCondition.class)
public class MyService {
...
}
条件化注解 | 配置生效条件 |
---|---|
@ConditionalOnBean | 配置了某个特定 |
@ConditionalOnMissingBean | 没有配置特定的 |
@ConditionalOnClass | |
@ConditionalOnMissingClass | |
@ConditionalOnExpression | 给定的 |
@ConditionalOnJava | |
@ConditionalOnProperty | 指定的配置属性要有一个明确的值 |
@ConditionalOnResource | |
@ConditionalOnWebApplication | 这是一个 |
@ConditionalOnNotWebApplication | 这不是一个 |
作用域与生命周期
-
Singleton, 单例模式,Spring IoC 容器中只会存在一个共享的Bean 实例,无论有多少个Bean 引用它,始终指向同一对象。Singleton 作用域是Spring 中的缺省作用域,也可以显式的将Bean 定义为Singleton 模式 -
Prototype, 原型模式,每次通过Spring 容器获取prototype 定义的bean 时,容器都将创建一个新的Bean 实例,每个Bean 实例都有自己的属性和状态,而Singleton 全局只有一个对象。根据经验,对有状态的bean 使用prototype 作用域,而对无状态的bean 使用Singleton 作用域。 -
Request, 在一次Http 请求中,容器会返回该Bean 的同一实例。而对不同的Http 请求则会产生新的Bean ,而且该bean 仅在当前Http Request 内有效。 -
Session, 在一次Http Session 中,容器会返回该Bean 的同一实例。而对不同的Session 请求则会创建新的实例,该bean 实例仅在当前Session 内有效。 -
Global Session, 在一个全局的Http Session 中,容器会返回该Bean 的同一个实例,仅在使用portlet context 时有效。
可以通过

我们常用的生命周期的
@PostConstruct
public void initAfterStartup() {
...
}
@PreDestroy
public void cleanupBeforeExit() {
...
}
对于使用
@Bean(destroyMethod = "close")
public MyBean myBean(){...
Application LifeCycle | 应用生命周期
@Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(
CommandLineAppStartupRunner.class
);
@Override
public void run(String... args) throws Exception {
logger.info(
"Application started with command-line arguments: {} . \n To kill this application, press Ctrl + C.",
Arrays.toString(args)
);
}
}
@Component
public class AppStartupRunner implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(
AppStartupRunner.class
);
@Override
public void run(ApplicationArguments args) throws Exception {
logger.info(
"Your application started with option names : {}",
args.getOptionNames()
);
}
}
配置管理
# 在本地跑时,默认是 local,在其它环境跑时,要通过 -Dspring.profiles.active= 来指定
spring.profiles.active=local
当某些属性的值需要配置的时候,我们一般会在
// jdbc.mysql.url=jdbc:mysql://localhost:3306/sampledb
// 配置数据源
@Configuration
public class HikariDataSourceConfiguration {
@Value("jdbc.mysql.url")
public String url;
...
@Bean
public HikariDataSource dataSource() {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setJdbcUrl(url);
...
return new HikariDataSource(hikariConfig);
}
}
对于更为复杂的配置,
@Configuration
@PropertySource("classpath:configprops.properties")
@ConfigurationProperties(prefix = "mail")
public class ConfigProperties {
public static class Credentials {
private String authMethod;
private String username;
private String password;
// standard getters and setters
}
private String host;
private int port;
private String from;
private Credentials credentials;
private List<String> defaultRecipients;
private Map<String, String> additionalHeaders;
// standard getters and setters
}
// 使用的时候直接注入
@AutoWired
public ConfigProperties config;
#Simple properties
mail.host=mailer@mail.com
mail.port=9000
mail.from=mailer@mail.com
#List properties
mail.defaultRecipients[0]=admin@mail.com
mail.defaultRecipients[1]=owner@mail.com
#Map Properties
mail.additionalHeaders.redelivery=true
mail.additionalHeaders.secure=true
#Object properties
mail.credentials.username=john
mail.credentials.password=password
mail.credentials.authMethod=SHA1
也可以为配置类添加校验:
@Length(max = 4, min = 1)
private String authMethod;
端口设置
Controller | 请求处理
传统的

路由与参数
@RestController
@RequestMapping("/persons")
class PersonController {
@GetMapping("/{id}")
public Person getPerson(@PathVariable Long id) {
// ...
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public void add(@RequestBody Person person) {
// ...
}
}
请求校验
@GetMapping("/hello/{name}")
private String hello(@PathVariable(value = "name", required = true) String name){
//...
}
@GetMapping("/name")
private ResponseEntity<?> queryPerson(@RequestParam(value = "query", required = false) String query) {
// ...
}
对于复杂请求体的验证,可以使用
public class Message {
@NotNull
private String title;
@NotNull
private String message;
// getters/setters/etc
}
然后在
@PostMapping
public ResponseEntity<?> createMessage(@Valid @RequestBody Message message) {
// ...
}
如果我们需要去自定义校验器,则可以选择去扩展
public class InRangeValidator implements ConstraintValidator<InRange, Integer> {
private int min;
private int max;
@Override
public void initialize(InRange constraintAnnotation) {
this.min = constraintAnnotation.min();
this.max = constraintAnnotation.max();
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return value == null || (value >= min && value <= max);
}
}
// 扩展 InRange 接口,添加自定义属性
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = { InRangeValidator.class })
public @interface InRange {
String message() default "Value is out of range";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
int min() default Integer.MIN_VALUE;
int max() default Integer.MAX_VALUE;
}
在使用的时候,直接注解即可:
@NotNull(groups = {Existing.class, New.class})
@InRange(
min=18,
message = "User must be at least 18 years old",
groups = {Existing.class, New.class}
)
private Integer age;
最后,我们还可以自定义返回的错误格式,譬如以
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleException(MethodArgumentNotValidException exception) {
String errorMsg = exception.getBindingResult().getFieldErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.findFirst()
.orElse(exception.getMessage());
return ErrorResponse.builder().message(errorMsg).build();
}
响应
Service | 服务
Logging | 日志
日志配置
对外接口统一拦截捕获,避免异常向外系统传播,自身系统无法感知问题。
严格规范日志输出等级,尤其
服务层日志统一输出,包括耗时、接口成功标识、业务成功标识,为监控做准备。
所有日志
<appender name="ERROR-APPENDER"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/common-error.log</file>
<!-- Error 级别过滤 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN:-%5p} - [%thread] : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}
</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天滚动,可根据实际量调整单位 -->
<fileNamePattern>${LOG_PATH}/common-error.log.%d{yyyy-MM-dd}
</fileNamePattern>
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<root level="INFO">
<!-- root中增加Error输出配置 -->
<appender-ref ref="ERROR-APPENDER" />
</root>
<logger name="testLog" level="INFO"
additivity="false">
<appender-ref ref="WORK_SHIFT_CORE_MONITOR_LOG" />
<!-- 每个logger增加ERROR输出 -->
<appender-ref ref="ERROR-APPENDER" />
</logger>
缓存
dependencies {
compile("org.springframework.boot:spring-boot-starter-cache")
}
然后在@EnableCaching
注解,这样我们在进行方法调用时可以缓存调用结果:
@Cacheable("books")
public Book getByIsbn(String isbn) {
...
}
Storage | 数据访问
Spring JDBC Template
MyBatis
Redis
Test | 测试
请求
// Testing classes in Spring Boot
@RunWith(SpringJUnit4ClassRunner.class)
// Load context via Spring Boot
@SpringApplicationConfiguration(classes = ReadinglistApplication.class)
@WebAppConfiguration
public class ReadinglistApplicationTests {
// Test that the context successfully loads (the method can be empty -> the test will fail if the context cannot be loaded)
@Test
public void contextLoads() {}
}
服务
// Integration test by loading Springs application context
// To to integration testing with Spring, all components of the application have to be configured and wired up.
// Instead of doing this by hand we can use Spring's SpringJUnit4ClassRunner.
// It helps load a Spring application context in JUnit-based application tests.
// This method with the @ContextConfiguration annotation doesn't apply extenal properites (application.properties) and logging
// @ContextConfiguration specifies how to load the application context: A configuraiton class is passed to it as a parameter
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = PlaylistConfiguration.class)
public class PlaylistServiceTests {
@Autowired
private PlaylistService playlistService;
@Test
public void testService() {
Playlist playlist = playlistService.findByName("X-Mas Songs");
assertEquals("X-Mas Songs", playlist.getName());
assertEquals(12, playlist.countSongs());
}
}
数据存储
@SpringBootTest
@Transactional
class MySpec extends Specification {
@Autowired
MyRepository myRepo
def "Persist an entity"() {
given:
MyEntity entity = new MyEntity()
when:
myRepo.saveAndFlush(entity)
then:
myRepo.count() == 1
}
def "Persist another entity"() {
given:
MyEntity entity = new MyEntity()
when:
myRepo.saveAndFlush(entity)
then:
myRepo.count() == 1
}
}