Java 注解

前言

  • Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。

  • 注解就是代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,从而做相对应的处理。

  • 注解跟注释很像,区别是注释是给人看的,而注解是给程序看的,它可以被编译器读取。

  • Java 注解(Annotation)

  • 注解 快速入门

1、注解

  • Java 语言中的类、方法、变量、参数和包等都可以被标注。

  • 和 Javadoc 不同,Java 注解可以通过反射获取标注的内容。

  • Java 虚拟机可以保留注解内容,在编译器生成类文件时,注解可以被嵌入到字节码中,在运行时可以获取到标注的内容。

  • 当然它也支持自定义 Java 注解。

  • 注解大多时候与反射或者 AOP 切面结合使用,它的作用有很多,比如标记和检查,最重要的一点就是简化代码,降低耦合性,提高执行效率。

  • 注解分类

    • 按照注解的作用域(@Retention)分

      • RetentionPolicy.SOURCE: Java 源文件上的注解
      • RetentionPolicy.CLASS: Class 类文件上的注解
      • RetentionPolicy.RUNTIME: 运行时的注解
    • 按照注解的来源分

      • 内置注解,如 @Override@Deprecated 等等
      • 第三方注解,如 Hibernate, Struts, Spring 等等
      • 自定义注解,如仿 Hibernate 的自定义注解

2、内置注解

  • Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。

  • 从 Java 7 开始,额外添加了 3 个注解。

关键字 介绍
作用在代码的注解
@Override 用在方法上,表示这个方法重写了父类的方法。如果父类没有这个方法,那么就无法编译通过。
@Deprecated 表示这个方法已经过期,不建议开发者使用。暗示在将来某个不确定的版本,就有可能会取消掉。
@SuppressWarnings 指示编译器忽略注解中声明的警告。
常见的值,分别对应如下意思:
       1. deprecation:使用了不赞成使用的类或方法时的警告(使用 @Deprecated 使得编译器产生的警告);
       2. unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型;
           关闭编译器警告;
       3. fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
       4. path:在类路径、源文件路径等中有不存在的路径时的警告;
       5. serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;
       6. finally:任何 finally 子句不能正常完成时的警告;
       7. rawtypes:泛型类型未指明;
       8. unused:引用定义了,但是没有被使用;
       9. all:关于以上所有情况的警告。
作用在其他注解的注解,或者说 元注解
@Retention 标识这个注解怎么保存,是只在代码中,还是编入 class 文件中,或者是在运行时可以通过反射访问。
@Documented 标记这些注解是否包含在用户文档中。
@Target 标记这个注解应该是哪种 Java 成员。
@Inherited 标记这个注解是继承于哪个注解类 (默认 注解并没有继承于任何子类)
从 Java 7 开始,额外添加的 3 个注解
@SafeVarargs 当使用可变数量的参数的时候,而参数的类型又是泛型 T 的话,就会出现警告。这个时候使用来去掉这个警告。
@SafeVarargs 注解只能用在参数长度可变的方法或构造方法上,且方法必须声明为 static 或 final,否则会出现编译错误。
一个方法使用 @SafeVarargs 注解的前提是,必须确保这个方法的实现中对泛型类型参数的处理不会引发类型安全问题。
@FunctionalInterface 用于约定函数式接口
如果接口中只有一个抽象方法(可以包含多个默认方法或多个 static 方法),该接口称为函数式接口。
函数式接口其存在的意义,主要是配合 Lambda 表达式 来使用。
@Repeatable 元注解 Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
  • 实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    @Override                                               // 重写父类方法
    public String toString() {
    return name;
    }

    @Deprecated // 方法已经过期
    public void hackMap() {

    }

    @SuppressWarnings({ "rawtypes", "unused" }) // 忽略 引用定义了,但是没有被使用 警告信息
    public static void main(String[] args) {
    List heros = new ArrayList();
    }

    @SafeVarargs // 使用可变数量,而参数的类型又是泛型 T
    public static <T> T getFirstOne(T... elements) {
    return elements.length > 0 ? elements[0] : null;
    }

    @FunctionalInterface // 约定函数式接口
    public interface AD {
    public void adAttack();
    }

3、元注解

  • 元数据 metadata 是为其他数据提供信息的数据。

  • 元注解 meta annotation 是用于标注 “注解” 的注解。

