Java自定义注解Annotation详解[通俗易懂]

简介开发中经常使用到注解,在项目中也偶尔会见到过自定义注解,今天就来探讨一下这个注解是什么鬼,以及注解的作用和自定义注解。列举开发中常见的注解@Override:当重写父类的方法时一般都会在方法上标注上此注解(我们最经常看到的toString()方法上总能看到这货)@Deprecated:用于标记某个方法已经过期,请使用新的方法来替代已经废弃的方法@SuppressWarnings:让编译器或

大家好,又见面了,我是你们的朋友全栈君。

分享一个朋友的人工智能教程(请以“右键”->”在新标签页中打开连接”的方式访问)。比较通俗易懂,风趣幽默,感兴趣的朋友可以去看看。

一:简介

开发中经常使用到注解,在项目中也偶尔会见到过自定义注解,今天就来探讨一下这个注解是什么鬼,以及注解的应用场景和如何自定义注解。

下面列举开发中常见的注解

  • @Override:用于标识该方法继承自超类, 当父类的方法被删除或修改了,编译器会提示错误信息(我们最经常看到的toString()方法上总能看到这货)

  • @Deprecated:表示该类或者该方法已经不推荐使用,已经过期了,如果用户还是要使用,会生成编译的警告

  • @SuppressWarnings:用于忽略的编译器警告信息

  • Junit测试:@Test

  • Spring的一些注解:@Controller、@RequestMapping、@RequestParam、@ResponseBody、@Service、@Component、@Repository、@Resource、@Autowire

  • Java验证的注解:@NotNull、@Email

下面看一下注解Override.java的庐山真面目

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {

}

二:注解基本知识

1. 注解数据类型

注解是写在.java文件中,使用@interface作为关键字, 所以注解也是Java的一种数据类型,从广泛的定义来说,Class、Interface、Enum、Annotation都属于Class类型。

2. 元注解

在创建注解的时候,需要使用一些注解来描述自己创建的注解,就是写在@interface上面的那些注解,这些注解被称为元注解,如在Override中看到的@Target、@Retention等。下面列出一些元注解

  • @Documented: 用于标记在生成javadoc时是否将注解包含进去,可以看到这个注解和@Override一样,注解中空空如也,什么东西都没有

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Documented {
    
    }
    
  • @Target:用于定义注解可以在什么地方使用,默认可以在任何地方使用,也可以指定使用的范围,开发中将注解用在类上(如@Controller)、字段上(如@Autowire)、方法上(如@RequestMapping)、方法的参数上(如@RequestParam)等比较常见。

    • TYPE : 类、接口或enum声明
    • FIELD: 域(属性)声明
    • METHOD: 方法声明
    • PARAMETER: 参数声明
    • CONSTRUCTOR: 构造方法声明
    • LOCAL_VARIABLE:局部变量声明
    • ANNOTATION_TYPE:注释类型声明
    • PACKAGE: 包声明

    Target.java

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Target {
        /**
         * Returns an array of the kinds of elements an annotation type
         * can be applied to.
         * @return an array of the kinds of elements an annotation type
         * can be applied to
         */
        ElementType[] value();
    }
    
    public enum ElementType {
        /** Class, interface (including annotation type), or enum declaration */
        TYPE,
    
        /** Field declaration (includes enum constants) */
        FIELD,
    
        /** Method declaration */
        METHOD,
    
        /** Formal parameter declaration */
        PARAMETER,
    
        /** Constructor declaration */
        CONSTRUCTOR,
    
        /** Local variable declaration */
        LOCAL_VARIABLE,
    
        /** Annotation type declaration */
        ANNOTATION_TYPE,
    
        /** Package declaration */
        PACKAGE,
    
        /** Type parameter declaration */
        TYPE_PARAMETER,
    
        /** Use of a type */
        TYPE_USE
    }
    
  • @Inherited:允许子类继承父类中的注解,可以通过反射获取到父类的注解

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Inherited {
    
    }
    
  • @Constraint:用于校验属性值是否合法

    @Documented
    @Target({ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Constraint {
        Class<? extends ConstraintValidator<?, ?>>[] validatedBy();
    }
    
  • @Retention:注解的声明周期,用于定义注解的存活阶段,可以存活在源码级别、编译级别(字节码级别)、运行时级别

    • SOURCE:源码级别,注解只存在源码中,一般用于和编译器交互,用于检测代码。如@Override, @SuppressWarings。

    • CLASS:字节码级别,注解存在于源码和字节码文件中,主要用于编译时生成额外的文件,如XML,Java文件等,但运行时无法获得。 如mybatis生成实体和映射文件,这个级别需要添加JVM加载时候的代理(javaagent),使用代理来动态修改字节码文件。

    • RUNTIME:运行时级别,注解存在于源码、字节码、java虚拟机中,主要用于运行时,可以使用反射获取相关的信息。

      @Documented
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.ANNOTATION_TYPE)
      public @interface Retention {
          /**
           * Returns the retention policy.
           * @return the retention policy
           */
          RetentionPolicy value();
      }
      

3. 注解的内容

在上面的注解源码中可以看到有的注解中没有任何内容,有的注解的有内容,看似像方法。

注解的内容的语法格式: 数据类型 属性名() default 默认值,数据类型用于描述属性的数据类型,默认值是说当没有给属性赋值时使用默认值,一般String使用空字符串””作为默认值,数组一般使用空数组{ }作为默认值.

下面看一下SpringMVC中的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 {};
}

