注解的优势:

  • 采用纯 java 代码,不在需要配置繁杂的 xml 文件
  • 在配置中也可享受面向对象带来的好处
  • 类型安全对重构可以提供良好的支持
  • 减少复杂配置文件的同时亦能享受到 springIoC 容器提供的功能

1. 常用的Spring Boot注释及其用途和示例

1)@SpringBootApplication

这是一个组合注解,它包含了 @Configuration,@EnableAutoConfiguration 和 @ComponentScan 三个注解。它的作用是标记 Spring Boot 应用的主类,让 Spring Boot 自动进行必要的配置,扫描并加载符合条件的组件或 bean 定义,启动内嵌的 web 服务器等。一般来说,我们只需要在主类上添加这个注解,就可以快速启动一个 Spring Boot 应用。

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

2)@RestController

这是一个组合注解,它包含了 @Controller 和 @ResponseBody 两个注解。它的作用是标记一个控制器类,表示该类的所有方法的返回值都直接写入 HTTP 响应体中,而不是解析为跳转路径。这个注解通常用于构建 RESTful 的 API,返回 JSON 或 XML 格式的数据。

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public User getUser(@PathVariable("id") Long id) {
        return userService.getUserById(id);
    }
}

3)@RequestMapping

这是一个用于映射 HTTP 请求的注解,它可以用在类或方法上。它的作用是指定一个或多个请求路径,以及对应的请求方法,请求参数,请求头等条件,将其绑定到一个控制器方法上。它还可以指定一个返回值的媒体类型,以及一个视图名称等属性。

@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello() {
    return "Hello, Spring Boot!";
}

4)@GetMapping、@PostMapping、 @PutMapping、 @DeleteMapping、@PatchMapping

这些注解都是 @RequestMapping 的缩写,它们分别对应了 GET, POST, PUT, DELETE, PATCH 这五种 HTTP 请求方法。它们的作用是简化 @RequestMapping 的写法,只需要指定一个请求路径即可,其他属性都有默认值。它们的用法和 @RequestMapping 类似,只是更加简洁。

@GetMapping("/hello")
public String hello() {
    return "Hello, Spring Boot!";
}

@PostMapping("/users")
public User createUser(@RequestBody User user) {
    return userService.saveUser(user);
}

5)@PathVariable、@RequestParam、@RequestBody、 @RequestHeader、 @CookieValue

这些注解都是用于获取 HTTP 请求中的数据的注解,它们可以用在控制器方法的参数上。它们的作用是分别从请求路径,请求参数,请求体,请求头,或 Cookie 中获取数据,并将其绑定到方法参数上。它们都可以指定一个参数名,以及一个是否必须的属性。

@GetMapping("/users/{id}")
public User getUser(@PathVariable("id") Long id) {
    return userService.getUserById(id);
}

@GetMapping("/users")
public List<User> getUsers(@RequestParam(value = "name", required = false) String name) {
    return userService.getUsersByName(name);
}

@PostMapping("/users")
public User createUser(@RequestBody User user) {
    return userService.saveUser(user);
}

@GetMapping("/hello")
public String hello(@RequestHeader("User-Agent") String userAgent) {
    return "Hello, your user agent is: " + userAgent;
}

@GetMapping("/cookie")
public String cookie(@CookieValue("JSESSIONID") String sessionId) {
    return "Your session id is: " + sessionId;
}

6)@Autowired、@Resource、@Inject

这些注解都是用于实现依赖注入的注解,它们可以用在字段,构造器,或方法上。它们的作用是从 Spring 容器中获取一个 bean 实例,并将其赋值给被注解的元素。它们的区别是:

  • @Autowired 是 Spring 提供的注解,它默认按照类型匹配 bean,也可以通过 @Qualifier 指定按照名称匹配。
  • @Resource 是 Java 标准提供的注解,它默认按照名称匹配 bean,也可以通过 type 属性指定按照类型匹配。
  • @Inject 是 Java 标准提供的注解,它和 @Autowired 类似,都是按照类型匹配 bean,但是它需要依赖 JSR-330 的实现,如 Google Guice。
@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    @Resource(name = "userCache")
    private Cache userCache;

    @Inject
    private PasswordEncoder passwordEncoder;

    // ...
}

7)@Bean、@Component、 @Service、@Repository、 @Controller

这些注解都是用于声明一个 bean 的注解,它们可以用在类上。它们的作用是让 Spring 容器扫描并识别这些类,将其实例化并加入到容器中,以便其他组件可以使用。它们的区别是:

  • @Bean 是用在方法上的,它表示该方法返回一个 bean 实例,方法所在的类必须被 @Configuration 注解标记。
  • @Component 是一个通用的注解,它表示该类是一个组件,没有特殊的功能。
  • @Service 是一个业务逻辑层的注解,它表示该类是一个服务类,提供一些业务功能。
  • @Repository 是一个数据访问层的注解,它表示该类是一个数据访问对象,提供一些数据操作功能。
  • @Controller 是一个控制器层的注解,它表示该类是一个控制器类,处理 HTTP 请求。
@Configuration
public class AppConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