关键字 介绍 示例代码
@Target 表示这个注解能放在什么位置上,是只能放在类上?还是即可以放在方法上,又可以放在属性上。
可以选择的位置列表如下:
           ElementType.TYPE:能修饰类、接口或枚举类型
           ElementType.FIELD:能修饰成员变量
           ElementType.METHOD:能修饰方法
           ElementType.PARAMETER:能修饰参数
           ElementType.CONSTRUCTOR:能修饰构造器
           ElementType.LOCAL_VARIABLE:能修饰局部变量
           ElementType.ANNOTATION_TYPE:能修饰注解
           ElementType.PACKAGE:能修饰包
@Target({METHOD, TYPE})
@Retention 表示生命周期。
可选的值有 3 个:
           RetentionPolicy.SOURCE:注解只在源代码中存在,编译成 class 之后,就没了。@Override 就是这种注解。
           RetentionPolicy.CLASS: 注解在 java 文件编程成 .class 文件后,依然存在,但是运行起来后就没了。
                               @Retention 的默认值,即当没有显式指定 @Retention 的时候,就会是这种类型。
           RetentionPolicy.RUNTIME:注解在运行起来之后依然存在,程序可以通过反射获取这些信息,自定义注解
                               @JDBCConfig 就是这样。
@Retention(RetentionPolicy.RUNTIME)
@Inherited 表示这个注解可以被子类继承。 @Inherited
@Documented 表示当执行 javadoc 命令生成 API 文档的时候,本注解会生成相关文档。 @Documented
@Repeatable 当没有 @Repeatable 修饰的时候,注解在同一个位置,只能出现一次。
使用 @Repeatable 之后,再配合一些其他动作,就可以在同一个地方使用多次了。
java1.8 新增。
@Repeatable(FileTypes.class)
public @interface FileType { };

4、自定义注解

  • Java 注解可以通过反射获取标注的内容。

  • 创建注解类型的时候即不使用 class 也不使用 interface,而是使用 @interface

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import java.lang.annotation.*;
    import static java.lang.annotation.ElementType.METHOD;
    import static java.lang.annotation.ElementType.TYPE;

    @Target({METHOD, TYPE}) // 表示这个注解可以用用在类/接口上,还可以用在方法上
    @Retention(RetentionPolicy.RUNTIME) // 表示这是一个运行时注解,即运行起来之后,才获取注解中的相关信息
    @Inherited // 表示这个注解可以被子类继承
    @Documented // 表示当执行 javadoc 的时候,本注解会生成相关文档
    public @interface JDBCConfig { // 定义注解 @interface

    String ip(); // 注解元素,这些注解元素就用于存放注解信息,在解析的时候获取出来
    int port() default 3306; // 设置默认值,若所有注解元素都设置了默认值,使用注解时可以不用写参数,即直接使用 @JDBCConfig
    String database();
    String encoding() default "UTF-8";
    String loginName();
    String password();
    }
  • 若所有注解元素都设置了默认值,使用注解时可以不用写参数,即直接使用 @JDBCConfig,就可以直接在被注解的类中使用注解中定义的注解元素的值。

  • 使用自定义的注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @JDBCConfig(ip = "127.0.0.1",                           // 使用自定义的注解
    database = "test",
    loginName = "root",
    password = "admin") // 若所有注解元素都设置了默认值,使用注解时可以不用写参数,即直接使用 @JDBCConfig
    public class DBUtil {

    static {
    try {
    Class.forName("com.mysql.jdbc.Driver");
    } catch (ClassNotFoundException e) {
    e.printStackTrace();
    }
    }
    }
  • 解析自定义的注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;

    import anno.JDBCConfig;

    // 若所有注解元素都设置了默认值,使用注解时可以不用写参数,即直接使用 @JDBCConfig,就可以直接在被注解的类中使用注解中定义的注解元素的值
    @JDBCConfig(ip = "127.0.0.1", database = "test", loginName = "root", password = "admin")
    public class DBUtil {

    static {
    try {
    Class.forName("com.mysql.jdbc.Driver");
    } catch (ClassNotFoundException e) {
    e.printStackTrace();
    }
    }

    public static Connection getConnection() throws SQLException, NoSuchMethodException, SecurityException {

    // 通过反射,获取这个 DBUtil 这个类上的注解对象,拿到注解对象之后,通过其方法,获取各个注解元素的值
    JDBCConfig config = DBUtil.class.getAnnotation(JDBCConfig.class);

    String ip = config.ip();
    int port = config.port();
    String database = config.database();
    String encoding = config.encoding();
    String loginName = config.loginName();
    String password = config.password();

    String url = String.format("jdbc:mysql://%s:%d/%s?characterEncoding=%s", ip, port, database, encoding);
    return DriverManager.getConnection(url, loginName, password);
    }
    }
  • 自定义仿 Hibernate 注解

