注解的认识与使用

注解的基本元素

如果说注释是写给人看的,那么注解就是写给程序看的。它更像一个标签,贴在一个类、一个方法或者字段上。
当声明一个注解时,用到的东西:

  • 修复符: 访问修饰符必须为public,不写默认为pubic
  • 关键字:关键字为@interface;
  • 注解名称: 注解名称为自定义注解的名称,使用时还会用到;
  • 注解类型元素:注解类型元素是注解中内容,可以理解成自定义接口的实现部分;
  • ()不是定义方法参数的地方,也不能在括号中定义任何参数,仅仅只是一个特殊的语法
  • 基本数据类型、String、Class、枚举类型、注解类型(体现了注解的嵌套效果)以及上述类型的一位数组
public @interface Info {
    String value() default "tracy";
    boolean isDelete();
}

使用元注解修饰注解

JDK中有一些元注解,主要有@Target,@Retention,@Document,@Inherited用来修饰注解

@Target值介绍

表明该注解可以应用的java元素类型

Target的类型值描述
ElementType.TYPE应用于类、接口(包括注解类型)、枚举
ElementType.FIELD应用于属性(包括枚举中的常量)
ElementType.METHOD应用于方法
ElementType.PARAMETER应用于方法的形参
ElementType.CONSTRUCTOR应用于构造函数
ElementType.LOCAL_VARIABLE应用于局部变量
ElementType.ANNOTATION_TYPE应用于注解类型
ElementType.PACKAGE应用于包
ElementType.TYPE_PARAMETER1.8版本新增,应用于类型变量
ElementType.TYPE_USE1.8版本新增,应用于任何使用类型的语句中(例如声明语句、泛型和强制转换语句中的类型)

@Retention值介绍

表明该注解的生命周期

Retention取值值描述
RetentionPolicy.SOURCE注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃
RetentionPolicy.CLASS注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期
RetentionPolicy.RUNTIME注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在

@Documented

描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息

@Inherited

表明使用了@Inherited注解的注解,所标记的类的子类也会拥有这个注解

@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Info {
      String value() default "tracy";
      boolean isDelete();
}

自定义注解

比如有一个要评分的活动,对于其中的每个子项目进行评分(加分,减分,不加分也不减分)

定义一个注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.FIELD)
public @interface MarkReason {
    //评分项目名称
    String reasonName();
    //是否减分项
    boolean isSubtraction() default false;
    //是否待评估
    boolean isUnknown() default false;
    //是否加分项
    boolean isAddition()  default true;
}

定义一个类,里面使用到注解:

public class UserMark {

    @MarkReason(reasonName="卫生清扫干净")
    private Double score1;

    @MarkReason(reasonName="节约用电",isUnknown = true,isAddition=false)
    private Double score2;

    @MarkReason(reasonName="节约水资源",isSubtraction=true,isAddition = false)
    private Double score3;

    private Double score4;


}

获取注解里面的内容:

public static void setMarkReasons() throws Exception {
        Class beanClass = UserMark.class;
        /**
         * getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。
         * getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
         */
        Field[] fields = beanClass.getDeclaredFields();

        //遍历属性
        for (Field field : fields) {
            //判断该属性是否被 MarkReason  注解修饰
            if (field.isAnnotationPresent(MarkReason.class)) {
                StringBuilder reasonsSb = new StringBuilder();
                //允许私有属性访问
                field.setAccessible(true);
                MarkReason reasonAnno = field.getAnnotation(MarkReason.class);

                //判断是否是减分项
                String isSubtraction = "无";
                if (reasonAnno.isSubtraction()) {
                    isSubtraction = "-10";
                }
                //判断是否是加分项
                String isAddition = "无";
                if (reasonAnno.isAddition()) {
                    isAddition = "+10";
                }

                //判断是否待评估
                String isUnknown="否";
                if(reasonAnno.isUnknown()){
                    isUnknown = "是";
                }

                //拼装加减分原因
                reasonsSb.append(reasonAnno.reasonName() + ":" + field.getName() + ";-->减分:"+isSubtraction+ ";-->加分:"+isAddition+ ";-->待评估:"+isUnknown);
                System.out.println("被注解修饰的结果:"+reasonsSb.toString());
            }else{
                System.out.println("没有被注解修饰的:"+field.getName());

            }
        }

    }

运行时注解(如@Autowired)会采用反射机制处理,针对编译时注解(如@Override)会采用 AbstractProcessor

自定义注解解析器

自己创建一个类,继承AbstractProcessor

@AutoService(Processor.class)
//等同于下面写的getSupportedSourceVersion()方法
@SupportedSourceVersion(SourceVersion.RELEASE_8)
//等同于下面写的getSupportedAnnotationTypes()方法
@SupportedAnnotationTypes(value= {"com.example.usersport.annotation.MarkReason"})
public class FactoryProcessor extends AbstractProcessor {
    private Types typeUtils;
    private Elements elementUtils;
    private Filer filer;
    private Messager messager;