@Component
public class HelloComponent {

    public void sayHello() {
        System.out.println("Hello, Spring Boot!");
    }
}

@Service
public class UserService {

    // ...
}

@Repository
public class UserDao {

    // ...
}

@Controller
public class UserController {

    // ...
}

8)@Value、 @ConfigurationProperties、 @PropertySource

这些注解都是用于配置属性的注解,它们可以用在类或字段上。它们的作用是从配置文件或环境变量中获取一些属性值,并将其赋值给被注解的元素。它们的区别是:

  • @Value 是用于获取单个属性值的,它可以使用 ${…} 占位符或 SpEL 表达式来获取属性值,也可以指定一个默认值。
  • @ConfigurationProperties 是用于获取一组属性值的,它可以使用 prefix 属性来指定一个属性前缀,然后将其下的所有属性值绑定到一个 bean 的字段上,支持松散绑定和类型转换,也支持 JSR-303 的数据校验。
  • @PropertySource 是用于指定一个额外的属性源的,它可以使用 value 属性来指定一个或多个属性文件的路径,然后将其加载到 Spring 环境中,以便其他注解可以使用。
@Component
@PropertySource("classpath:app.properties")
public class AppConfig {

    @Value("${app.name}")
    private String appName;

    @Value("${app.version:1.0}")
    private String appVersion;

    @Value("#{systemProperties['os.name']}")
    private String osName;

    // ...
}

@Component
@ConfigurationProperties(prefix = "user")
@Validated
public class UserConfig {

    @NotBlank
    private String name;

    @Min(18)
    private int age;

    private List<String> hobbies;

    // getters and setters
}

9)@EnableAutoConfiguration

此注解用于启用 Spring Boot 的自动配置机制。它根据类路径依赖项和属性自动配置应用程序。他可以简化配置过程,从而实现快速开发。

例如

@SpringBootApplication
@EnableAutoConfiguration
public class MyApplication {
    // ...
}

有@EnableAutoConfiguration的情况下:

  • Spring Boot将会根据项目的依赖和配置,自动配置应用程序的各个组件,例如数据源、JPA、Web等。
  • MyService类会被自动扫描并纳入Spring容器管理。

没有@EnableAutoConfiguration的情况下:

  • 我们需要手动配置应用程序的各个组件,例如配置数据源、JPA、Web等,这会增加开发工作量。
  • MyService类不会被自动扫描,需要显式配置才能被Spring容器管理

如果发现正在应用不需要的特定自动配置类,则可以使用 @EnableAutoConfiguration 的 exclude 属性 来禁用它们

例如

@EnableAutoConfiguration(excludeName = {"org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration",
"org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration"})

10)@ConditionalOnProperty

此注解用于根据属性的值有条件地启用或禁用 bean 或配置。

例如

@Configuration
@ConditionalOnProperty(name = "my.feature.enabled", havingValue = "true")
public class MyConfiguration {
    // 当启用时的功能配置
}

11)@Scheduled

此注解用于以固定的时间间隔调度方法的执行。

例如

@Component
public class MyScheduler {
    @Scheduled(fixedDelay = 5000)
    public void doSomething() {
        // 定期执行任务
    }
}

12)@Cacheable、@CachePut、@CacheEvict

这些注解用于缓存方法结果。它们允许您分别缓存方法的返回值、更新缓存或去除缓存。

@Service
public class MyService {
    @Cacheable("users")
    public User getUserById(Long id) {
        // 从数据库检索用户
    }
    
    @CachePut("users")
    public User updateUser(User user) {
        // 更新数据库和缓存中的用户
    }
    
    @CacheEvict("users")
    public void deleteUser(Long id) {
        // 从数据库删除用户并从缓存中移除
    }
}

13)@PostConstruct

@PostConstruct注解用于在依赖注入完成后,且在类的初始化之前执行标注的方法。这提供了一个非常方便的生命周期钩子,允许开发者在对象创建和依赖注入完成后执行初始化代码。

@Component
public class MyBean {

    @PostConstruct
    public void init() {
        // 执行初始化操作,如加载配置文件,检查资源等
        System.out.println("MyBean is initialized");
    }
}

在上述代码中,init方法会在MyBean对象创建并注入所有必要的依赖之后被自动调用。

14)@ExceptionHandler、@ControllerAdvice

@ExceptionHandler注解用于处理Controller层抛出的异常。通过将此注解应用于方法上,可以捕获特定类型的异常,并对其进行自定义处理。

@ControllerAdvice最常见的用途之一是全局异常处理。通过在类上使用@ControllerAdvice注解,然后在该类中使用@ExceptionHandler注解标注的方法,可以捕获指定类型的异常,并进行处理。

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<Object> handleException(Exception e) {
        // 构建自定义的响应体,如错误信息和错误码
        return new ResponseEntity<>("An error occurred", HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

2. 面向切面的编程 (AOP) 注解

这些注解通常用于Spring框架中的面向切面编程(AOP,Aspect-Oriented Programming),于实现面向切面编程,将横切关注点从核心业务逻辑中分离出来,以提高代码的可维护性和可重用性。

1)@Aspect

这个注解用于声明一个类是一个切面类,定义切面类的时候需要添加这个注解。切面类是用来封装切面逻辑的类,通常包含一个或多个通知方法,以及一个或多个切点表达式。切面类需要被 Spring 容器管理,因此通常也需要添加 @Component 注解。例如:

@Aspect
@Component
public class LogAspect {

    // 通知方法和切点表达式
}

2)@Pointcut