5、常用注解

5.1 Application 相关

关键字 介绍 示例代码
@SpringBootApplication 一般放在项目的一个启动类上,用来标注项目入口,以及完成一些基本的自动自动配置。 @SpringBootApplication
public class Application { }
@EnableAsync 开启多线程(开启 @Async 的功能)。
SpringBoot 可以放在 Application 上,也可以放其他配置文件上。
在 Spring Boot 中,@EnableXXX 注解的功能通常是开启某个功能。@EnableXXX 注解通常会和 @Import 注解配合使用,通过 @Import 导入配置类,根据配置类自动装配一些B ean,来达到开启某些功能的目的。
@EnableAsync
@SpringBootApplication
public class Application { }
@EnableScheduling 开启定时任务(开启 @Scheduled 的功能) @EnableScheduling
@SpringBootApplication
public class Application { }
@MapperScan 指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类。
使用 @MapperScan 注解多个包时,使用 @MapperScan({“com.kfit.demo”,”com.kfit.user”})
@MapperScan("com.qianchia.dao")
@SpringBootApplication
public class Application { }
  • 可以把 @SpringBootApplication 看作是 @Configuration、@EnableAutoConfiguration、@ComponentScan 注解的集合。
关键字 介绍 示例代码
@Configuration 允许在 Spring 上下文中注册额外的 bean 或导入其他配置类,@Configuration 类允许通过调用同一类中的其他 @Bean 方法来定义 bean 之间的依赖关系。
一般用来声明配置类,可以使用@Component注解替代,不过使用@Configuration注解声明配置类更加语义化。标注在类上,相当于把该类作为 spring 的 xml 配置文件中的,作用是用于配置 spring 容器 (应用上下文)。
@Configuration
public class FilterConfig { }
@EnableAutoConfiguration 启用 SpringBoot 的自动配置机制。
@ComponentScan 扫描被 @Component(@Service,@Controller) 注解的 bean,注解默认会扫描该类所在的包下所有的类。
  • 如果想要每个接口都要变成实现类,那么需要在每个接口类上加上 @Mapper 注解,比较麻烦,解决这个问题用 @MapperScan。
关键字 介绍 示例代码
@Mapper 在接口类上添加了 @Mapper,在编译之后会生成相应的接口实现类。
@Async 标记异步方法。需配置 @EnableAsync,开启多线程。@Async 标记的方法只能是 void 或者 Future 返回值,它必须仅适用于 public 方法,在同一个类中调用异步方法将无法正常工作。 @Async
public void sendTenantCheckInMessage(Leasing leasing, String from) { }
@Scheduled 定时任务。需配置 @EnableScheduling,开启定时任务。 @Scheduled(cron = "0 0 1 * * *")
public void cleanUpExpiredData() { }
@Override 用在方法上,表示这个方法重写了父类的方法。如果父类没有这个方法,那么就无法编译通过。

5.2 Spring Bean 相关

  • Bean 是符合一定规范编写的 Java 类,不是一种技术,而是一种规范。
关键字 介绍 示例代码
@Bean Spring 的 @Bean 注解用于告诉方法,产生一个 Bean 对象,然后这个 Bean 对象交给 Spring 管理。产生这个 Bean 对象的方法 Spring 只会调用一次,随后这个S pring 将会将这个 Bean 对象放在自己的 IOC 容器中。SpringIOC 容器管理一个或者多个 bean,这些 bean 都需要在 @Configuration 注解下进行创建,在一个方法上使用 @Bean 注解就表明这个方法需要交给 Spring 进行管理。
@Autowired 自动导入对象到类中,自动装配,其作用是为了消除代码里的 getter/setter 与 bean 属性中的 property。 @Autowired
private ContactService contactService;
@Resource @Resource 和 @Autowired 注解都是用来实现依赖注入的。只是 @ Autowired 按 byType 自动注入,而 @Resource 默认按 byName 自动注入。@Resource 有两个重要属性,分别是 name 和 type。既不指定 name 属性,也不指定 type 属性,则自动按 byName 方式进行查找。如果没有找到符合的 bean,则回退为一个原始类型进行查找,如果找到就注入。 @Resource
private MailService mailService;
@Lazy 懒加载。默认情况下,Spring 会在应用程序上下文的启动时创建所有单例 bean。这背后的原因很简单:立即避免和检测所有可能的错误,而不是在运行时。但是,有些情况下我们需要创建一个 bean,而不是在应用程序上下文启动时,而是在我们请求时。@Configuration 和 @lazy 一起使用时,意味着所有使用 @Bean 的方法都是懒加载 @Resource
@Lazy
private AccountingService accountingService;
  • 一般使用 @Autowired 注解让 Spring 容器帮我们自动装配 bean。要想把类标识成可用于 @Autowired 注解自动装配的 bean 的类,可以采用一下注解实现。