使用SpringMVC中的RequestMapping注解

@RequestMapping(value = "/list", 
				method = RequestMethod.POST, 
				produces = {"application/json;charset=UTF-8;"})
public String list(){

}

4. 注解的使用场景

可以通过注解的声明周期来分析注解的使用场景:

  • SOURCE源码级别:给编译器使用,如@Override、@Deprecated 等, 这部分开发者应该使用的场景不多
  • CLASS:字节码级别,这部分也很少见到
  • RUNTIME:运行时级别,这个是最多的,几乎开发者使用到的注解都是运行时级别,运行时注解常用的有以下几种情况
    • 注解中没有任何属性的,空的注解,这部分注解通常起到一个标注的作用,如@Test、@Before、@After,通过获取这些标记注解在逻辑上做一些特殊的处理
    • 可以使用约束注解@Constraint来对属性值进行校验,如@Email, @NotNull等
    • 可以通过在注解中使用属性来配置一些参数,然后可以使用反射获取这些参数,这些注解没有其他特殊的功能,只是简单的代替xml配置的方式来配置一些参数。使用注解来配置参数这在Spring boot中得到了热捧,如@Configuration

关于配置方式xml vs annotation, 一般使用xml配置一些和业务关系不太紧密的配置,使用注解配置一些和业务密切相关的参数。


三:注解和反射基本API

// 获取某个类型的注解
public <A extends Annotation> A getAnnotation(Class<A> annotationClass);
// 获取所有注解(包括父类中被Inherited修饰的注解)
public Annotation[] getAnnotations(); 
// 获取声明的注解(但是不包括父类中被Inherited修饰的注解)
public Annotation[] getDeclaredAnnotations();
// 判断某个对象上是否被某个注解进行标注
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

// 获取某个类声明的所有字段
public Field[] getDeclaredFields() throws SecurityException;
// 获取某个方法
public Method getMethod(String name, Class<?>... parameterTypes);

四:自定义注解

使用自定义注解+拦截器或者是AOP等可以进行权限的控制。

下面通过定义一个注解用来限制当用户访问接口时必须要登录的示例

步骤一:定义注解
RequiresLogin.java

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresLogin {

}

步骤二:使用注解

@Controller
@RequestMapping("/user")
public class UserController {
	@RequiresLogin
	@RequestMapping(value = "/list", produces = {"application/json;charset=UTF-8;"})
	public String getUserList(){

		System.out.println("--------------");
		return "[{'id': 1, 'username':'zhangsan'}]";
	}
}

步骤三:使用AOP进行拦截,解析注解

public class LoginAdvices {
    public void before(JoinPoint joinPoint) throws Exception{

        Object target = joinPoint.getTarget();
        String methodName = joinPoint.getSignature().getName();

        System.out.println(target + "-------" + methodName);
        Method method = target.getClass().getMethod(methodName);
        boolean annotationPresent = method.isAnnotationPresent(RequiresLogin.class);
        if (annotationPresent) {
            // 用户必须登录
            boolean isLogin = false;
            if (!isLogin) {
                throw new Exception("访问该接口必须先登录");
            } else {
                System.out.println("已登录...");
            }
        }
    }
}

在applicationContext.xml中配置aop

<bean id="loginAdvices" class="com.mengdee.manager.aop.LoginAdvices"/>
    <!-- aop配置 -->
    <aop:config proxy-target-class="true">
        <!--切面 -->
        <aop:aspect ref="loginAdvices">
            <!-- 切点 -->
            <aop:pointcut id="pointcut1" expression="execution(* com.mengdee.manager.controller.*.*(..))"/>
            <!--连接通知方法与切点 -->
            <aop:before method="before" pointcut-ref="pointcut1"/>
        </aop:aspect>
    </aop:config>

浏览器访问http://localhost:8080/xxx/user/list

关于如何自定义注解对属性值的合法性进行校验,请移步http://blog.csdn.net/vbirdbest/article/details/72620957 这里有完整的示例


