Spring源码系列——注解详解

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> Spring源码系列——注解详解

因为要看Spring中注解的具体定义,所以在说之前,先来简单说下JAVA中注解的一些基本知识。

元注解

什么是元注解呢,就是注解的注解。java中提供了以下几种:

  • @Target 注解的作用域描述
    public enum ElementType {
        /** 类, 接口 或者枚举 */
        TYPE,
        /** 字段 */
        FIELD,
        /** 方法 */
        METHOD,
        /** 参数 */
        PARAMETER,
        /** 构造方法 */
        CONSTRUCTOR,
        /** 局部变量 */
        LOCAL_VARIABLE,
        /** 注解类型 */
        ANNOTATION_TYPE,
        /** 包 */
        PACKAGE
    }
    
  • @Retention 生命周期描述
    public enum RetentionPolicy {
        /**
         * 在原文件中有效,被编译器丢弃。 
         */
        SOURCE,
        /**
         * 在class文件有效,可能会被虚拟机忽略。 
         */
        CLASS,
        /**
         * 在运行时有效。
         */
        RUNTIME
    }
    
  • @Inherited 标识性的元注解,它允许子注解继承它。
  • @Documented 用于标准生成javadoc时会包含的注解。
  • 注解的作用域描述

    生命周期描述

    
    public enum RetentionPolicy {
        /**
         * 在原文件中有效,被编译器丢弃。 
         */
        SOURCE,
        /**
         * 在class文件有效,可能会被虚拟机忽略。 
         */
        CLASS,
        /**
         * 在运行时有效。
         */
        RUNTIME
    }
    

    标识性的元注解,它允许子注解继承它。

    用于标准生成javadoc时会包含的注解。

    JAVA中注解的定义方式

    
    public @interface 注解名 {定义体}
    

    上面试一些基本概念点,关注注解其他的一些特性和用法就不细说了。直接看Spring中的注解吧。

    1、@Component

    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Component {
    
        /**
         * The value may indicate a suggestion for a logical component name,
         * to be turned into a Spring bean in case of an autodetected component.
         * @return the suggested component name, if any
         */
        String value() default "";
    
    }
    

    指示注释类是“组件”。 当使用基于注释的配置和类路径扫描时,这些类被认为是自动检测的候选对象。

    2、@Controller

    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Controller {
        String value() default "";
    }
    

    使用过Spring mvc的小伙伴对于这个注解肯定不陌生。@Controller表示注释的类是“控制器”(例如Web控制器)。这个注解作为@Component的一个特定方式存在,允许通过类路径扫描来自动检测实现类。通常情况下会结合RequestMapping注解使用。从它的定义层面来看,这个注解只能用于接口或者类上面,不能用于方法或者属性字段上面。

    3、@Service

    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Service {
        String value() default "";
    }
    

    表示注释类是一个“服务”,最初由Domain-Driven Design (Evans,2003)定义为“作为模型中独立的接口提供的操作,没有封装状态”。

    在一般情况下,我们把他用在标准我们的service服务接口的实现类上面,实际上这相当于缩小它们的语义和适当的使用。

    @Service这个注释作为 @Component的一个特例,允许通过类路径扫描来自动检测实现类。

    4、@Repository

    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Repository {
        /**
         * The value may indicate a suggestion for a logical component name,
         * to be turned into a Spring bean in case of an autodetected component.
         * @return the suggested component name, if any
         */
        String value() default "";
    }
    

    用于标注数据访问组件,即DAO组件

    5、@RequestMapping

    
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Mapping
    public @interface RequestMapping {
        String name() default "";
    
        @AliasFor("path")
        String[] value() default {};
    
        @AliasFor("value")
        String[] path() default {};
    
        RequestMethod[] method() default {};
    
        String[] params() default {};
    
        String[] headers() default {};
    
        String[] consumes() default {};
    
        String[] produces() default {};
    
    }
    

    @RequestMapping是一个用来处理地址映射请求的注解,从定义可以看出,可作用于方法或者类上。

  • 用于类上,大多数是为了进行区分controller
  • 用于方法上则是对方法进行注解以产生访问的路径。
  • 它包括了几个属性:

  • value 用于设置方法或者类的映射路径,可以直接写路径。我们通常都是直接写,例如:@RequestMapping("/XXX");
  • method 用于指定请求的方法,可以设置单个或多个,如果请求方法不满足条件则会请求失败。
  • params  指定request中必须包含某些参数值是,才让该方法处理。
  • name 此映射指定一个名称
  • path 仅在Servlet环境中:路径映射URI(例如“/myPath.do”)。也支持Ant风格的路径模式(例如“/myPath/*.do”)。在方法级别,在类型级别表示的主映射内支持相对路径(例如“edit.do”)。 路径映射URI可能包含占位符(例如“/ $ {connect}”)
  • consumes 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
  • produces 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
  • headers 指定request中必须包含某些指定的header值,才能让该方法处理请求。
  • 其他的几个没怎么用过,确实不了解,有知道的小伙伴,欢迎留言。

    6、@ResponseBody

    @ResponseBody这个我一般是用在做异步请求调用的方法上来使用的。因为在使用@RequestMapping后,返回值通常解析为跳转路径。加上@responsebody后,返回结果直接写入HTTP response body中,不会被解析为跳转路径。

    对于异步请求,我们不希望返回解析视图,二是希望响应的结果是json数据,那么加上@responsebody后,就会直接返回json数据。

    7、@Autowired

    Autowired就是自动装配的意思,其作用是为了消除代码Java代码里面的getter/setter与bean属性中的property。当然,getter看个人需求,如果私有属性需要对外提供的话,就应该保留。

    @Autowired默认按类型匹配的方式,在容器查找匹配的Bean,当有且仅有一个匹配的Bean时,Spring将其注入@Autowired标注的变量中。

    但是当接口存在两个实现类的时候必须使用@Qualifier指定注入哪个实现类,否则可以省略,只写@Autowired。

    8、@Qualifier

    @Qualifier用于指定注入Bean的名称,就是上面说到的,如果容器中有一个以上匹配的Bean,则可以通过@Qualifier注解限定Bean的名称。

    9、@Resource

    这个注解不是Spring的,放在这里是为了和@Autowired做一个区别。
    @Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。

    10、@PathVariable

    当使用@RequestMapping URI template 样式映射时, 即 someUrl/{paramId}, 这时的paramId可通过 @Pathvariable注解绑定它传过来的值到方法的参数上。

    
    @RequestMapping("/user/{userId}")
    public ModelAndView userCenter(HttpServletRequest request,
                                HttpServletResponse response,
                                @PathVariable String userId){
        //do something 
    }
    

    如果方法参数名称和需要绑定的uri template中变量名称不一致,需要在@PathVariable(“name”)指定uri template中的名称。

    11、@RequestParam

    @RequestParam注解有两个属性: value、required;

  • value用来指定要传入值的id名称
  • required用来指示参数是否必须绑定;
  • 举个例子:

    
    @RequestMapping("/t_rparam1")  
    public String t_rparam1(@RequestParam  Long userId) {  
        //do something   
    }  
    
    @RequestMapping("/t_rparam2")  
    public String t_rparam2(Long userId) {  
        //do something   
    }
    
  • t_rparam1 必须带有参数,也就是说你直接输入localhost:8080/t_rparam1 会报错只能输入localhost:8080/t_rparam1?userId=? 才能执行相应的方法
  • t_rparam2  可带参数也可不带参数;也就是说输入localhost:8080/t_rparam2和输入 localhost:8080/t_rparam2?userId=?都可以正常运行
  • 当然我们也可以设置 @RequestParam 里面的required为false(默认为true 代表必须带参数) 这样t_rparam1就跟t_rparam2是一样的了。

    12、@RequestHeader

    利用@RequestHeader 注解可以把Request请求header部分的值绑定到方法的参数上。

    
    @RequestMapping("/t_heander")  
    public void getRequestHeaderTest(HttpServletRequest request,
                                HttpServletResponse response,
                                @RequestHeader("Accept-Encoding")String encoding)  {  
    
      //do something 
    } 
    

    13、@CookieValue

    @CookieValue就是把Request header中cookie的值绑定到方法的参数上。比如说我们的cookie如下:

    
    Cookie:JSESSIONID=ka8A5L5t7WTUPXbaLupBieqOdmc0ZpD5MyKvea6oQr7JJSIZzM;userId=001;sysFlag=glmapper
    

    获取如下:

    
    @RequestMapping("/t_cookie")  
    public void getCookieValueTest(@CookieValue("JSESSIONID") String cookie)  {  
      //do something 
    } 
    

    14、@RequestBody

    @RequestBody这个注解常用来处理Content-Type不是application/x-www-form-urlencoded编码的内容,比如说:application/json, application/xml等等;这个和ResonseBody可以反过来理解。

    15、@ModelAttribute

  • 方法上 通常用来在处理@RequestMapping之前,为请求绑定需要从后台查询的model;
  • 参数上 用来通过名称对应,把相应名称的值绑定到注解的参数bean上;
  • 通常用来在处理@RequestMapping之前,为请求绑定需要从后台查询的model;

    用来通过名称对应,把相应名称的值绑定到注解的参数bean上;

    参考

  • 《Spring技术内幕》
  • https://www.cnblogs.com/FrankLei/p/6579843.html
  • 原文始发于微信公众号(glmapper工作室):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> Spring源码系列——注解详解


     上一篇
    Spring源码系列——依赖注入(四)-总结 Spring源码系列——依赖注入(四)-总结
    Spring源码系列:依赖注入(一)getBean Spring源码系列:依赖注入(二)createBean Spring源码系列:依赖注入(三)-属性注入 在上面三篇文章中对依赖注入做了一个大致的梳理;里面都是大量代码的分析
    2021-04-05
    下一篇 
    SpringMVC源码系列——AbstractUrlHandlerMapping SpringMVC源码系列——AbstractUrlHandlerMapping
    微信公众号:glmapper工作室 如有问题或建议,请公众号留言 座右铭:开放 开源 分享 共享 AbstractUrlHandlerMapping是通过url来进行匹配的,也就是说通过url与对应的Handler包存到一个Map
    2021-04-05