关键字 介绍 示例代码
@Component 通用的注解,可标注任意类为 Spring 组件。可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用 @Component 注解标注。 @Component
public interface TenantDao { }
@Repository 是基于 @Component 注解的扩展,被 @Repository 注解的 POJO 类对应持久层即 Dao 层,主要用于数据库相关操作。
@Service 是基于 @Component 注解的扩展,被 @Service 注解的 POJO 类对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。 @Service
public class ContactServiceImpl implements ContactService { }
@Controller 表明了这个类是一个控制器类。是基于 @Component 注解的扩展,被 @Controller 注解的类对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。
@RestController 是 @Controller 和 @ResponseBody 的合集。表示这是个控制器 bean,并且是将函数的返回值直 接填入 HTTP 响应体中,是 REST 风格的控制器。 @RestController
public class ContactController { }
@Scope 用在类上,声明 Spring Bean 的作用域。参数:singleton: 唯一 bean 实例,Spring 中的 bean 默认都是单例的。prototype: 每次请求都会创建一个新的 bean 实例。request: 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。session: 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。 @Scope("singleton")

5.3 HTTP 请求

关键字 介绍 示例代码
@GetMapping 请求从服务器获取特定资源。等价于 @RequestMapping(value=”/tenants”, method=RequestMethod.GET) @GetMapping("/tenants")
@GetMapping("/tenant/{id:\\d+}")
@PostMapping 在服务器上创建一个新的资源。等价于 @RequestMapping(value=”/tenant”, method=RequestMethod.POST) @PostMapping("/tenant")
@PutMapping 更新服务器上的资源 (客户端提供更新后的整个资源)。等价于 @RequestMapping(value=”/tenant/{id:\d+}”, method=RequestMethod.PUT) @PutMapping("/tenant/{id:\\d+}")
@DeleteMapping 从服务器删除特定的资源。等价于 @RequestMapping(value=”/tenant/{id:\d+}”, method=RequestMethod.DELETE) @DeleteMapping("/tenant/{id:\\d+}")
@PatchMapping 更新服务器上的资源 (客户端提供更改的属性,可以看做作是部分更新)。一般实际项目中,我们都是 PUT 不够用才使用 PATCH 请求去更新数据。
关键字 介绍 示例代码
@RequestMapping 是用来映射请求的,即指明处理器可以处理哪些 URL 请求。
该注解既可以用在类上,也可以用在方法上。
当使用 @RequestMapping 标记控制器类时,方法的请求地址是相对类的请求地址而言的;当没有使用 @RequestMapping 标记类时,方法的请求地址是绝对路径。
@RequestMapping("/api")
public class ContactController { }

@RequestMapping(value = "/api/image/upload", method = RequestMethod.POST)
@ResponseBody 绑定 response body 数据。表示方法的返回值直接以指定的格式写入 Http response body 中,而不是解析为跳转路径。格式的转换是通过 HttpMessageConverter 中的方法实现的,因为它是一个接口,因此由其实现类完成转换。如果要求方法返回的是 json 格式数据,而不是跳转页面,可以直接在类上标注 @RestController,而不用在每个方法中标注 @ResponseBody,简化了开发过程。 @ResponseBody
public Response imageUpload(HttpServletRequest request) { }
@ResponseStatus 是 spring-web 包中提供的一个注解,其主要作用就是为了改变HTTP响应的状态码,具有 value、code、reason 三个属性 @ResponseStatus(HttpStatus.NOT_FOUND)
Response handleUnknownException() { }
关键字 介绍 示例代码
@PostConstruct 被 @PostConstruct 修饰的方法会在服务器加载 Servlet 的时候运行,并且只会被服务器调用一次,类似于 Servlet的inti() 方法。被 @PostConstruct 修饰的方法会在构造函数之后,init() 方法之前运行。 @PostConstruct
public void loadCountries() { }
@PreDestroy 被 @PreDestroy 修饰的方法会在服务器卸载 Servlet 的时候运行,并且只会被服务器调用一次,类似于 Servlet的destroy() 方法。被 @PreDestroy 修饰的方法会在 destroy() 方法之后运行,在 Servlet 被彻底卸载之前。

