注解的优势:
- 采用纯 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;
// 其他属性和方法
}