[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. 通过 <bean> 元素的 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地址为:

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
  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包,截至目前,最新版的包分别是:

<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.9.Final</version>
</dependency>

第二步,编写我们的实体对象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<ObjectError> 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的规范。大家可以根据需要,了解下新版本的内容。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,192评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,186评论 1 303
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,844评论 0 252
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,471评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,876评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,891评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,068评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,791评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,539评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,772评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,250评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,577评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,244评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,146评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,949评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,995评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,812评论 2 276

推荐阅读更多精彩内容