[spring注解]Spring相关注解(五)

前言

  本篇文章主要简单了解下Spring中一些JSR规范所提供的注解,所谓JSR规范,是Java Specification Requests的缩写,意为Java规范提案,它是JCP,Java标准化组织(Java Community Process)提交给Sun/Oracle的标准化技术规范的请求,目的是为了提供一种规范,并且补充和完善JAVA相关的功能。而目前,JSR规范已经成为了Java界的一个重要标准。JSR所有规范的地址是:https://jcp.org/en/jsr/all

一、JSR-250规范相关
1. Resource注解

  和@Autowired功能类似,@Resource注解也是用来注入bean的,但它并不属于Spring的注解,而是JSR-250规范中提供的注解,位于javax.annotation包下,它和@Autowired不同的一点主要就是,默认情况下它是根据名称(byName)来注入的,而@Autowired是根据类型来注入的(byType)。我们先来看下它的实现:

@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {

从上面我们可以看到,该注解可以用于类,方法与属性上。接下来,我们来看一下它的一些参数:

  1. name,用于绑定JNDI context的对象的名称(不太明白),默认情况下,用于属性上时,该名称是字段名;用于方法上时,名称是与方法相对应的JavaBean的属性名;而用于类上时,没有默认值,必须要指定一个。
@Resource
private DataSource dataSource; // bean的名称就是dataSource

// 同理
private DataSource dataSource;
@Resource
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
  1. lookup,要注入的bean的名称(不太明白);
  2. type,要注入的bean的类型,和name的默认情况类似。用于属性上时,该类型是属性字段的类型;用于方法上时,类型是与方法相对应的javaBean的属性类型;而用于类上时,没有默认值,必须要指定一个;
  3. authenticationType, 枚举类型,不太明白;
  4. shareable,表示该组件和其他组件之间是否可以共享该资源,默认true类型;
  5. mappedName,不太明白;
  6. description,要注入的bean的描述信息。

由于Resource注解不但但用于bean的注入,更多的情况下还可以用于解析JNDI相关的对象,所以我们不用过多关注它的属性,等用到JNDI的时候再来查看文档不晚。

@Resource在通过byName 进行自动注入对象时,如果查询不到,会回退到byType,也就是说如果通过byName查询不到对象,那么就会通过byType来进行注入。

有关@Resource的解释,除了看下Spring官方文档,可以看下Oracle 的JAVA EE 6的官方文档:https://docs.oracle.com/javaee/6/tutorial/doc/bncjk.html

2. PostContruct 和 PreDestroy 注解

  首先,我们知道,Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,而常用的设定方式有以下三种:

1.通过实现 InitializingBean/DisposableBean 接口来定制初始化之后或者销毁之前的操作方法;

  1. 通过 元素的 init-method/destroy-method属性指定初始化之后 或者销毁之前调用的操作方法;
  2. 在指定方法上加上@PostConstruct 或@PreDestroy注解来制定该方法是在初始化之后还是销毁之前调用。

  和@Resource注解一样,这两个注解也不属于Spring本身的注解,也是属于JSR-250规范所提供的注解,而且这两个注解都是用于方法级别的。

这里有关它们的顺序,我们简单说下,感兴趣的童鞋可以自己测试下:

在Spring的bean进行注入的时候,首先会初始化构造方法,然后会执行@PostContruct注解修饰的方法,其次会执行InitializingBean方法,最后,才会执行init-method方法。也就是 Constructor > @PostConstruct > InitializingBean > init-method;而对应的PreDestroy修饰的方法则会在destroy方法执行之后执行;

这里参考自:源码解析:init-method、@PostConstruct、afterPropertiesSet孰先孰后

  1. 这两个注解所修饰的方法一般是无参的非静态方法,
  2. 由于@PostConstruct注解是在对象构造完成后调用init-method之前执行的,所以如果我们配置了延迟初始化的话(default-lazy-init="true"),只有当这个bean被调用了的情况下,@PostConstruct注解所修饰的方法才会执行。

二、JSR-330 规范

1. Inject 注解

  另一个可以实现注入功能的注解是@Inject,该注解也不是Spring注解,而是JSR-330规范中提供的注解,而JSR-330规范则是为了统一Java依赖注入的规范,所以其核心注解@Inject基本上可以完全替换@Autowired注解。该规范一共6个注解,分别对应为:InjectNamedProviderQualifierScopeSingleton。该注解所在jar包对应的maven地址为:


    javax.inject
    javax.inject
    1

  1. @Inject 和 @Autowired一样,也是通过byType来自动注入,都是通过AutowiredAnnotationBeanPostProcessor类来实现的依赖注入。@Inject 注解可以用于方法,属性,构造器上,与@Autowired不同的是,@Inject没有 required属性,因此@Inject注解所依赖的关系必须存在,如果不存在,则会抛出一个异常。
  2. 相对于@Autowired注解使用@Qualifier注解,@Inject使用@Named注解来解决@Autowired的这个问题。而@Named注解与@Qualifier注解又是特别相似,两者的区别仅仅在于语义层面。@Qualifier注解帮助我们缩小所匹配Bean的选择范围(默认使用Bean的ID),而@Named通过Bean的ID来标志可选择的bean。
@Inject
@Named("userService")
private UserService userService;
  1. JSR-330在 javax.inject包里有自己的@Qualifier注解,但JSR-330不建议使用该注解,而是鼓励我们将此注解做为元注解来自定义我们的注解,另外,这两个@Qualifier注解功能基本上是相同的。

JSR-330的这几个注解我们就不仔细说了,等用到了再来细说。对应的官网文档地址:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-standard-annotations

三、JSR-303 相关注解

1. Valid注解

  首先,Valid不是Spring本身提供的注解,而是由JSR-303用于校验传入参数合法性的一个注解。做Java后台开发的都知道,前端参数合法性校验是我们开发过程中必不可少的步骤,但是验证参数本身算是一个体力活,而且还会造成代码冗余,影响代码的美观性,而JSR-303则正是为了解决这个问题而出现的一种比较优雅的解决方式。

JSR-303作为JAVA-EE 6 的一项规范,又被称为Bean Validation,而Hibernate Validator则是官方的参考实现,并且Hibernate Validator还在JSR-303的基础上进行了扩展,以支持更多的校验规则。

  第一步,首先,我们需要引入validation-api jar包和hibernate-validator包,我们可以去Maven仓库下载最新版的jar包,截至目前,最新版的包分别是:



    javax.validation
    validation-api
    2.0.1.Final




    org.hibernate
    hibernate-validator
    6.0.9.Final

第二步,编写我们的实体对象User:

public class User implements Serializable{
    @NotNull(message = "名字不能为空")
    private String name;
    @NotNull(message = "id不能为空")
    private String id;
    @Min(value = 14, message = "数字不能小于14")
    private Double aDouble;
    ......
}

第三步,编写我们的控制器方法,使用@Valid注解来进行校验:

@RequestMapping(value = "/test.html", method = RequestMethod.GET)
public JsonResult findPet(@Valid User user, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        // 错误提示信息
        List errorList = bindingResult.getAllErrors();
        for(ObjectError error : errorList){
            System.out.println(error.getDefaultMessage());
        }
        FieldError fieldError = bindingResult.getFieldError();
        String validMess = fieldError.getDefaultMessage();
        jsonResult = new JsonResult(false, validMess);
        return jsonResult;
    }
}

  一般情况下,我们可以通过借助BindingResult对象来判断错误信息,然后根据获取到的错误信息,抛给全局异常处理,或者把错误信息封装成 JsonResult 对象返回给我们的前端。

  无需过多配置,就可以实现校验的功能,并且省去了许多冗余的代码,让代码看上去更美观些。而如果要查看对应的校验类型的话,我们可以参考JSR303规范来进行查看。而如果Hibernate Validtor满足不了我们的需求的时候,我们还可以自定义校验注解。