自定义异常


  • 为什么要自定义异常
    Java虽然提供了丰富的异常处理类,但是在项目中还会经常使用自定义异常,其主要原因是Java提供的异常类在某些情况下还是不能满足各种业务的需求。 例如系统中有些错误是符合Java语法,但不符合业务逻辑。如当用户登录时账号不存在或者账号已锁定可以自定义一个账号异常AccountException。
    或者有些情况下Java的同一个异常可能会有多种原因引起,在排查问题时不容易定位错误,此时可以使用自定义一个更加明确的异常。

  • 自定义异常的好处:自定义异常可以使异常更加明确,可以隐藏底层的异常,这样更安全,异常信息更加直观。

  • 自定义异常的使用:自定义异常一般继承自Exception或者RuntimeException,根据业务需要可以带一些属性作为构造函数的参数,自定义异常需要程序员手动抛出异常,并处理异常。

  • 下面是Apache Shiro中自定义异常的示例

public class ShiroException extends RuntimeException {
    public ShiroException() {
    }

    public ShiroException(String message) {
        super(message);
    }

    public ShiroException(Throwable cause) {
        super(cause);
    }

    public ShiroException(String message, Throwable cause) {
        super(message, cause);
    }
}
分享一个朋友的人工智能教程(请以“右键”->”在新标签页中打开连接”的方式访问)。比较通俗易懂,风趣幽默,感兴趣的朋友可以去看看。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/129039.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • docker菜鸟教程_k8s部署docker镜像

    docker菜鸟教程_k8s部署docker镜像前记:最近跟着哔站码神之路做了一个SpringBoot练手项目,第一次操作碰到了很多困难和问题,尤其是在部署部分,走了很多弯路,这里写下自己的部署过程,供大家参考,也欢迎大家提出宝贵的意见。哔站码神视频链接:https://www.bilibili.com/video/BV1Gb4y1d7zb?p=36我的网站:www.zhangshidi.space前置知识以下知识点希望大家首先搜一搜,读一读,有一个大概的了解。什么是Linux以及掌握Linux的一些基本指令。什么是docke

    2022年10月19日
  • 关于博客背景_好看的博客背景图

    关于博客背景_好看的博客背景图目前从事nuclear工程设计工作,基本与编程无关行业。但因为读研期间主要从事编程开发相关研究,所以有所涉猎,主要为C++、ZIGbee、RFID、STM32相关工作,但是基础知识较为欠缺。想通过博客的方式督促自身的学习。博客内容主要集中在机器学习和软件编程相关,虽然之前有过嵌入式、单片机的软硬件设计经验,但是相对学习成本高昂,不适合现在业余自学,所以目前专注于计算机软件编程知识。与君共勉,2

    2022年10月10日
  • 用settimeout如何实现倒计时_javascript一分钟倒计时代码

    用settimeout如何实现倒计时_javascript一分钟倒计时代码<!–8秒倒计时–><p><spanid=”time”></span>秒后自动跳转到老版本</p><!–js部分–>functioncountDown(secs,url){//secs–设置倒计时秒数,url–要跳转的链接 vartime=document.getElementById(“time”) time.innerHTML=secs//页面上显示所设定的倒计时时长 if

  • 像素,分辨率,PPI(像素密度),BPP 扫盲

    像素,分辨率,PPI(像素密度),BPP 扫盲像素于分辨率像素,又称画素,为图像显示的基本单位,译自英文“pixel”,pix是英语单词picture的常用简写,加上英语单词“元素”element,就得到pixel,故“像素”表示“图像元素”之意,有时亦被称为pel(picture element)。每个这样的信息元素不是一个点或者一个方块,而是一个抽象的采样。仔细处理的话,一幅图像中的像素可以在任何尺度上看起来都不像分离的点或者方块;但…

  • Docker安装配置教程[通俗易懂]

    Docker安装配置教程[通俗易懂]之前docker很久就写了笔记了。后面因为家里有事,耽搁了一年。现在把这部分笔记重新上传一下。大家看看。Docker要求:lunix内核,要求3.8以上centos7Docker是一个进程,一启动就两个进程,一个服务,一个守护进程。占用资源就非常少,启动速度非常快,1s。一台机器上vm,3到10个实例。docker100到10000。1.核心概念:1)镜像images,事先做好一…

    2022年10月10日
  • SQL 模糊查询LIKE concat用法[通俗易懂]

    SQL 模糊查询LIKE concat用法[通俗易懂]concat用来拼接查询的字符串,如下代码所示SELECT*FROMdeploymentWHEREnameLIKEconcat(concat(‘%’,#{queryMessage}),’%’) 

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号