5.4 前后端传值

关键字 介绍 示例代码
@ModelAttribute 可被应用在方法或方法参数上。

在方法上的 @ModelAttribute 说明方法是用于添加一个或多个属性到 model 上。这样的方法能接受与 @RequestMapping 标注相同的参数类型,只不过不能直接被映射到具体的请求上。在同一个控制器中,标注了 @ModelAttribute 的方法实际上会在 @RequestMapping 方法之前被调用。

标注在方法参数上的 @ModelAttribute 说明了该方法参数的值将由 model 中取得。如果 model 中找不到,那么该参数会先被实例化,然后被添加到 model 中。在 model 中存在以后,请求中所有名称匹配的参数都会填充到该参数中。这在 Spring MVC 中被称为数据绑定,一个非常有用的特性,我们不用每次都手动从表格数据中转换这些字段数据。
@ModelAttribute PageRequest pageRequest
@RequestBody 绑定 request body 数据,用于读取 Request 请求 (可能是 POST, PUT, DELETE, GET 请求) 的 body 部分并且 Content-Type 为 application/json 格式的数据,接收到数据之后会自动将数据绑定到 Java 对象上去。系统会使用 HttpMessageConverter 或者自定义的 HttpMessageConverter 将请求的 body 中的 json 字符串转换为 java 对象。 @RequestBody(required = false) TenantQueryRequest request
@RequestParam 用于获取查询参数 @RequestParam(name = "status", required = false) Integer status
@PathVariable 用于获取路径参数 @GetMapping("/tenant/{id:\\d+}")
@PathVariable("id") Long id
  • 一个请求方法只可以有一个 @RequestBody,但是可以有多个 @RequestParam 和 @PathVariable。如果你的方法必须要用两个 @RequestBody 来接受数据的话,大概率是你的数据库设计或者系统设计出问题了!

5.5 参数校验

  • 请求参数验证
关键字 介绍 示例代码
@Validated 这个参数可以告诉 Spring 去校验方法参数。可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上。 @Validated @RequestBody NewTenantRequest request
@Valid 在需要验证的参数上加上了 @Valid 注解,如果验证失败,它将抛出 MethodArgumentNotValidException。由 javax 提供。可以用在方法、构造函数、方法参数和成员属性(字段)上。 @RequestBody @Valid Person person
  • 在检验入参是否符合规范时,使用 @Validated 或者 @Valid 在基本验证功能上没有太多区别。但是在分组、注解地方、嵌套验证等功能上两个有所不同。

  • 字段验证

关键字 介绍 示例代码
@NotEmpty 被注释的字符串的不能为 null 也不能为空
@NotBlank 被注释的字符串非 null,并且必须包含一个非空白字符
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Pattern 被注释的元素必须符合指定的正则表达式 @Pattern(regexp = "((^Man$|^Woman$|^UGM$))", message = "sex 值不在可选范围")
@Email 被注释的元素必须是 Email 格式。
@Min 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 @Min(value = 0)
@Max 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 @Max(value = 5)
@DecimalMin 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 @DecimalMin(value)
@DecimalMax 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 @DecimalMax(value)
@Size 被注释的元素的大小必须在指定的范围内 @Size(max=, min=)
@Digits 被注释的元素必须是一个数字,其值必须在可接受的范围内 @Digits(integer, fraction)
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期

5.6 读取配置信息

关键字 介绍 示例代码
@value 读取比较简单的配置信息 @Value("${storage.path}")
private String storagePath;
@ConfigurationProperties 读取配置信息并与 bean 绑定 @Configuration
@ConfigurationProperties(prefix = "exchange")
public class ExchangeRateProperties { }

5.7 事务回滚

关键字 介绍 示例代码
@Transactional 声明式事务管理建立在 AOP 之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。简而言之,@Transactional 注解在代码执行出错的时候能够进行事务回滚 @Transactional
@Override
public void createTenant(long orgId, NewTenantRequest request) { }