    /**返回值表示注解是否由当前Processor 处理。如果返回 true,则这些注解由此注解来处理,
     * 后续其它的 Processor 无需再处理它们;如果返回 false,则这些注解未在此Processor中处理,
     * 那么后续 Processor 可以继续处理它们
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
        System.out.println("进入了process");
        Messager messager = processingEnv.getMessager();
        for (Element ele : roundEnvironment.getElementsAnnotatedWith(MarkReason.class)) {
            if (ele.getKind() == ElementKind.FIELD) {
                messager.printMessage(Diagnostic.Kind.NOTE, "输出值是:" + ele.toString());
            }
        }
        return true;

    }

    /**
     * 这个方法用于初始化处理器,方法中有一个ProcessingEnvironment类型的参数,ProcessingEnvironment是一个注解处理工具的集合。它包含了众多工具类。例如:
     * Filer可以用来编写新文件;
     * Messager可以用来打印错误信息;
     * Elements是一个可以处理Element的工具类
     * @param processingEnvironment
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        System.out.println("进入了init");
        super.init(processingEnv);
        typeUtils = processingEnv.getTypeUtils();
        elementUtils = processingEnv.getElementUtils();
        filer = processingEnv.getFiler();
        messager = processingEnv.getMessager();

    }

    /**
     * 这个方法的返回值是一个Set集合,集合中指要处理的注解类型的名称
     * (这里必须是完整的包名+类名,例如com.example.annotation.Factory)。
     * 由于在本例中只需要处理@Factory注解,因此Set集合中只需要添加@Factory的名称即可
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        System.out.println("进入了getSupportedAnnotationTypes");
        Set<String> annotations = new LinkedHashSet<>();
        annotations.add(MarkReason.class.getCanonicalName());
        annotations.add(Controller.class.getCanonicalName());
        return annotations;
    }



    /**
     * 用来指定当前正在使用的Java版本,通常return SourceVersion.latestSupported()即可
     * @return
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        System.out.println("getSupportedSourceVersion");
        return  SourceVersion.latestSupported();
    }

}

类的顶部加入注解:@AutoService(Processor.class),这个注解处理器是Google开发的。
当启动程序时,可以用来生成 META-INF/services/javax.annotation.processing.Processor
效果图:
在这里插入图片描述

常用的注解

@Component

用来定义Bean, 不好归类时使用

  • @Controller:定义控制层Bean
  • @Service:定义业务层Bean
  • @Repository:定义DAO层Bean

@Deprecated

若某类或某方法加上该注解之后,表示此方法或类不再建议使用,调用时也会出现删除线,但并不代表不能用,只是说,不推荐使用,因为还有更好的方法可以调用。

//给方法添加此注解
@Deprecated
OCRInfo<BankCardResult> getBankCardInfo(String image,String platform);

加入注解后,在调用此方法的位置会有提示:
在这里插入图片描述

@Controller和@RestController

  1. 直接在这个类上使用@RestController,类里面的方法上就可以不加@Responsebody
  2. 如果你想处理完请求后,然后跳到另外一个页面,不要在你的这个类上使用@RestController

@Autowired与@Resource

都可以用来装配bean. 都可以写在字段上,或写在setter方法上。

  • @Autowired默认按类型装配(这个注解是属于spring的),
  1. 默认情况下必须要求依赖对象必须存在,即:@Autowired(required=true),
  2. 如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配
  3. 可以结合@Qualifier注解进行使用,解决按类型匹配找到多个Bean问题
  • @Resource(这个注解属于J2EE的),
  1. 默认按name注入,找不到名称匹配的bean再按类型装配,可以通过name和type属性进行选择性注入
  2. 可以写在成员属性上,或写在setter方法上
  3. 可以通过@Resource(name=“beanName”) 指定被注入的bean的名称, 要是未指定name属性, 默认使用成员属性的变量名,一般不用写name属性

@RequestParam

将请求参数绑定到你控制器的方法参数上

语法:@RequestParam(value=”参数名”,required=”true/false”,defaultValue=””)
 
value:请求中传入参数的名称
required:是否包含该参数,默认为true,表示该请求路径中必须包含该参数,如果不包含就报错。
 
defaultValue:默认参数值,如果设置了该值,required=true将失效,自动为false,如果没有传该参数,就使用默认值

@RequestBody

主要用来接收前端传递给后端的json字符串中的数据,GET方式无请求体,前端不能使用GET方式提交数据,而是用POST方式进行提交
请求的类:

@RequestMapping("/json")
@ResponseBody
public UserJsonTest json(@RequestBody UserJsonTest userJsonTest) {
        System.out.println("进入了UserJsonTest");
        System.out.println(userJsonTest.toString());
        return userJsonTest;
    }

实体类:(省略了getter,setter)

public class UserJsonTest {

    @JsonAlias(value={"Name","name1","name2"})
    private String name;

    private int age;

    private String gender;

    @JsonProperty("NOTEMESSAGE")
    private String noteMessage;

    private String flag;
    }

请求报文:

{
    "name2":"张三",
    "age":"18",
    "gender":"男男",
    "gender":"女女",
    "test":"没有用",
    "NOTEMESSAGE":"这是个注释",
    "noteMessage":"666"
}

返回报文:

{
    "name": "张三",
    "age": 18,
    "gender": "女女",
    "flag": null,
    "NOTEMESSAGE": "这是个注释"
}

运行时控制台日志:

进入了UserJsonTest
UserJsonTest{name='张三', age=18, gender='女女', noteMessage='这是个注释', flag='null'}

解释:
根据不同的Content-Type等情况,Spring-MVC会采取不同的HttpMessageConverter实现来进行信息转换解析。
下面介绍的是最常用的:前端以Content-Type 为application/json,传递json字符串数据;后端以@RequestBody
模型接收数据的情况。

  1. 使用 @JsonAlias注解,实现:json转模型时,使json中的特定key能转化为特定的模型属性;但是模型转json时,对应的转换后的key仍然与属性名一致
  2. 使用@JsonProperty注解,实现:json转模型时,使json中的特定key能转化为指定的模型属性;同样的,模型转json时,对应的转换后的key为指定的key(示例中的NOTEMESSAGE字段)
  3. 使用@JsonAlias注解需要依赖于setter、getter,而@JsonProperty注解不需要。
  4. 在不考虑上述两个注解的一般情况下,key与属性匹配时,默认大小写敏感
  5. 有多个相同的key的json字符串中,转换为模型时,会以相同的几个key中,排在最后的那个key的值给模 型属性复制,因为setter会覆盖原来的值。(见示例中的gender属性)
  6. 后端@RequestBody注解对应的类在将HTTP的输入流(含请求体)装配到目标类(即:@RequestBody后面
    的类)时,会根据json字符串中的key来匹配对应实体类的属性,如果匹配一致且json中的该key对应的值
    符合(或可转换为)实体类的对应属性的类型要求时,会调用实体类的setter方法将值赋给该属性

@ResponseBody

将java对象转为json格式的数据,作用在方法上,将该方法的返回结果直接写入 HTTP response body 区;用此注解之后不会再走视图处理器

使用@ResponseBody例子:

 @RequestMapping("/json/response/1")
    @ResponseBody
    public UserJsonTest jsonResponse1() {
        System.out.println("进入了jsonResponse");
        UserJsonTest user =  new  UserJsonTest();
        user.setAge(78);
        user.setName("李四1");
        user.setNoteMessage("这是注释");
        return user;
    }

浏览器中请求的效果图:
在这里插入图片描述

不使用@ResponseBody例子:

@RequestMapping("/json/response/2")
    public void jsonResponse2( HttpServletResponse response) throws Exception {
        System.out.println("进入了jsonResponse");
        UserJsonTest user =  new  UserJsonTest();
        user.setAge(78);
        user.setName("李四2");
        user.setNoteMessage("这是注释");
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json");
        response.getWriter().write(user.toString());
    }

浏览器中请求的效果图:
在这里插入图片描述

@RequestMapping

它可以在方法和类的声明中使用;看它的属性中有好多方法均返回数组,也就是可以定义多个属性值
它的源码:

@Target({ElementType.TYPE, ElementType.METHOD})
@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 {};
}

@AliasFor表示别名,它可以注解到自定义注解的两个属性上,表示这两个互为别名,也就是说这两个属性其实同一个含义;无论指明设置哪个属性名设置属性值,另一个属性名也是同样属性值。若两个都指明属性值,要求值必须相同(必须声明相同的返回类型,必须声明默认值且默认值要相同),否则会报错。

举例属性值的使用:
    @RequestMapping(value="/json/requestMapping",
            method= {RequestMethod.POST,RequestMethod.GET},
            params={"username=picc","password=123"},
            headers={"Host=localhost:8087"},
            produces=MediaType.APPLICATION_JSON_VALUE+";charset=iso-8859-1",
            consumes= {MediaType.APPLICATION_JSON_VALUE,MediaType.APPLICATION_ATOM_XML_VALUE})
    @ResponseBody
    public String jsonRequestMapping( HttpServletResponse response) throws Exception {
        System.out.println("进入了requestMapping");
        UserJsonTest user =  new  UserJsonTest();
        user.setAge(78);
        user.setName("李四2");
        user.setNoteMessage("这是注释");
        return user.toString();
    }
注解中的属性值作用:
属性值示例作用描述报错时参考图
method= {RequestMethod.POST,RequestMethod.GET}同时指定多个请求方式在这里插入图片描述
params={“username=picc”,“password=123”}该属性表示请求参数,也就是追加在URL上的键值对,多个请求参数以&隔开(此时要求请求的url后面必须要有username=picc&password=123)在这里插入图片描述
headers={“Host=localhost:8087”}可以限制客户端发来的请求(此处限制请求为本地请求且端口是8087)在这里插入图片描述
consumes= {MediaType.APPLICATION_JSON_VALUE,MediaType.APPLICATION_ATOM_XML_VALUE}指定处理请求的提交内容类型(Content-Type)在这里插入图片描述
produces=MediaType.APPLICATION_JSON_VALUE+";charset=iso-8859-1"可以设置返回值类型还可以设定返回值的字符编码在这里插入图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值