Spring 速查表
本文档提供 Spring Framework 常用配置、注解和代码片段的快速参考。
核心注解
组件注解
| 注解 | 说明 | 示例 |
|---|---|---|
| @Component | 通用组件 | @Component |
| @Repository | 持久层组件 | @Repository |
| @Service | 业务层组件 | @Service |
| @Controller | 控制器组件 | @Controller |
| @RestController | REST 控制器 | @RestController |
| @Configuration | 配置类 | @Configuration |
| @Bean | Bean 定义方法 | @Bean |
依赖注入
| 注解 | 说明 | 示例 |
|---|---|---|
| @Autowired | 自动装配 | @Autowired |
| @Qualifier | 指定 Bean 名称 | @Qualifier("userService") |
| @Primary | 默认 Bean | @Primary |
| @Resource | JSR-250 注入 | @Resource |
| @Inject | JSR-330 注入 | @Inject |
| @Value | 属性注入 | @Value("{app.name}") |
| @Lazy | 延迟加载 | @Lazy |
作用域
| 注解 | 说明 |
|---|---|
| @Scope("singleton") | 单例(默认) |
| @Scope("prototype") | 原型 |
| @Scope("request") | 请求作用域 |
| @Scope("session") | 会话作用域 |
| @Scope("application") | 应用作用域 |
条件化
| 注解 | 说明 |
|---|---|
| @Conditional | 条件化 Bean |
| @ConditionalOnProperty | 属性条件 |
| @ConditionalOnClass | 类存在条件 |
| @ConditionalOnMissingBean | Bean 不存在条件 |
| @Profile | 环境配置 |
IOC 容器
配置类
@Configuration
@ComponentScan("com.example")
@PropertySource("classpath:application.properties")
public class AppConfig {
@Bean
@Primary
public DataSource dataSource() {
return new HikariDataSource();
}
@Bean
@ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
public CacheManager cacheManager() {
return new RedisCacheManager();
}
}
获取 Bean
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
UserService userService = context.getBean("userService", UserService.class);
Map<String, UserService> beans = context.getBeansOfType(UserService.class);
Bean 生命周期
@Service
public class UserService implements InitializingBean, DisposableBean {
@PostConstruct
public void init() {
}
@Override
public void afterPropertiesSet() {
}
@PreDestroy
public void cleanup() {
}
@Override
public void destroy() {
}
}
AOP
切面定义
@Aspect
@Component
@Order(1)
public class LoggingAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
@Before("serviceLayer()")
public void before(JoinPoint joinPoint) {
}
@AfterReturning(pointcut = "serviceLayer()", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
}
@AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
}
@Around("serviceLayer()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
return joinPoint.proceed();
}
}
切入点表达式
| 表达式 | 说明 |
|---|---|
execution(* com.example.service.*.*(..)) | 匹配 service 包下所有方法 |
execution(* com.example.service..*.*(..)) | 匹配 service 包及子包下所有方法 |
execution(public * *(..)) | 匹配所有 public 方法 |
execution(* set*(..)) | 匹配所有 setter 方法 |
within(com.example.service.*) | 匹配包内所有类型 |
@annotation(com.example.Log) | 匹配带注解的方法 |
bean(userService) | 匹配特定 Bean |
事务管理
声明式事务
@Service
public class UserService {
@Transactional
public void save(User user) {
}
@Transactional(readOnly = true)
public User findById(Long id) {
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void log(User user) {
}
@Transactional(isolation = Isolation.READ_COMMITTED)
public void update(User user) {
}
@Transactional(rollbackFor = Exception.class)
public void delete(Long id) {
}
@Transactional(timeout = 30)
public void longOperation() {
}
}
传播行为
| 传播行为 | 说明 |
|---|---|
| REQUIRED | 有事务则加入,无则新建(默认) |
| SUPPORTS | 有事务则加入,无则非事务执行 |
| MANDATORY | 必须在事务中执行 |
| REQUIRES_NEW | 总是新建事务 |
| NOT_SUPPORTED | 非事务方式执行 |
| NEVER | 非事务方式执行,有事务则异常 |
| NESTED | 嵌套事务 |
隔离级别
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ_UNCOMMITTED | √ | √ | √ |
| READ_COMMITTED | × | √ | √ |
| REPEATABLE_READ | × | × | √ |
| SERIALIZABLE | × | × | × |
数据访问
JdbcTemplate
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public User findById(Long id) {
return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
new BeanPropertyRowMapper<>(User.class), id);
}
public List<User> findAll() {
return jdbcTemplate.query(
"SELECT * FROM users",
new BeanPropertyRowMapper<>(User.class));
}
public int save(User user) {
return jdbcTemplate.update(
"INSERT INTO users(name, email) VALUES(?, ?)",
user.getName(), user.getEmail());
}
public Long saveAndGetId(User user) {
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(conn -> {
PreparedStatement ps = conn.prepareStatement(
"INSERT INTO users(name, email) VALUES(?, ?)",
Statement.RETURN_GENERATED_KEYS);
ps.setString(1, user.getName());
ps.setString(2, user.getEmail());
return ps;
}, keyHolder);
return keyHolder.getKey().longValue();
}
}
NamedParameterJdbcTemplate
@Repository
public class UserDao {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
public User findById(Long id) {
String sql = "SELECT * FROM users WHERE id = :id";
MapSqlParameterSource params = new MapSqlParameterSource("id", id);
return jdbcTemplate.queryForObject(sql, params,
new BeanPropertyRowMapper<>(User.class));
}
public List<User> findByIds(List<Long> ids) {
String sql = "SELECT * FROM users WHERE id IN (:ids)";
MapSqlParameterSource params = new MapSqlParameterSource("ids", ids);
return jdbcTemplate.query(sql, params,
new BeanPropertyRowMapper<>(User.class));
}
}
Spring MVC
控制器
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping
public List<User> list() {
return userService.findAll();
}
@GetMapping("/{id}")
public User detail(@PathVariable Long id) {
return userService.findById(id);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public User create(@Valid @RequestBody UserDTO dto) {
return userService.create(dto);
}
@PutMapping("/{id}")
public User update(@PathVariable Long id, @RequestBody User user) {
return userService.update(id, user);
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long id) {
userService.delete(id);
}
@GetMapping("/search")
public List<User> search(
@RequestParam String name,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
return userService.search(name, PageRequest.of(page, size));
}
}
参数注解
| 注解 | 说明 |
|---|---|
| @PathVariable | 路径变量 |
| @RequestParam | 请求参数 |
| @RequestBody | 请求体 |
| @RequestHeader | 请求头 |
| @CookieValue | Cookie 值 |
| @ModelAttribute | 表单绑定 |
| @Valid | 数据验证 |
异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse("NOT_FOUND", ex.getMessage()));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
String message = ex.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(", "));
return ResponseEntity.badRequest()
.body(new ErrorResponse("VALIDATION_ERROR", message));
}
}
缓存
缓存注解
@Service
@CacheConfig(cacheNames = "users")
public class UserService {
@Cacheable(key = "#id")
public User findById(Long id) {
return userDao.findById(id);
}
@Cacheable(key = "#email", unless = "#result == null")
public User findByEmail(String email) {
return userDao.findByEmail(email);
}
@CachePut(key = "#user.id")
public User update(User user) {
return userDao.save(user);
}
@CacheEvict(key = "#id")
public void delete(Long id) {
userDao.deleteById(id);
}
@CacheEvict(allEntries = true)
public void refreshAll() {
}
}
任务调度
定时任务
@Component
public class ScheduledTasks {
@Scheduled(fixedRate = 5000)
public void everyFiveSeconds() {
}
@Scheduled(fixedDelay = 5000)
public void afterCompletion() {
}
@Scheduled(cron = "0 0 12 * * ?")
public void atNoon() {
}
@Scheduled(initialDelay = 10000, fixedRate = 60000)
public void withInitialDelay() {
}
}
异步任务
@Service
public class EmailService {
@Async
public void sendEmail(String to, String content) {
}
@Async
public CompletableFuture<String> sendAsync(String to, String content) {
return CompletableFuture.completedFuture("sent");
}
}
事件
事件定义与使用
public class UserCreatedEvent extends ApplicationEvent {
private final Long userId;
public UserCreatedEvent(Object source, Long userId) {
super(source);
this.userId = userId;
}
}
@Service
public class UserService {
@Autowired
private ApplicationEventPublisher publisher;
public void create(User user) {
publisher.publishEvent(new UserCreatedEvent(this, user.getId()));
}
}
@Component
public class UserEventListener {
@EventListener
public void onUserCreated(UserCreatedEvent event) {
}
@Async
@EventListener
public void asyncHandler(UserCreatedEvent event) {
}
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void afterCommit(UserCreatedEvent event) {
}
}
配置属性
属性绑定
@Configuration
@ConfigurationProperties(prefix = "app")
@Data
public class AppProperties {
private String name;
private int timeout;
private List<String> servers;
}
@Configuration
@ConfigurationProperties(prefix = "app.datasource")
@Data
public class DataSourceProperties {
private String url;
private String username;
private String password;
}
application.properties:
app.name=MyApp
app.timeout=30000
app.servers[0]=server1
app.servers[1]=server2
app.datasource.url=jdbc:mysql://localhost:3306/test
app.datasource.username=root
app.datasource.password=secret
常用配置
数据源配置
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public DataSource dataSource() {
return new HikariDataSource();
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
Web MVC 配置
@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("/static/");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/static/**");
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter());
}
}