这个注解用于定义一个切点表达式,指定哪些连接点(即方法执行的点)需要被拦截。切点表达式可以使用 AspectJ 的语法,也可以使用自定义的注解。切点表达式通常定义在一个空的方法上,作为一个标识符,供其他注解引用。例如:

@Pointcut("execution(* com.example.service.*.*(..))")
public void servicePointcut() {
    // 空方法,仅用于标识切点
}

这个注解表示,所有 com.example.service 包下的类的所有方法都是切点,需要被拦截。

3)@Before, @After, @AfterReturning, @AfterThrowing, @Around

这些注解都是用于定义通知方法的,指定在切点执行的不同阶段执行不同的逻辑。通知方法是用来实现切面功能的方法,通常需要引用一个切点表达式,或者一个切点标识符。这些注解的含义和用法如下:

  • @Before:表示在切点之前执行,可以用来做一些前置处理,如参数校验,日志打印等。
  • @After:表示在切点之后执行,无论切点是否正常返回或抛出异常,都会执行,可以用来做一些清理工作,如释放资源,还原状态等。
  • @AfterReturning:表示在切点正常返回之后执行,可以用来做一些后置处理,如返回结果处理,日志记录等。
  • @AfterThrowing:表示在切点抛出异常之后执行,可以用来做一些异常处理,如异常日志记录,事务回滚等。
  • @Around:表示环绕切点执行,可以在切点前后执行自定义的逻辑,也可以控制切点是否执行,以及修改切点的参数和返回值,是最强大也最复杂的通知类型。

例如:

@Before("servicePointcut()")
public void beforeAdvice(JoinPoint joinPoint) {
    // 获取方法签名和参数
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    Method method = signature.getMethod();
    Object[] args = joinPoint.getArgs();
    // 打印方法信息和参数
    System.out.println("Before advice: " + method + ", args: " + Arrays.toString(args));
}

@AfterReturning(value = "servicePointcut()", returning = "result")
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
    // 获取方法签名和返回值
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    Method method = signature.getMethod();
    // 打印方法信息和返回值
    System.out.println("After returning advice: " + method + ", result: " + result);
}

@AfterThrowing(value = "servicePointcut()", throwing = "ex")
public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
    // 获取方法签名和异常
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    Method method = signature.getMethod();
    // 打印方法信息和异常
    System.out.println("After throwing advice: " + method + ", exception: " + ex.getMessage());
}

@Around("servicePointcut()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
    // 获取方法签名
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    Method method = signature.getMethod();
    // 记录方法开始时间
    long startTime = System.currentTimeMillis();
    // 执行切点方法
    Object result = joinPoint.proceed();
    // 记录方法结束时间
    long endTime = System.currentTimeMillis();
    // 计算方法耗时
    long duration = endTime - startTime;
    // 打印方法信息和耗时
    System.out.println("Around advice: " + method + ", duration: " + duration + " ms");
    // 返回结果
    return result;
}

4)@Order

这个注解用于指定切面类的优先级,当有多个切面类作用于同一个切点时,可以用这个注解来控制执行顺序。注解的值越小,优先级越高。例如:

@Aspect
@Component
@Order(1)
public class LogAspect {
    // ...
}

@Aspect
@Component
@Order(2)
public class SecurityAspect {
    // ...
}

这个注解表示,LogAspect 的优先级高于 SecurityAspect,因此 LogAspect 的通知方法会先于 SecurityAspect 的通知方法执行。

3. 验证注释

这些注解通常用于Java中的Bean Validation(JSR-380)规范中,用于对JavaBean属性进行验证

@Valid、@NotNull、@Size、@Min、@Max、@Email、@Pattern

  • @Valid,用于指示在验证嵌套对象时应该递归执行验证。通常与复杂对象的属性一起使用,以确保嵌套对象的所有属性都被验证。
  • @NotNull,用于验证属性值不能为null。
  • @Size,用于验证属性值的长度是否在指定范围内。
  • @Min,用于验证属性值是否大于等于指定的最小值。
  • @Max,用于验证属性值是否小于等于指定的最大值。
  • @Email,用于验证属性值是否符合Email地址的格式。
  • @Pattern,用于验证属性值是否匹配指定的正则表达式。

例如

public class Address {
    @NotNull
    private String street;
    // 其他属性和方法
}

public class User {
    @Valid
    private Address address;
    @NotNull
    private String username;
    @Size(min = 2, max = 50)
    private String username;
    @Min(18)
    private int age;
    @Max(100)
    private int age;
    @Email
    private String email;
    @Pattern(regexp = "^[A-Za-z0-9]+$")
    private String username;
    // 其他属性和方法
}