这里简单说几点:

  1. 一般情况下,我们都可以通过BindingResult来检查错误信息,如果不适用该对象的话,如果校验不通过,Spring将直接会抛出异常;
  2. JSR-303和Hibernate Validtor都是用于校验对象,而针对单个参数的校验,我们可以使用Spring Validator来实现,Spring Validator是在JSR-303规范基础上进行了扩展,添加了MethodValidationPostProcessor拦截器,可以使用@Validated注解实现对方法参数的校验,并且还可以实现分组校验,而@Valid是不支持分组校验的(比如同一个实现在不同的controller中,而我们想分别对不同Controller中的实体进行校验,这就是分组),后面看下简单的例子。
  3. 这里只列举了JSR-303规范,并且只学习了最基础也是最常用的校验功能,其实关于校验的东西还有很多,比如国际化,错误信息封装,组合参数校验等,这些等我们后续有时间了再仔细研究。

针对方法参数验证,我们看一个简单的实现:

    1. 添加MethodValidationPostProcessor的bean配置:
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
    return new MethodValidationPostProcessor();
}
    1. Controller添加@Validated注解:
@RestController
@RequestMapping("/world")
@Validated
public class WorldController {
}
    1. Controller中方法接着使用JSR-303注解:
@RequestMapping(value = "/test.html", method = RequestMethod.GET)
public String findPet(@NotNull(message = "不能为空") String user) {
}
    1. 可以看到我们的配置已经生效,如果我们的传入参数不满足条件,将会抛出ConstraintViolationException异常,我们可以通过它的getConstraintViolations获得所有没有通过校验的ConstraintViolation集合,然后来得到它们对应的错误信息;也可以使用全局异常来处理:
@ExceptionHandler
@ResponseBody
public JsonResult exceptions(ConstraintViolationException exception) {
    JsonResult jsonResult = new JsonResult();
    jsonResult.setSuccess(false);
    jsonResult.setMessage(exception.getMessage());
    return jsonResult;
}
  1. JSR-303不但可以用于后端接收前端参数的校验,也可以用于dubbo接口参数的校验;
  2. 这里大致说下相关版本对应的规范,Bean Validation 1.0对应 JSR-303,属于JAVA EE 6的规范;Bean Validation 1.1 对应 JSR-349,属于JAVA EE 7的规范;Bean Validation 2.0对应JSR-380,属于JAVA EE 8的规范。大家可以根据需要,了解下新版本的内容。

你可能感兴趣的:([spring注解]Spring相关注解(五))