注解
使用注解
什么是注解(Annotation)?注解是放在Java源码的类、方法、字段、参数前的一种特殊“注释”:
// this is a component:
@Resource("hello")
public class Hello {
@Inject
int n;
@PostConstruct
public void hello(@Param String name) {
System.out.println(name);
}
@Override
public String toString() {
return "Hello";
}
}注释会被编译器直接忽略,注解则可以被编译器打包进入class文件,因此,注解是一种用作标注的“元数据”。
注解的作用
从JVM的角度看,注解本身对代码逻辑没有任何影响,如何使用注解完全由工具决定。
Java的注解可以分为三类:
第一类是由编译器使用的注解,例如:
@Override:让编译器检查该方法是否正确地实现了覆写;@SuppressWarnings:告诉编译器忽略此处代码产生的警告。
这类注解不会被编译进入.class文件,它们在编译后就被编译器扔掉了。
第二类是由工具处理.class文件使用的注解,比如有些工具会在加载class的时候,对class做动态修改,实现一些特殊的功能。
这类注解会被编译进入.class文件,但加载结束后并不会存在于内存中。这类注解只被一些底层库使用,一般我们不必自己处理。
第三类是在程序运行期能够读取的注解,它们在加载后一直存在于JVM中,这也是最常用的注解。例如,一个配置了@PostConstruct的方法会在调用构造方法后自动被调用(这是Java代码读取该注解实现的功能,JVM并不会识别该注解)。
定义一个注解时,还可以定义配置参数。配置参数可以包括:
- 所有基本类型;
- String;
- 枚举类型;
- 基本类型、String、Class以及枚举的数组。
因为配置参数必须是常量,所以,上述限制保证了注解在定义时就已经确定了每个参数的值。
注解的配置参数可以有默认值,缺少某个配置参数时将使用默认值。
此外,大部分注解会有一个名为value的配置参数,对此参数赋值,可以只写常量,相当于省略了value参数。
如果只写注解,相当于全部使用默认值。
举个栗子,对以下代码:
public class Hello {
@Check(min=0, max=100, value=55)
public int n;
@Check(value=99)
public int p;
@Check(99) // @Check(value=99)
public int x;
@Check
public int y;
}@Check就是一个注解。第一个@Check(min=0, max=100, value=55)明确定义了三个参数,第二个@Check(value=99)只定义了一个value参数,它实际上和@Check(99)是完全一样的。最后一个@Check表示所有参数都使用默认值。
定义注解
Java语言使用@interface语法来定义注解(Annotation),它的格式如下:
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}注解的参数类似无参数方法,可以用default设定一个默认值(强烈推荐)。最常用的参数应当命名为value。
元注解
有一些注解可以修饰其他注解,这些注解就称为元注解(meta annotation)。Java标准库已经定义了一些元注解,我们只需要使用元注解,通常不需要自己去编写元注解。
@Target
最常用的元注解是@Target。使用@Target可以定义Annotation能够被应用于源码的哪些位置:
- 类或接口:
ElementType.TYPE; - 字段:
ElementType.FIELD; - 方法:
ElementType.METHOD; - 构造方法:
ElementType.CONSTRUCTOR; - 方法参数:
ElementType.PARAMETER。
例如,定义注解@Report可用在方法上,我们必须添加一个@Target(ElementType.METHOD):
@Target(ElementType.METHOD)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
定义注解@Report可用在方法或字段上,可以把@Target注解参数变为数组{ ElementType.METHOD, ElementType.FIELD }:
@Target({
ElementType.METHOD,
ElementType.FIELD
})
public @interface Report {
...
}实际上@Target定义的value是ElementType[]数组,只有一个元素时,可以省略数组的写法。
@Retention
另一个重要的元注解@Retention定义了Annotation的生命周期:
- 仅编译期:
RetentionPolicy.SOURCE; - 仅class文件:
RetentionPolicy.CLASS; - 运行期:
RetentionPolicy.RUNTIME。
如果@Retention不存在,则该Annotation默认为CLASS。因为通常我们自定义的Annotation都是RUNTIME,所以,务必要加上@Retention(RetentionPolicy.RUNTIME)这个元注解:
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}@Repeatable
使用@Repeatable这个元注解可以定义Annotation是否可重复。这个注解应用不是特别广泛。
@Repeatable(Reports.class)
@Target(ElementType.TYPE)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
@Target(ElementType.TYPE)
public @interface Reports {
Report[] value();
}
经过@Repeatable修饰后,在某个类型声明处,就可以添加多个@Report注解:
@Report(type=1, level="debug")
@Report(type=2, level="warning")
public class Hello {
}
@Inherited
使用@Inherited定义子类是否可继承父类定义的Annotation。@Inherited仅针对@Target(ElementType.TYPE)类型的annotation有效,并且仅针对class的继承,对interface的继承无效:
@Inherited
@Target(ElementType.TYPE)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
在使用的时候,如果一个类用到了@Report:
@Report(type=1)
public class Person {
}
则它的子类默认也定义了该注解:
public class Student extends Person {
}如何定义Annotation
第一步,用@interface定义注解:
public @interface Report {
}第二步,添加参数、默认值:
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
把最常用的参数定义为value(),推荐所有参数都尽量设置默认值。
第三步,用元注解配置注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}其中,必须设置@Target和@Retention,@Retention一般设置为RUNTIME,因为我们自定义的注解通常要求在运行期读取。一般情况下,不必写@Inherited和@Repeatable。
处理注解
Java的注解本身对代码逻辑没有任何影响。根据@Retention的配置:
SOURCE类型的注解在编译期就被丢掉了;CLASS类型的注解仅保存在class文件中,它们不会被加载进JVM;RUNTIME类型的注解会被加载进JVM,并且在运行期可以被程序读取。
如何使用注解完全由工具决定。SOURCE类型的注解主要由编译器使用,因此我们一般只使用,不编写。CLASS类型的注解主要由底层工具库使用,涉及到class的加载,一般我们很少用到。只有RUNTIME类型的注解不但要使用,还经常需要编写。
因此,我们只讨论如何读取RUNTIME类型的注解。
因为注解定义后也是一种class,所有的注解都继承自java.lang.annotation.Annotation,因此,读取注解,需要使用反射API。
Java提供的使用反射API读取Annotation的方法包括:
判断某个注解是否存在于Class、Field、Method或Constructor:
- Class.isAnnotationPresent(Class)
- Field.isAnnotationPresent(Class)
- Method.isAnnotationPresent(Class)
- Constructor.isAnnotationPresent(Class)
例如:
// 判断@Report是否存在于Person类:
Person.class.isAnnotationPresent(Report.class);
使用反射API读取Annotation:
- Class.getAnnotation(Class)
- Field.getAnnotation(Class)
- Method.getAnnotation(Class)
- Constructor.getAnnotation(Class)
例如:
// 获取Person定义的@Report注解:
Report report = Person.class.getAnnotation(Report.class);
int type = report.type();
String level = report.level();
使用反射API读取Annotation有两种方法。方法一是先判断Annotation是否存在,如果存在,就直接读取:
Class cls = Person.class;
if (cls.isAnnotationPresent(Report.class)) {
Report report = cls.getAnnotation(Report.class);
...
}第二种方法是直接读取Annotation,如果Annotation不存在,将返回null:
Class cls = Person.class;
Report report = cls.getAnnotation(Report.class);
if (report != null) {
...
}
读取方法、字段和构造方法的Annotation和Class类似。但要读取方法参数的Annotation就比较麻烦一点,因为方法参数本身可以看成一个数组,而每个参数又可以定义多个注解,所以,一次获取方法参数的所有注解就必须用一个二维数组来表示。
例如,对于以下方法定义的注解:
public void hello(@NotNull @Range(max=5) String name, @NotNull String prefix) {
}
要读取方法参数的注解,我们先用反射获取Method实例,然后读取方法参数的所有注解:
// 获取Method实例:
Method m = ...
// 获取所有参数的Annotation:
Annotation[][] annos = m.getParameterAnnotations();
// 第一个参数(索引为0)的所有Annotation:
Annotation[] annosOfName = annos[0];
for (Annotation anno : annosOfName) {
if (anno instanceof Range r) { // @Range注解
r.max();
}
if (anno instanceof NotNull n) { // @NotNull注解
//
}
}
使用注解
注解如何使用,完全由程序自己决定。例如,JUnit是一个测试框架,它会自动运行所有标记为@Test的方法。
我们来看一个@Range注解,我们希望用它来定义一个String字段的规则:字段长度满足@Range的参数定义:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range {
int min() default 0;
int max() default 255;
}
在某个JavaBean中,我们可以使用该注解:
public class Person {
@Range(min=1, max=20)
public String name;
@Range(max=10)
public String city;
}
但是,定义了注解,本身对程序逻辑没有任何影响。我们必须自己编写代码来使用注解。这里,我们编写一个Person实例的检查方法,它可以检查Person实例的String字段长度是否满足@Range的定义:
void check(Person person) throws IllegalArgumentException, ReflectiveOperationException {
// 遍历所有Field:
for (Field field : person.getClass().getFields()) {
// 获取Field定义的@Range:
Range range = field.getAnnotation(Range.class);
// 如果@Range存在:
if (range != null) {
// 获取Field的值:
Object value = field.get(person);
// 如果值是String:
if (value instanceof String s) {
// 判断值是否满足@Range的min/max:
if (s.length() < range.min() || s.length() > range.max()) {
throw new IllegalArgumentException("Invalid field: " + field.getName());
}
}
}
}
}
这样一来,我们通过@Range注解,配合check()方法,就可以完成Person实例的检查。注意检查逻辑完全是我们自己编写的,JVM不会自动给注解添加任何额外的逻辑。
Java
Spring
核心注解
| 注解 | 说明 | 支持版本 |
|---|---|---|
| @Component | 标识一个类为 Spring 组件 | Spring 2.5+ |
| @Repository | 标识数据访问组件 | Spring 2.0+ |
| @Service | 标识服务层组件 | Spring 2.0+ |
| @Controller | 标识控制器组件 | Spring 2.5+ |
| @RestController | @Controller + @ResponseBody | Spring 4.0+ |
| @Configuration | 标识配置类 | Spring 3.0+ |
| @Bean | 声明一个方法的返回值为 Bean | Spring 3.0+ |
| @Autowired | 自动装配依赖 | Spring 2.5+ |
| @Qualifier | 指定注入的 Bean 名称 | Spring 2.5+ |
| @Primary | 优先注入的 Bean | Spring 3.0+ |
| @Value | 注入属性值 | Spring 3.0+ |
| @PropertySource | 加载属性文件 | Spring 3.1+ |
| @Profile | 指定组件在特定环境下激活 | Spring 3.1+ |
| @Scope | 定义 Bean 的作用域 | Spring 2.5+ |
| @Lazy | 延迟初始化 Bean | Spring 3.0+ |
| @DependsOn | 定义 Bean 初始化依赖 | Spring 3.0+ |
@Controller
用于修饰MVC中controller层的组件,SpringBoot中的组件扫描功能会识别到该注解,并为修饰的类实例化对象,通常与@RequestMapping联用,当SpringMVC获取到请求时会转发到指定路径的方法进行处理。
@Controller
@RequestMapping("/admin")
public class UmsAdminController {
}@RestController
用于表示controller层的组件,与@Controller注解的不同在于,相当于在每个请求处理方法上都添加了@ResponseBody注解,这些方法都将返回JSON格式数据。
@Service
用于修饰service层的组件,service层组件专注于系统业务逻辑的处理,同样会被组件扫描并生成实例化对象。
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
}@Repository
用于修饰dao层的组件,dao层组件专注于系统数据的处理,例如数据库中的数据,同样会被组件扫描并生成实例化对象。
@Repository
public interface UmsAdminRoleRelationDao {
}@Configuration
于声明一个Java形式的配置类,SpringBoot推荐使用Java配置,在该类中声明的Bean等配置将被SpringBoot的组件扫描功能扫描到。
@Configuration
@MapperScan({"com.macro.mall.mapper","com.macro.mall.dao"})
public class MyBatisConfig {
}@Component
用于修饰SpringBoot中的组件,会被组件扫描并生成实例化对象。@Controller、@Service、@Repository都是特殊的组件注解。
@Component
public class CancelOrderSender {
}@Autowired
会根据对象的类型自动注入依赖对象,默认要求注入对象实例必须存在,可以配置required=false来注入不一定存在的对象。
@Controller
@RequestMapping("/admin")
public class UmsAdminController {
@Autowired
private UmsAdminService adminService;
}@Resource
默认会根据对象的名称自动注入依赖对象,如果想要根据类型进行注入,可以设置属性为type = UmsAdminService.class。
@Controller
@RequestMapping("/admin")
public class UmsAdminController {
@Autowired
@Resource(name = "umsAdminServiceImpl")
private UmsAdminService adminService;
}@Qualifier
当同一个对象有多个实例可以注入时,使用@Autowired注解无法进行注入,这时可以使用@Qualifier注解指定实例的名称进行精确注入。
@Controller
@RequestMapping("/admin")
public class UmsAdminController {
@Autowired
@Qualifier("umsAdminServiceImpl")
private UmsAdminService adminService;
}@Bean
用于修饰方法,标识该方法会创建一个Bean实例,并交给Spring容器来管理。
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}@Scope
用于声明一个SpringBean实例的作用域,作用域的范围有以下几种:
- singleton:单例模式,在Spring容器中该实例唯一,Spring默认的实例模式。
- prototype:原型模式,每次使用实例都将重新创建。
- request:在同一请求中使用相同的实例,不同请求重新创建。
- session:在同一会话中使用相同的实例,不同会话重新创建。
@Configuration
public class RestTemplateConfig {
@Bean
@Scope("singleton")
public RestTemplate restTemplate(){
return new RestTemplate();
}
}@Primary
当同一个对象有多个实例时,优先选择该实例。
@Configuration
public class JacksonConfig {
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return objectMapper;
}
}@PostConstruct
用于修饰方法,当对象实例被创建并且依赖注入完成后执行,可用于对象实例的初始化操作。
@PreDestroy
用于修饰方法,当对象实例将被Spring容器移除时执行,可用于对象实例持有资源的释放。
public class DynamicSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
private static Map<String, ConfigAttribute> configAttributeMap = null;
@Autowired
private DynamicSecurityService dynamicSecurityService;
@PostConstruct
public void loadDataSource() {
configAttributeMap = dynamicSecurityService.loadDataSource();
}
@PostConstruct
public void loadDataSource() {
configAttributeMap = dynamicSecurityService.loadDataSource();
}
@PreDestroy
public void clearDataSource() {
configAttributeMap.clear();
configAttributeMap = null;
}
}Web MVC 注解
| 注解 | 说明 | 支持版本 |
|---|---|---|
| @RequestMapping | 映射请求路径 | Spring 2.5+ |
| @GetMapping | GET 请求映射 | Spring 4.3+ |
| @PostMapping | POST 请求映射 | Spring 4.3+ |
| @PutMapping | PUT 请求映射 | Spring 4.3+ |
| @DeleteMapping | DELETE 请求映射 | Spring 4.3+ |
| @PatchMapping | PATCH 请求映射 | Spring 4.3+ |
| @RequestParam | 获取请求参数 | Spring 2.5+ |
| @PathVariable | 获取路径变量 | Spring 3.0+ |
| @RequestBody | 获取请求体内容 | Spring 3.0+ |
| @ResponseBody | 返回内容直接写入响应体 | Spring 3.0+ |
| @ModelAttribute | 绑定参数到模型对象 | Spring 2.5+ |
| @SessionAttribute | 访问会话属性 | Spring 3.1+ |
| @CookieValue | 获取 Cookie 值 | Spring 3.0+ |
| @RequestHeader | 获取请求头值 | Spring 3.0+ |
| @CrossOrigin | 跨域支持 | Spring 4.2+ |
| @ExceptionHandler | 异常处理方法 | Spring 3.0+ |
| @ControllerAdvice | 全局控制器增强 | Spring 3.2+ |
| @ResponseStatus | 定义响应状态码 | Spring 3.0+ |
@RequestMapping
可用于将Web请求路径映射到处理类的方法上,当作用于类上时,可以统一类中所有方法的路由路径,当作用于方法上时,可单独指定方法的路由路径。
method属性可以指定请求的方式,如GET、POST、PUT、DELETE等。
@RequestBody
表示方法的请求参数为JSON格式,从Body中传入,将自动绑定到方法参数对象中。
@ResponseBody
表示方法将返回JSON格式的数据,会自动将返回的对象转化为JSON数据。
@RequestParam
用于接收请求参数,可以是如下三种形式:
- query param:GET请求拼接在地址里的参数。
- form data:POST表单提交的参数。
- multipart:文件上传请求的部分参数。
@PathVariable
用于接收请求路径中的参数,常用于REST风格的API。
@RequestPart
用于接收文件上传中的文件参数,通常是multipart/form-data形式传入的参数。
@Controller
@RequestMapping("/minio")
public class MinioController {
@RequestMapping(value = "/upload", method = RequestMethod.POST)
@ResponseBody
public CommonResult upload(@RequestPart("file") MultipartFile file) {
//省略文件上传操作...
return CommonResult.success(minioUploadDto);
}
}SpringMVC注解示例:
@Controller
@RequestMapping("/admin")
public class UmsAdminController {
@RequestMapping(value = "/register", method = RequestMethod.POST)
@ResponseBody
public CommonResult<UmsAdmin> register(@RequestBody UmsAdminParam umsAdminParam) {
UmsAdmin umsAdmin = adminService.register(umsAdminParam);
if (umsAdmin == null) {
return CommonResult.failed();
}
return CommonResult.success(umsAdmin);
}
@RequestMapping(value = "/list", method = RequestMethod.GET)
@ResponseBody
public CommonResult<CommonPage<UmsAdmin>> list(@RequestParam(value = "keyword", required = false) String keyword,
@RequestParam(value = "pageSize", defaultValue = "5") Integer pageSize,
@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum) {
List<UmsAdmin> adminList = adminService.list(keyword, pageSize, pageNum);
return CommonResult.success(CommonPage.restPage(adminList));
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@ResponseBody
public CommonResult<UmsAdmin> getItem(@PathVariable Long id) {
UmsAdmin admin = adminService.getItem(id);
return CommonResult.success(admin);
}
}@GetMapping
用于表示GET请求方法,等价于@RequestMapping(method = RequestMethod.GET)。
@PostMapping
用于表示POST请求方法,等价于@RequestMapping(method = RequestMethod.POST)。
REST风格注解示例:
@RestController
@RequestMapping("/admin")
public class UmsAdminController {
@PostMapping("/register")
public CommonResult<UmsAdmin> register(@RequestBody UmsAdminParam umsAdminParam) {
UmsAdmin umsAdmin = adminService.register(umsAdminParam);
if (umsAdmin == null) {
return CommonResult.failed();
}
return CommonResult.success(umsAdmin);
}
@GetMapping("/list")
public CommonResult<CommonPage<UmsAdmin>> list(@RequestParam(value = "keyword", required = false) String keyword,
@RequestParam(value = "pageSize", defaultValue = "5") Integer pageSize,
@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum) {
List<UmsAdmin> adminList = adminService.list(keyword, pageSize, pageNum);
return CommonResult.success(CommonPage.restPage(adminList));
}
}@ControllerAdvice
常与@ExceptionHandler注解一起使用,用于捕获全局异常,能作用于所有controller中。
@ExceptionHandler
修饰方法时,表示该方法为处理全局异常的方法。
@ControllerAdvice
public class GlobalExceptionHandler {
@ResponseBody
@ExceptionHandler(value = ApiException.class)
public CommonResult handle(ApiException e) {
if (e.getErrorCode() != null) {
return CommonResult.failed(e.getErrorCode());
}
return CommonResult.failed(e.getMessage());
}
}事务注解
| 注解 | 说明 | 支持版本 |
|---|---|---|
| @Transactional | 声明事务 | Spring 2.0+ |
| @EnableTransactionManagement | 启用事务管理 | Spring 3.1+ |
@EnableTransactionManagement
启用Spring基于注解的事务管理功能,需要和@Configuration注解一起使用。
@Configuration
@EnableTransactionManagement
@MapperScan({"com.macro.mall.mapper","com.macro.mall.dao"})
public class MyBatisConfig {
}@Transactional
表示方法和类需要开启事务,当作用与类上时,类中所有方法均会开启事务,当作用于方法上时,方法开启事务,方法上的注解无法被子类所继承。
public interface OmsPortalOrderService {
/**
* 根据提交信息生成订单
*/
@Transactional
Map<String, Object> generateOrder(OrderParam orderParam);
}数据访问注解
| 注解 | 说明 | 支持版本 |
|---|---|---|
| @Entity | JPA 实体类 | JPA 1.0+ |
| @Table | 指定实体对应的表 | JPA 1.0+ |
| @Id | 标识主键 | JPA 1.0+ |
| @GeneratedValue | 主键生成策略 | JPA 1.0+ |
| @Column | 指定字段映射 | JPA 1.0+ |
| @OneToOne | 一对一关系 | JPA 1.0+ |
| @OneToMany | 一对多关系 | JPA 1.0+ |
| @ManyToOne | 多对一关系 | JPA 1.0+ |
| @ManyToMany | 多对多关系 | JPA 1.0+ |
| @JoinColumn | 指定关联列 | JPA 1.0+ |
| @Query | 自定义查询语句 | Spring Data 1.0+ |
| @Modifying | 标识修改操作 | Spring Data 1.0+ |
| @EnableJpaRepositories | 启用 JPA 仓库 | Spring Data 1.0+ |
Spring Boot 特有注解
| 注解 | 说明 | 支持版本 |
|---|---|---|
| @SpringBootApplication | 主启动类注解 | Spring Boot 1.2+ |
| @EnableAutoConfiguration | 启用自动配置 | Spring Boot 1.0+ |
| @ComponentScan | 组件扫描 | Spring 3.1+ |
| @ConditionalOnClass | 类路径存在时生效 | Spring Boot 1.0+ |
| @ConditionalOnMissingBean | Bean 不存在时生效 | Spring Boot 1.0+ |
| @ConditionalOnProperty | 属性条件判断 | Spring Boot 1.0+ |
| @ConfigurationProperties | 绑定配置属性 | Spring Boot 1.0+ |
| @EnableConfigurationProperties | 启用配置属性绑定 | Spring Boot 1.0+ |
| @EnableAsync | 启用异步方法 | Spring 3.0+ |
| @Async | 标识异步方法 | Spring 3.0+ |
| @EnableScheduling | 启用定时任务 | Spring 3.0+ |
| @Scheduled | 定时任务方法 | Spring 3.0+ |
| @EnableCaching | 启用缓存 | Spring 3.1+ |
| @Cacheable | 缓存方法结果 | Spring 3.1+ |
| @CacheEvict | 清除缓存 | Spring 3.1+ |
| @CachePut | 更新缓存 | Spring 3.1+ |
@SpringBootApplication
用于表示SpringBoot应用中的启动类,相当于@EnableAutoConfiguration、@EnableAutoConfiguration和@ComponentScan三个注解的结合体。
@SpringBootApplication
public class MallTinyApplication {
public static void main(String[] args) {
SpringApplication.run(MallTinyApplication.class, args);
}
}@EnableAutoConfiguration
启用SpringBoot的自动化配置,会根据你在pom.xml添加的依赖和application-dev.yml中的配置自动创建你需要的配置。
@Configuration
@EnableAutoConfiguration
public class AppConfig {
}@ComponentScan
启用SpringBoot的组件扫描功能,将自动装配和注入指定包下的Bean实例。
@Configuration
@ComponentScan({"xyz.erupt","com.macro.mall.tiny"})
public class EruptConfig {
}@EnableCaching
当添加Spring Data Redis依赖之后,可用该注解开启Spring基于注解的缓存管理功能。
@EnableCaching
@Configuration
public class RedisConfig extends BaseRedisConfig {
}@value
用于注入在配置文件中配置好的属性,例如我们可以在application.yml配置如下属性:
jwt:
tokenHeader: Authorization #JWT存储的请求头
secret: mall-admin-secret #JWT加解密使用的密钥
expiration: 604800 #JWT的超期限时间(60*60*24*7)
tokenHead: 'Bearer ' #JWT负载中拿到开头然后在Java类中就可以使用@Value注入并进行使用了。
public class JwtTokenUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
@Value("${jwt.tokenHead}")
private String tokenHead;
}@ConfigurationProperties
用于批量注入外部配置,以对象的形式来导入指定前缀的配置,比如这里我们在application.yml中指定了secure.ignored为前缀的属性:
secure:
ignored:
urls: #安全路径白名单
- /swagger-ui/
- /swagger-resources/**
- /**/v2/api-docs
- /**/*.html
- /**/*.js
- /**/*.css
- /**/*.png
- /**/*.map
- /favicon.ico
- /actuator/**
- /druid/**然后在Java类中定义一个urls属性就可以导入配置文件中的属性了。
@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "secure.ignored")
public class IgnoreUrlsConfig {
private List<String> urls = new ArrayList<>();
}@Conditional
用于表示当某个条件满足时,该组件或Bean将被Spring容器创建,下面是几个常用的条件注解。
- @ConditionalOnBean:当某个Bean存在时,配置生效。
- @ConditionalOnMissingBean:当某个Bean不存在时,配置生效。
- @ConditionalOnClass:当某个类在Classpath存在时,配置生效。
- @ConditionalOnMissingClass:当某个类在Classpath不存在时,配置生效。
@Configuration
public class JacksonConfig {
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return objectMapper;
}
}测试注解
| 注解 | 说明 | 支持版本 |
|---|---|---|
| @SpringBootTest | Spring Boot 测试 | Spring Boot 1.4+ |
| @DataJpaTest | JPA 测试 | Spring Boot 1.4+ |
| @WebMvcTest | MVC 控制器测试 | Spring Boot 1.4+ |
| @MockBean | 模拟 Bean | Spring Boot 1.4+ |
| @Test | JUnit 测试方法 | JUnit 4+ |
| @BeforeEach | 每个测试方法前执行 | JUnit 5+ |
| @AfterEach | 每个测试方法后执行 | JUnit 5+ |
| @BeforeAll | 所有测试方法前执行 | JUnit 5+ |
| @AfterAll | 所有测试方法后执行 | JUnit 5+ |
安全注解 (Spring Security)
| 注解 | 说明 | 支持版本 |
|---|---|---|
| @EnableWebSecurity | 启用 Web 安全 | Spring Security 3.2+ |
| @PreAuthorize | 方法执行前权限检查 | Spring Security 3.0+ |
| @PostAuthorize | 方法执行后权限检查 | Spring Security 3.0+ |
| @Secured | 方法权限检查 | Spring Security 2.0+ |
| @RolesAllowed | JSR-250 角色检查 | Spring Security 2.0+ |
| @EnableGlobalMethodSecurity | 启用方法级安全 | Spring Security 3.0+ |
@EnableWebSecurity
启用SpringSecurity的Web功能。
@EnableGlobalMethodSecurity
启用SpringSecurity基于方法的安全功能,当我们使用@PreAuthorize修饰接口方法时,需要有对应权限的用户才能访问。
Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig{
}Spring Cache
@Cacheable
添加在方法上,缓存方法的执行结果。执行过程如下:
- 1)首先,判断方法执行结果的缓存。如果有,则直接返回该缓存结果。
- 2)然后,执行方法,获得方法结果。
- 3)之后,根据是否满足缓存的条件。如果满足,则缓存方法结果到缓存。
- 4)最后,返回方法结果。
@CachePut
添加在方法上,缓存方法的执行结果。不同于 @Cacheable 注解,它的执行过程如下:
- 1)首先,执行方法,获得方法结果。也就是说,无论是否有缓存,都会执行方法。
- 2)然后,根据是否满足缓存的条件。如果满足,则缓存方法结果到缓存。
- 3)最后,返回方法结果。
@CacheEvict
添加在方法上,删除缓存
AOP相关注解
| 注解 | 说明 |
|---|---|
| @Aspect | 用于定义切面,切面是通知和切点的结合,定义了何时、何地应用通知功能。 |
| @Before | 表示前置通知(Before),通知方法会在目标方法调用之前执行,通知描述了切面要完成的工作以及何时执行。 |
| @After | 表示后置通知(After),通知方法会在目标方法返回或抛出异常后执行。 |
| @AfterReturning | 表示返回通知(AfterReturning),通知方法会在目标方法返回后执行。 |
| @AfterThrowing | 表示异常通知(AfterThrowing),通知方法会在目标方法返回后执行。 |
| @Around | 表示环绕通知(Around),通知方法会将目标方法封装起来,在目标方法调用之前和之后执行自定义的行为。 |
| @Pointcut | 定义切点表达式,定义了通知功能被应用的范围。 |
| @Order | 用于定义组件的执行顺序,在AOP中指的是切面的执行顺序,value属性越低优先级越高。 |
AOP相关示例:
@Aspect
@Component
@Order(1)
public class WebLogAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(WebLogAspect.class);
@Pointcut("execution(public * com.macro.mall.tiny.controller.*.*(..))")
public void webLog() {
}
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
}
@AfterReturning(value = "webLog()", returning = "ret")
public void doAfterReturning(Object ret) throws Throwable {
}
@Around("webLog()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
WebLog webLog = new WebLog();
//省略日志处理操作...
Object result = joinPoint.proceed();
LOGGER.info("{}", JSONUtil.parse(webLog));
return result;
}
}
Lombok
| 注解 | 说明 | 支持版本 |
|---|---|---|
| @val | 用在局部变量前面,相当于将变量声明为final | - |
| @NonNull | 给方法参数增加这个注解会自动在方法内对该参数进行是否为空的校验,如果为空,则抛出NPE(NullPointerException) | - |
| @Cleanup | 自动管理资源,用在局部变量之前,在当前变量范围内即将执行完毕退出之前会自动清理资源,自动生成try-finally这样的代码来关闭流 | - |
| @Getter/@Setter | 用在属性上,再也不用自己手写setter和getter方法了,还可以指定访问范围 | - |
| @ToString | 用在类上,可以自动覆写toString方法,当然还可以加其他参数,例如@ToString(exclude=”id”)排除id属性,或者@ToString(callSuper=true, includeFieldNames=true)调用父类的toString方法,包含所有属性 | - |
| @EqualsAndHashCode | 用在类上,自动生成equals方法和hashCode方法 | - |
| @NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor | 用在类上,自动生成无参构造和使用所有参数的构造函数以及把所有@NonNull属性作为参数的构造函数,如果指定staticName = “of”参数,同时还会生成一个返回类对象的静态工厂方法,比使用构造函数方便很多 | - |
| @Data | 注解在类上,相当于同时使用了@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstrutor这些注解,对于POJO类十分有用 | - |
| @Value | 用在类上,是@Data的不可变形式,相当于为属性添加final声明,只提供getter方法,而不提供setter方法 | - |
| @Builder | 用在类、构造器、方法上,为你提供复杂的builder APIs,让你可以像如下方式一样调用Person.builder().name(“Adam Savage”).city(“San Francisco”).job(“Mythbusters”).job(“Unchained Reaction”).build();更多说明参考Builder | - |
| @SneakyThrows | 自动抛受检异常,而无需显式在方法上使用throws语句 | - |
| @Synchronized | 用在方法上,将方法声明为同步的,并自动加锁,而锁对象是一个私有的属性$lock或$LOCK,而java中的synchronized关键字锁对象是this,锁在this或者自己的类对象上存在副作用,就是你不能阻止非受控代码去锁this或者类对象,这可能会导致竞争条件或者其它线程错误 | - |
| @Getter(lazy=true) | 可以替代经典的Double Check Lock样板代码 | - |
| @Log | 根据不同的注解生成不同类型的log对象,但是实例名称都是log,有六种可选实现类 | - |
@CommonsLogCreates log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);@LogCreates log = java.util.logging.Logger.getLogger(LogExample.class.getName());@Log4jCreates log = org.apache.log4j.Logger.getLogger(LogExample.class);@Log4j2Creates log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);@Slf4jCreates log = org.slf4j.LoggerFactory.getLogger(LogExample.class);@XSlf4jCreates log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
Hibernate Validator
| 注解 | 说明 | 支持版本 |
|---|---|---|
| @NotBlank | 只能用于字符串不为 null ,并且字符串 #trim() 以后 length 要大于 0 | - |
| @NotEmpty | 集合对象的元素不为 0 ,即集合不为空,也可以用于字符串不为 null | - |
| @NotNull | 不能为 null | |
| @Pattern(value) | 被注释的元素必须符合指定的正则表达式 | - |
| @Max(value) | 该字段的值只能小于或等于该值 | - |
| @Min(value) | 该字段的值只能大于或等于该值 | - |
| @Range(min=, max=) | 检被注释的元素必须在合适的范围内 | - |
| @Size(max, min) | 检查该字段的 size 是否在 min 和 max 之间,可以是字符串、数组、集合、Map 等 | - |
| @Length(max, min) | 被注释的字符串的大小必须在指定的范围内。 | - |
| @AssertFalse | 被注释的元素必须为 false | - |
| @AssertTrue | 被注释的元素必须为 true | - |
| 被注释的元素必须是电子邮箱地址 | - | |
| @URL(protocol=,host=,port=,regexp=,flags=) | 被注释的字符串必须是一个有效的 URL | - |
| @Null | 必须为 null | - |
| @DecimalMax(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 | - |
| @DecimalMin(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 | - |
| @Digits(integer, fraction) | 被注释的元素必须是一个数字,其值必须在可接受的范围内 | - |
| @Positive | 判断正数 | - |
| @PositiveOrZero | 判断正数或 0 | - |
| @Negative | 判断负数 | - |
| @NegativeOrZero | 判断负数或 0 | - |
| @Future | 被注释的元素必须是一个将来的日期 | - |
| @FutureOrPresent | 判断日期是否是将来或现在日期 | - |
| @Past | 检查该字段的日期是在过去 | - |
| @PastOrPresent | 判断日期是否是过去或现在日期 | - |
| @SafeHtml | 判断提交的 HTML 是否安全。例如说,不能包含 JavaScript 脚本等等 | - |
easyExcel
| 注解 | 说明 | 支持版本 |
|---|---|---|
| @ExcelProperty | 这是最常用的一个注解,注解中有三个参数value,index,converter分别代表列明,列序号,数据转换方式,value和index只能二选一,通常不用设置converter | - |
| @ColumnWith | 用于设置列宽度的注解,注解中只有一个参数value,value的单位是字符长度,最大可以设置255个字符,因为一个excel单元格最大可以写入的字符个数就是255个字符。 | - |
| @ContentFontStyle | 用于设置单元格内容字体格式的注解 | - |
| @ContentLoopMerge | 用于设置合并单元格的注解 | - |
| @ContentRowHeight | 用于设置行高 | - |
| @ContentStyle | 设置内容格式注解 | - |
| @HeadFontStyle | 用于定制标题字体格式 | - |
| @HeadRowHeight | 设置标题行行高 | - |
| @HeadStyle | 设置标题样式 | - |
| @ExcelIgnore | 不将该字段转换成Excel | - |
| @ExcelIgnoreUnannotated | 没有注解的字段都不转换 | - |
References
https://liaoxuefeng.com/books/java/annotation/index.html
https://learnku.com/docs/springboot-like-laravel/spring-zhu-jie-da-quan/16925
https://sites.google.com/site/javahuide9/brief/lombok-in-10-minutes