@Transactional(rollbackFor = Exception.class)
  • 我们知道 Exception 分为运行时异常 RuntimeException 和非运行时异常。在 @Transactional 注解中如果不配置 rollbackFor 属性,那么事物只会在遇到 RuntimeException 的时候才会回滚,加上 rollbackFor=Exception.class,可以让事物在遇到非运行时异常时也回滚。

  • @Transactional 注解一般用在类或者方法、接口上。

    • 作用于类:当把 @Transactional 注解放在类上时,表示所有该类的 public 方法都配置相同的事务属性信息。
    • 作用于方法:当类配置了 @Transactional,方法也配置了 @Transactional,方法的事务会覆盖类的事务配置信息。
    • 作用于接口:不推荐这种使用方法,因为一旦标注在 Interface 上并且配置了 Spring AOP 使用 CGLib 动态代理,将会导致 @Transactional 注解失效。

5.8 异常处理

关键字 介绍 示例代码
@ControllerAdvice 本质上是一个 Component,为那些声明了(@ExceptionHandler、@InitBinder 或 @ModelAttribute 注解修饰的)方法的类而提供的专业化的 @Component , 以供多个 Controller 类所共享。可以被扫描到。统一处理异常。 @ControllerAdvice
public class GlobalExceptionHandler { }
@ExceptionHandler 用在方法上面表示遇到这个异常就执行以下方法。用来统一处理方法抛出的异常。当使用这个 @ExceptionHandler 注解时,需要定义一个异常的处理方法,给这个方法加上 @ExceptionHandler 注解,这个方法就会处理类中其他方法(被 @RequestMapping 注解)抛出的异常。 @ExceptionHandler({Exception.class})
public Response defaultErrorHandler(HttpServletResponse response, Exception e) { }

5.9 Json 数据处理

关键字 介绍 示例代码
@JsonIgnoreProperties 过滤 Json 数据。作用在类上用于过滤掉特定字段不返回或者不解析。 // 生成 json 时将 userRoles 属性过滤
@JsonIgnoreProperties({"userRoles"})
@JsonIgnore 过滤 Json 数据。一般用于类的属性上,作用和上面的 @JsonIgnoreProperties 一样。 @JsonIgnore
private List userRoles = new ArrayList<>();
@JsonFormat 一般用来格式化 json 数据。 @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone="GMT")
private Date date;

@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long id;

5.10 JPA 相关

  • JPA 实体管理器(EntityManager)用于管理系统中的实体,它是实体与数据库之间的桥梁。
关键字 介绍 示例代码
@Entity 声明一个类对应一个数据库实体。
@Table 设置表名
@Id 声明一个字段为主键
@Column 声明字段。
@Transient 声明不需要与数据库映射的字段,在保存的时候不需要保存进数据库。
@Enumerated 创建枚举类型的字段。
  • 创建表

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Entity
    @Table(name = "role")
    public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String description;

    省略 getter/setter......
    }
  • 设置属性 userName 对应的数据库字段名为 user_name,长度为 32,非空。

    1
    2
    @Column(name = "user_name", nullable = false, length=32)
    private String userName;
  • 设置字段类型并且加默认值,这个还是挺常用的。

    1
    2
    @Column(columnDefinition = "tinyint(1) default 1")
    private Boolean enabled;
  • 指定不持久化的特定字段

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Entity(name="USER")
    public class User {

    ......

    // not persistent because of @Transient}
    @Transient
    private String secrect;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    // not persistent because of staticfinal 
    static String secrect;

    // not persistent because of final
    String secrect = "Satish";

    // not persistent because of transient
    transient String secrect;
  • 可以使用枚举类型的字段,不过枚举字段要用 @Enumerated 注解修饰。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public enum Gender {

    MALE("男性"), FEMALE("女性");

    private String value;

    Gender(String str) {
    value = str;
    }}
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @Entity
    @Table(name = "role")
    public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String description;

    @Enumerated(EnumType.STRING)
    private Gender gender;

    省略 getter/setter ......
    }

5.11 SQL 相关

