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());
}
}
Spring Security
安全配置
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/", "/login", "/css/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.permitAll()
)
.logout(logout -> logout
.logoutSuccessUrl("/login?logout")
.permitAll()
)
.csrf(csrf -> csrf.ignoringRequestMatchers("/api/**"));
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
方法级安全
@Service
public class UserService {
@PreAuthorize("hasRole('ADMIN')")
public List<User> findAllUsers() { }
@PreAuthorize("hasAuthority('USER_CREATE')")
public User createUser(User user) { }
@PreAuthorize("#username == authentication.name")
public User findByUsername(String username) { }
@PostAuthorize("returnObject.owner == authentication.name")
public Document getDocument(Long id) { }
}
安全注解
| 注解 | 说明 |
|---|---|
| @PreAuthorize | 方法执行前检查权限 |
| @PostAuthorize | 方法执行后检查权限 |
| @PreFilter | 过滤输入参数 |
| @PostFilter | 过滤返回结果 |
| @Secured | 简单角色检查 |
| @WithMockUser | 测试时模拟用户 |
Spring Boot 自动配置
条件注解
| 注解 | 说明 |
|---|---|
| @ConditionalOnClass | 类路径存在指定类 |
| @ConditionalOnMissingClass | 类路径不存在指定类 |
| @ConditionalOnBean | 容器存在指定 Bean |
| @ConditionalOnMissingBean | 容器不存在指定 Bean |
| @ConditionalOnProperty | 属性满足条件 |
| @ConditionalOnResource | 资源存在 |
| @ConditionalOnWebApplication | Web 应用 |
| @ConditionalOnExpression | SpEL 表达式 |
自定义自动配置
@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyServiceProperties.class)
public class MyServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService(MyServiceProperties properties) {
return new MyService(properties.getHost(), properties.getPort());
}
}
排除自动配置
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class
})
public class Application { }
// 或在配置文件中
// spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
Spring 测试
测试注解
| 注解 | 说明 |
|---|---|
| @SpringBootTest | 完整 Spring Boot 测试 |
| @WebMvcTest | MVC 层切片测试 |
| @DataJpaTest | JPA 层切片测试 |
| @DataJdbcTest | JDBC 层切片测试 |
| @MockBean | 创建 Mock Bean |
| @SpyBean | 创建 Spy Bean |
| @AutoConfigureMockMvc | 自动配置 MockMvc |
MockMvc 测试
@WebMvcTest(UserController.class)
class UserControllerTests {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void testGetUser() throws Exception {
when(userService.findById(1L)).thenReturn(new User(1L, "test"));
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("test"));
}
}
数据层测试
@DataJpaTest
class UserRepositoryTests {
@Autowired
private UserRepository userRepository;
@Autowired
private TestEntityManager entityManager;
@Test
void testFindById() {
User user = new User("test");
entityManager.persist(user);
Optional<User> found = userRepository.findById(user.getId());
assertTrue(found.isPresent());
}
}
安全测试
@SpringBootTest
@AutoConfigureMockMvc
class SecurityTests {
@Autowired
private MockMvc mockMvc;
@Test
void testUnauthenticated() throws Exception {
mockMvc.perform(get("/api/private"))
.andExpect(status().isUnauthorized());
}
@Test
@WithMockUser(username = "admin", roles = {"ADMIN"})
void testAuthenticated() throws Exception {
mockMvc.perform(get("/api/admin"))
.andExpect(status().isOk());
}
}