关键字 介绍 示例代码
@Param 入参。这个注解是为 SQL 语句中参数赋值而服务的。作用就是给参数命名。比如在 mapper 里面某方法 A(int id),当添加注解后 A(@Param("userId") int id),也就是说外部想要取出传入的 id 值,只需要取它的参数名 userId 就可以了。将参数值传入 SQL 语句中,通过 #{userId} 进行取值给 SQL 的参数赋值。 Page<Tenant> listByOrgId(@Param("orgId") long orgId, @Param("category") Integer category, @Param("request")TenantQueryRequest request);
@Select 查询 sql, 和 xml select sql 语法完全一样。
@Insert 插入 sql, 和 xml insert sql 语法完全一样。
@Update 更新 sql, 和 xml update sql 语法完全一样。
@Delete 删除 sql, 和 xml delete sql 语法完全一样。
@Results 结果集合。
@Result 结果。

5.12 AOP 相关

  • AOP 意为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
关键字 介绍 示例代码
@Aspect 作用是把当前类标识为一个切面供容器读取 @Aspect
@Component
public class AuditLogAspect { }
@Pointcut Pointcut 是植入 Advice 的触发条件。
@Around 环绕增强,相当于 MethodInterceptor
@Before 标识一个前置增强方法,相当于 BeforeAdvice 的功能
@After final 增强,不管是抛出异常或者正常退出都会执行
@AfterReturning 后置增强,相当于 AfterReturningAdvice,方法正常退出时执行
@AfterThrowing 异常抛出增强,相当于 ThrowsAdvice
  • 每个 Pointcut 的定义包括 2 部分,一是表达式,二是方法签名。

  • 方法签名必须是 public 及 void 型。可以将 Pointcut 中的方法看作是一个被 Advice 引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此 Pointcut 中的方法只需要方法签名,而不需要在方法体内编写实际代码。

5.13 Swagger 相关

  • Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
关键字 介绍 示例代码
@Api 使用在类上,表明是 swagger 资源。
生成的 api 文档会根据 tags 分类,就是这个 controller 中的所有接口生成的接口文档都会在 tags 这个 list 下。tags 如果有多个值,会生成多个 list,每个 list 都显示所有接口。
value 的作用类似 tags,但是不能有多个值。
@Api(value="ContactController", tags={"Contact Api"})
public class ContactController { }
@ApiOperation 使用于在方法上,表示一个 http 请求的操作。value 用于方法描述,notes 用于提示内容,tags 可以重新分组(视情况而用) @ApiOperation(value = "Retrieve Tenant List")
public Response<PageResponse<Tenant>> listTenant( ) { }
@ApiModel 使用在类上,表示对类进行说明,用于参数用实体类接收。value 表示对象名,description 描述
@ApiModelProperty 使用在方法,字段上,表示对 model 属性的说明或者数据操作更改。value 字段说明,name 重写属性名字,dataType 重写属性类型,required 是否必填,example 举例说明,hidden 隐藏
@ApiParam 使用在方法上或者参数上,字段说明;表示对参数的添加元数据(说明或是否必填等)。name 参数名,value 参数说明,required 是否必填

5.14 Lombok 相关

  • 使用 Lombok 不用再手动写 setter, getter, toString, hashCode, 构造方法了。
关键字 介绍 示例代码
@Data 为类的所有属性自动生成 setter/getter、equals、canEqual、hashCode、toString 方法,如为 final 属性,则不会为该属性生成 setter 方法。 @Data
public class Hero { }
@AllArgsConstructor
@NoArgsConstructor
分别提供全参构造方法和无参构造方法。
@Builder 实例化和设置属性值的风格。

6、代码耦合性

  • 耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。

  • 耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。

  • 模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差(降低耦合性,可以提高其独立性)。

  • 软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。

文章目录
  1. 1. 前言
  2. 2. 1、注解
  3. 3. 2、内置注解
  4. 4. 3、元注解
  5. 5. 4、自定义注解
  6. 6. 5、常用注解
    1. 6.1. 5.1 Application 相关
    2. 6.2. 5.2 Spring Bean 相关
    3. 6.3. 5.3 HTTP 请求
    4. 6.4. 5.4 前后端传值
    5. 6.5. 5.5 参数校验
    6. 6.6. 5.6 读取配置信息
    7. 6.7. 5.7 事务回滚
    8. 6.8. 5.8 异常处理
    9. 6.9. 5.9 Json 数据处理
    10. 6.10. 5.10 JPA 相关
    11. 6.11. 5.11 SQL 相关
    12. 6.12. 5.12 AOP 相关
    13. 6.13. 5.13 Swagger 相关
    14. 6.14. 5.14 Lombok 相关
  7. 7. 6、代码耦合性
隐藏目录