精华内容
下载资源
问答
  • 所以就在今天我点进去源码,一直摁到恩不出来,看的我一脸懵逼!想成为大佬,却无奈自己依然是个弱鸡,只能百度了!百度大法好!!! 百度上写的注解作用:批注保留策略,是一个枚举类型。此枚举类型常量,...

    元注解@RetentionPolicy

    在最近的开发过程中遇到很多的问题,突然间发现自己真的是什么也不会,对一些注解只会粘贴,粘贴之后怎么用一点也没有头绪,真是气到抓头(本来也所剩无几,哭泣!)
    头发:“你别抓我,抓你自己!”
    在这里插入图片描述
    我深知自己要做做出改变了!!所以就在今天我点进去看了源码,一直摁到恩不出来,看的我一脸懵逼!想成为大佬,却无奈自己依然是个弱鸡,只能百度了!百度大法好!!!
    在这里插入图片描述
    百度上写的注解作用:批注保留策略,是一个枚举类型。此枚举类型的常量,描述用于保留注释的各种策略。它们被使用与{@link Retention}元注释类型一起指定。注释要保留多长时间。
    我又点进去看了下源码!
    在这里插入图片描述

    主要功能

    类型 功能
    source 编译器将丢弃注释。
    class 注释将由编译器记录在类文件中,但不需要在运行时由VM保留。这是默认值行为
    runtime 注释将由编译器记录在类文件中,并在运行时由VM获取,因此它们可以被反射式读取。

    注:VM为虚拟机
    就这些,不说了,我去剪头发了!
    在这里插入图片描述

    展开全文
  • java注解在实际工作中经常彭代,无论是java内置注解还是框架自带注解,在我们使用这些注解的时候是否思考过这些注解到底是什么作用?是怎么起作用的?java以及框架是怎么识别这些注解的?本篇我们就来聊一聊注解...

    java源码分析-注解基本原理

    ​ java注解在实际工作中经常彭代,无论是java内置注解还是框架自带注解,在我们使用这些注解的时候是否思考过这些注解到底是什么作用?是怎么起作用的?java以及框架是怎么识别这些注解的?本篇我们就来聊一聊注解的基本原理。

    1.注解是什么

    ​ 其实注解在大多数情况下与普通的修饰符(public,void,static等)在使用方式上并没有多大区别。我们先来看个例子:

    public class AnnotationDemo {
    
        @Override
        public String toString() {
            return super.toString();
        }
    
        @Deprecated
        public static void methodA(){
            System.out.println("方法已过期");
        }
    
        @SuppressWarnings("uncheck")
        public static void methodB(){
            System.out.println("忽略检查警告");
        }
    
    }
    

    ​ 上面的例子中@Override是一个注解,这是一个java内置注解,java通过该注解表示某个方法重写了父类的方法。可以看到注解其实就类似于一个标识符,当java运行时会根据注解的功能完成相关功能。而对于@Deprecated和@SuppressWarnings(“uncheck”)这两个注解,也是Java本身内置的注解,在代码中也可以经常看见它们,当方法或是类上面有@Deprecated注解时,说明该方法或是类都已经过期不建议再用,@SuppressWarnings 则表示忽略指定警告,比如@SuppressWarnings(“uncheck”),这就是注解的最简单的使用方式。

    ​ 自java1.5版本引入注解之后,注解就成了java平台中非常重要的一部分。注解也叫元数据,即一种描述数据的数据。所以可以说注解就是源代码的元数据。

    1.1注解的基本语法

    元注解

    ​ 元注解是用来修饰注解的注解,通常用作注解的定义和声明上。那最常见的@Overide注解来看一下它的定义:

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

    其中元注解就是@Target和@Retention。在java中有以下几个元注解:

    • @Target:注解的作用目标
    • @Retention:注解的生命周期
    • @Documented:注解是否应当被包含在 JavaDoc 文档中
    • @Inherited:是否允许子类继承该注解

    @Target注解用来指定声明的注解作用范围,也就是说@Override注解可以在哪些地方使用。

    @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();
    }
    

    @Target注解内部是是一个ElementType类型的数组。表示可以通过设置ElementType类型的值来为@Target设置值。看一下ElementType是一个什么?

    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
         *
         * @since 1.8
         */
        TYPE_PARAMETER, //类型参数(1.8新加入)
    
        /**
         * Use of a type
         *
         * @since 1.8
         */
        TYPE_USE    //类型使用声明(1.8新加入)
    }
    

    可以看到ElementType是一个枚举类,表示@Target注解的value可以设置的范围是这些枚举值。各个枚举值的含义也已经给出。

    注意当@Target未指定具体的值时,则表示此注解可用在任何地方。当有多个值时,可以使用{}并用逗号隔开:

    @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
    

    @Retention用来约束注解的生命周期,它的底层也是一个枚举类,但是与@Target不同,@Retention一次只能设置一个值。

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

    RetentionPolicy枚举类,它有三个值:SOURCE、CLASS、RUNTIME

    • SOURCE:表示注解会被编译期丢弃。该注解只会保留在源码中,源码经过编译,注解将被丢弃,即在class文件中将找不到该注解了;
    • CLASS:表示注解会被VM丢弃。该注解会保留在class文件中,但是当class加载到vm内存中是,该注解会被丢弃。注意,当注解未定义Retention值时,默认值是CLASS,如Java内置注解,@Override、@Deprecated、@SuppressWarnning等;
    • RUNTIME:表示该注解将在运行期(jvm)中保留,因此可以通过反射机制获取到该注解的相关信息。如SpringMvc中的@Controller、@Autowired、@RequestMapping等。
    public enum RetentionPolicy {
        /**
         * Annotations are to be discarded by the compiler.
         */
        SOURCE,
    
        /**
         * Annotations are to be recorded in the class file by the compiler
         * but need not be retained by the VM at run time.  This is the default
         * behavior.
         */
        CLASS,
    
        /**
         * Annotations are to be recorded in the class file by the compiler and
         * retained by the VM at run time, so they may be read reflectively.
         *
         * @see java.lang.reflect.AnnotatedElement
         */
        RUNTIME
    }
    

    所以@Override上的@Retention(RetentionPolicy.SOURCE)就表示@Override注解只会被保留在源码中。

    @Documented注解用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,也就是说被@Documented修饰的注解会生成到javadoc中。Documented是一个标记注解,没有成员。

    @Inherited可以让注解被继承,但其实不是真的继承,只是通过使用@Inherited,可以让子类Class对象使用getAnnotations()获取父类被@Inherited修饰的注解,举例如下:

    public class MyAnnotation2{
        public static void main(String[] args) {
            ClassA1 classA1 = new ClassA1();
            Annotation[] annotations1 = classA1.getClass().getAnnotations();
            System.out.println(Arrays.toString(annotations1));
    
            ClassB1 classB1 = new ClassB1();
            Annotation[] annotations2 = classB1.getClass().getAnnotations();
            System.out.println(Arrays.toString(annotations2));
        }
    
    }
    
    @Inherited
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotationA1 {
    }
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotationB1 {
    }
    
    @MyAnnotationA1
    class ClassA {
    
    }
    
    class ClassA1 extends ClassA{
    
    }
    
    @MyAnnotationB1
    class ClassB{
    
    }
    
    class ClassB1 extends ClassB{
    
    }
    

    直接代码:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KfZ3GbvI-1614775733571)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210302203823212.png)]

    可以看到ClassA、ClassB上分别使用了@MyAnnotationA1、@MyAnnotationA2注解,而只有@MyAnnotationA1住街上使用了@Inherited注解,这样在子类继承ClassA、ClassB时,使用getAnnotations()方法只能获取ClassA1的父类ClassA上的注解信息。

    注解元素

    ​ 在上述对@Override在注解中,我们可以看到该注解内部没有定义其他元素,所以@Override也称为标记注解(marker annotation)。但是一般在定义注解时都会包含一些元素以表示某些值,方便处理器使用。比如我们看到的@SuppressWarnings注解:

    @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
    @Retention(RetentionPolicy.SOURCE)
    public @interface SuppressWarnings {
        String[] value();
    }
    

    这个注解的注解元素就是String数组类型的value;通过使用元素来进行设置一些值,注解中的元素类似于属性或者参数。注意到注解中的元素声明的方式是方法(无方法体),这一点类似与接口。

    这样在使用注解时,就可以为该注解设定相关的值,当猪姐处理器解析(后面会说)注解元素的值后会做相应的处理。

    注解支持的元素类型除了上面的String之外还有以下:

    • 基本类型(int、char、byte、double、float、long、boolean)
    • 字符串String
    • 类Class
    • 枚举enum
    • 注解Annotation
    • 上述类型的数组类型

    去除这些类型,当我们使用其他类型修饰注解元素时,编译期会报错,Invalid type 'Integer' for annotation member。当然基本类型的包装类型也是不允许在注解中修饰注解元素的。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hlUO6mGI-1614775733575)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210302205753669.png)]

    这里还需要注意一点:注解Annotation也是可以作为注解元素的修饰类型。也就是嵌套注解。如下:

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation3 {
    
    //    Integer value();	//编译器报错
    
        AnnotationX value() default @AnnotationX(flag = true);
    
    }
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface AnnotationX{
        boolean flag() default false;
    }
    

    java内置注解

    java的内置注解主要有三个,上面也提到过,这里简单的过一下,分别是@Override、@Deprecated、@SuppressWarnning。

    • @Override:用于标明此方法覆盖了父类的方法,源码如下:
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.SOURCE)
    public @interface Override {
    }
    
    • @Deprecated:用于标明已经过时的方法或类,源码如下:
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
    public @interface Deprecated {
    }
    
    • @SuppressWarnnings:用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告,其实现源码如下:
    @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
    @Retention(RetentionPolicy.SOURCE)
    public @interface SuppressWarnings {
        String[] value();
    }
    

    该注解内部有一个String数组类型的注解元素value,主要取值如下:

    • deprecation:使用了不建议使用的类或方法时的警告;
    • unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型;
    • fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
    • path:在类路径、源文件路径等中有不存在的路径时的警告;
    • serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;
    • finally:任何 finally 子句不能正常完成时的警告;
    • all:关于以上所有情况的警告。

    举个例子:

    当我们诗句集合时,没有使用泛型来指定集合保存的类型时;

    public static void main(String[] args) {
    
        List list = new ArrayList();
        list.add("a");
        list.add(1);
        System.out.println(list);
    
    }
    

    编辑器就会有如下警告:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j3Q8Xx1O-1614775733580)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210302222040033.png)]

    可以通过@SuppressWarnings(“unchecked”)消除警告:

    @SuppressWarnings("unchecked")
    List list = new ArrayList();
    

    1.2注解的默认值

    ​ 注解对注解元素的默认值有着一定的限制。具体的限制如下:

    (1)首先,注解元素的默认值必须明确,也就是说元素不能有不能确定的值。这就要求注解元素的默认值要么是在定义注解的时候通过default给出,要么是在使用注解时为元素提供具体的值;

    (2)对于非基本类型的注解元素无论是在源代码中声明,还是在注解接口中定义默认值,都不能以null作为值。通常可以使用一些特殊字符(空字符串、负数等)来解决这个问题。

    1.3注解不能够继承

    ​ 首先注解是不能够被继承的,不能使用关键字extends来继承某个@interface,但是编译器在将注解编译后,编译器会自动继承java.lang.annotation.Annotation接口,本来想把@Override反编译出来论证一下,但发现Idea反编译工具太智能,反编译出来没有变化。但是我通过Idea中的类图工具Diagrams查看如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2ft3aDQD-1614775733588)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210302213212556.png)]

    这也说明了Override注解是继承Annotation。

    2.自定义注解

    2.1如何自定义注解

    我们那java内置注解@SuppressWarnings来分析:

    @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
    @Retention(RetentionPolicy.SOURCE)
    public @interface SuppressWarnings {
        String[] value();
    }
    

    (1)首先@Target元注解不可少,用来标注注解的使用范围;

    (2)@Retention注解也是不可少的,通过它能够确定我们定义的注解的生命周期;

    (3)定义注解是使用@interface,注意月接口仅相差一个@符号,但是意义完全不同;

    (4)注解元素可有可无,需要定义注解元素是通常的格式:类型 字段名()。类型范围在上文艺界说明;

    (5)一般会为注解元素通过default设置默认值,如果没有设置,在使用注解时一定要有值。

    例如我们定义一个表示人物信息的注解:

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyPersonAnnotation {
        //姓名
        String name() default "";
    
        //年龄
        int age() default 0;
    
        //性格
        Character character() default Character.OPEN;
    }
    
    enum Character{
        OPEN,
        IRRITABLE,
        GENTLE;
    }
    

    简单分析一下:

    @MyPersonAnnotation注解:

    • @Target(ElementType.METHOD)表示注解可以使用在方法上;
    • @Retention(RetentionPolicy.RUNTIME)表示该注解在jvm运行时可以通过反射获取到注解信息;
    • 有三个注解元素分别是name、age和character,其中character的类型是我们自定义的枚举类型Character。

    2.2使用注解

    其实注解使用时,只要根据该注解的定义就可以基本就知道如何使用了,例如上面我们定义的注解可以用在方法上,具体如下:

    public class MyPersonAnnotationTest {
        public static void main(String[] args) {
            getPersonInfo();    
        }
    
        @MyPersonAnnotation(name = "zhangsan", age = 18, character = Character.GENTLE)
        private static void getPersonInfo() {
        }
    
    }
    

    2.3注解处理器

    注解定义好了,并且也在方法上使用了,但是通过这个注解能干什么呢?这就是注解处理器的作用了,这也是自定义注解使用的核心。

    下面面我们演示通过反射获取动态运行时的注解信息,而这种方式其实就是注解处理器的工作原理:

    public class MyPersonAnnotationTest {
        public static void main(String[] args) throws NoSuchMethodException {
            process(MyPersonAnnotationTest.class);
        }
    
        @MyPersonAnnotation(name = "zhangsan", age = 18, character = Character.GENTLE)
        private static void getPersonInfo() {
        }
    
        private static void process(Class clazz) throws NoSuchMethodException {
            //通过Class对象拿到getPersonInfo方法对象Method
            Method method = clazz.getDeclaredMethod("getPersonInfo", null);
            System.out.println(method.getName());
            //根据Method获取到该方法上的注解
            MyPersonAnnotation declaredAnnotations = method.getDeclaredAnnotation(MyPersonAnnotation.class);
            System.out.println(declaredAnnotations);
        }
    
    }
    

    打印结果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IxUIp733-1614775733589)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210303204231367.png)]

    也就是说我们通过反射机制,拿到了运行时getPersonInfo()方法上的注解信息,通过这个方式我们就可以对这个注解标注的类、方法、字段等做更多的操作了。需要注意的是,通过反射机制获取运行时注解信息,需要我们在定义注解是设置@Retention(RetentionPolicy.RUNTIME),否则无法获取到!

    实际上这就是注解处理器的原理,在Spring中就是通过通过这种方式定义各种注解处理器类和方法对spring的相关注解,如@Service等进行相应的操作。

    展开全文
  • 它到底是个什么,我们来看看源码的第一段注解怎么说明完第一段,总结下AQS是一个同步基础框架,基于一个先进先出队列。锁机制基于一个状态值,它是原子值。AQS子类负责定义与操作这个状态值,但必须通过...

    AQS,全称AbstractQueuedSynchronizer,是Concurrent包锁的核心,没有AQS就没有Java的Concurrent包。它到底是个什么,我们来看看源码的第一段注解是怎么说明

    9bd6209c8d8dea2d8c00ce7b3db8d7e9.png

    262f862480810a1b0ed9a94643936ca0.png

    7769f1e802cf2486a7f1da38ee0d2f37.png

    看完第一段,总结下

    AQS是一个同步的基础框架,基于一个先进先出的队列。

    锁机制基于一个状态值,它是原子值。

    AQS的子类负责定义与操作这个状态值,但必须通过AQS提供的原子操作

    AQS剩余的方法就是围绕队列,与线程阻塞唤醒等功能

    基于以上概念,我们看看源码到底是这么实现这些功能的

    AQS的成员变量

    3a47eff242c998524a0c0b64fea7876b.png

    state

    private volatile int state;

    该变量标记为volatile,说明该变量是对所有线程可见的。作用在于每个线程改变该值,都会马上让其他线程可见,在CAS(可见锁概念与锁优化)的时候是必不可少的。在AQS类中,不会直接操作这个值,而是交由它的子类去操作和定义他的作用。

    Node、head、tail

    AQS中有一个静态内部类Node,其实现是一个双向链表。head与tail则是这个链表的头尾指针。作用是存储获取锁失败的阻塞线程。同样的,这个链表是会被多个线程操作的,所以它里面的变量多是被标记为volatile,并且操作也要通过CAS等原子方法去执行。

    Node还有一个模式的属性:独占模式和共享模式。独占模式下,锁是线程独占的,而共享模式下,锁是可以被多个线程占用的。

    9a97221f1926c93abeacdd1ccac9c496.png

    VarHandler

    对于大多数需要操作的原子属性,都对应会有一个大写的值,它的类是VarHandler。例如state、head、tail都有对应的VarHandler,STATE、HEAD、TAIL。VarHandler是1.9的新特性,提供了类似于原子操作以及Unsafe操作的功能,里面的原子操作大多是native方法,比较难查看源码。

    d0866235b59099275965ed047ca670db.png

    ConditionObject

    条件队列,是AQS中一个非常关键内部类。这个名字起非常奇异,让人搞不懂,看它类注释也看不懂说了什么。看看AQS头部注解

    36de2e63abf6c3b0ae14b0d3be185e3a.png

    这个类是为了让子类支持独占模式的。深入看其中的源码实现,其实就是Node在功能性上的封装,最终让子类实现让当前线程怎么独占一个Object锁。await()、dosign()等方法就是让线程阻塞、加入队列、唤醒线程等。AQS框架下基本各种独占的加锁,解锁等操作到最后都是基于这个类实现的。该类是提供给子类去使用的,具体实现等下次说ReentranLock再深入了解。有人可能觉得为什么实现这个内部类,又不用,而是给子类去用,那为什么不放到子类去呢?其实答案,很简单,抽象加模板模式。

    13d6641ad47b7d7a389fdad5f355b116.png

    p.s. 只有独占锁才能配合该类使用。

    AQS的成员函数

    AQS的公用的方法,主要是加锁与解锁方法。以下方法只提供了模板,部分实现还是在子类当中,直接调用会抛出异常。

    acquire()

    尝试获取锁,失败则进入队列。

    f3023292c636d8556feb64662a46f2e5.png

    先执行tryAcquire()(子类实现),成功则直接返回,如果是获取锁失败,则执行addWaiter(),通过CAS在双向链表的尾部添加一个新独占节点。

    ae0f30c433c6a94ce68139535f2d4227.png

    然后把节点丢到acquireQueued()中执行。该方法其实就是自旋尝试获取锁或阻塞线程(子类实现决定)。一开始,获取新节点的前驱节点,如果这个节点是head,则证明只有两个节点,此时再次执行tryAcquire()尝试获取锁,若获取成功,则不需要中断,成功结束。

    e0bcb40644902bb7eb58a16555a12835.png

    如果还是获取失败,则执行shouldParkAfterFailedAcquire(),根据前驱节点状态(子类设值)判断是否继续自旋(当waitStatus为初始值,重复上一步,直到前面的节点一直在减少到前驱节点为head)或者阻塞线程(当waitStatus标记为SIGNAL)

    37e620b77a005f4a31b00b1d8abb8faf.png

    最后如果acquireQueued()返回需要阻塞,则执行selfInterrupt()设置线程为中断

    0f88e13c40921b513df211375302f717.png

    可以看回acquire()函数的写法,十分的艺术。利用条件判断的短路规则,实现在if()条件内嵌套判断执行语音。一般人(笔者本人)如果要实现这个功能,会这么写

    42aa6fefbb463c4f1c4c5cfb635c6459.png

    所以下次遇到类似嵌套if条件判断的语句,可以学习下acquire()的这种短路写法。赞👍

    acquireInterruptibly()

    检查线程是否被中断并尝试获取锁,失败则进入队列。线程中断会退出队列。

    流程基本和acquire()相同。不同点就是,acquireInterruptibly()在自旋获取过程中如果线程是中断的,那么就会抛出异常退出流程,并且放弃锁。

    40e83100e8eaf78aeb03366d1a48471a.png

    doAcquireInterruptibly()方法与acquireQueued()方法非常相似,不同就是前者在中断状态下,不会再继续获取锁。注意最后有cancelAcquire()方法的执行。

    bb64627fe00b5a7c4c905b92a7b0cf30.png

    tryAcquireNanos()

    尝试获取锁,失败则进入队列。当超过指定时间或线程中断会退出队列。

    在acquireInterruptibly()基础上,增加多一个时间判断,超过指定时间,则退出,放弃获取锁。

    b8dce14bacbd13507579897728ba61d9.png

    release()

    释放当前锁,并唤醒下一个Node。

    尝试释放锁

    696ed25e60344772e95b3e9cc9ee88a5.png

    若释放成功,且waitStatus不为0(证明是SIGNAL的),就会执行unparkSuccessor(),先取消SIGNAL标志,然后找到最近一个需要SIGNAL的节点,并且唤醒它。

    67f09e08d27c39a1bb432a226c2e093e.png

    **shared()

    以上方法皆为独占模式,对应都有共享模式的方法。最大的不同其实就是Node的waitStatus值为PROPAGATE。具体流程与独占大体相同,细节留到ReentrantReadWriteLock再细了解。

    总结

    回顾下要点

    AQS是一个同步的基础框架,是ReentranLock、ReentranReadWriteLock的父类

    AQS原理是维护一个state原子值,通过一个双向链表的队列实现同步。

    对于state、与队列的操作都是原子操作,通过VarHandle实现

    主要对外方法是加锁与解锁,区别是否中断、超时、共享或独占模式

    以上即使AQS的大致内容,可能有些部分难以理解,其实很正常,因为AQS提供的是流程模板与工具,没有实质落地的场景,是比较难理解的。等后面介绍ReentranLock与ReentrantReadWriteLock的时候,就可以更好更全面的了解整体AQS框架了。

    如果觉得还不错,请关注公众号:Zack说码

    展开全文
  • 不管是在Spring Framework,Spring Boot还是Spring Cloud等等框架,它们源码中以及示例中都充斥着各种重要的注解,比如下面Spring MVC中@Controller注解和Spring Boot中@SpringBootApplication注解。...

    不管是在Spring Framework,Spring Boot还是Spring Cloud等等框架,它们源码中以及示例中都充斥着各种重要的注解,比如下面的Spring MVC中的@Controller注解和Spring Boot中的@SpringBootApplication注解。你是否有好奇它们是什么意思?怎么组成的?怎么工作的?如果回答是肯定的,那么下面将和我一起去解开它们神秘的面纱,并且创建一个自己的注解然后验证它。

    首先,明确一点,元注解的含义是,用于标识注解的注解。

    package org.springframework.stereotype;
    
    ...
    
    //Spring MVC重要的@Controller注解
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Controller {
    
      ...
    }
    package org.springframework.boot.autoconfigure;
    
    ...
    
    // Spring Boot中重要的注解
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {
      
        ...
    
    }

    不管是Spring还是其他基于java的框架的注解,都是基于java基础包java.lang.annotation下的6个注解,分别是@Document@Inherited@Native(不属于元注解)@Repeatable@Retention以及@Target注解。同时,为了辅助元注解更好的工作,还提供了ElementTypeRetentionPolicy枚举类以及所有注释类的公共接口Annotation。

    1. @Target注解

    这个注解用来指示被标识的注解的作用域。而辅助枚举类ElementType提供了作用域的枚举值。

    package java.lang.annotation;
    
    //用于指定注解的作用域
    public enum ElementType {
        TYPE,  //用于类,接口(包括注解类型),或者枚举类声明
        FIELD, //用于变量,包括枚举常量声明
        METHOD, //用于方法声明
        PARAMETER, //用于正式的参数声明
        CONSTRUCTOR, //用于构造器声明
        LOCAL_VARIABLE, //用于本地变量声明
        ANNOTATION_TYPE, //用于注解类型声明
        PACKAGE, //用于包声明
        TYPE_PARAMETER, //用于类型参数声明,自java 1.8开始提供
        TYPE_USE, //可以用于所有类型声明的地方,自java1.8开始提供
        MODULE //用于模块声明,自java 9开始提供
    }

    例如,上面的@SpringBootApplication中的@Target(ElementType.TYPE),指的是该注解用于类声明上。

    2. @Retention注解

    用于指示被标识的注解的生命周期。而辅助枚举类RetentionPolicy提供了生命周期的枚举值。

    package java.lang.annotation;
    
    
    public enum RetentionPolicy {
    
        SOURCE, // 在源码中,但将在编译期被丢弃
    
        CLASS, // 在源码以及编译器存在,即在class文件中可见,但将在运行期被丢弃,这个是默认的注解生命周期策略
    
        RUNTIME // 在源码,编译器,运行期都存在,并会被加载进入虚拟机,可以利用反射获取该注解。
    }

    例如,上面@SpringBootApplication中的@Retention(RetentionPolicy.RUNTIME),指的是被标识的注解存在于源码,编译期以及运行期,并可利用反射获取被标识的注解。

    3. @Documented注解

     用于指示被标识的注解是否会包含在javadoc中。包含@Ducumented的注解例子如下,

    @Document注解示例源码
    @Document注解源码示例

    则生成的javadoc中的HelloService中也会包含@Comment注解,如下图,

    含有@Document注解的HelloService的javadoc

     

       去掉上图源码中@Comment中的@Documented注解,则HelloService的javadoc如下,

    无@Documented注解的HelloService的javadoc

     

    4. @Native注解(不属于元注解)

    是Java 1.8新添加的注解,用于指示被标识的常量可以被本地代码(native code)引用。如下代码,为java提供的Long类,

    package java.lang;
    
    ...
    
    public final class Long extends Number implements Comparable<Long> {
    
        @Native public static final long MIN_VALUE = 0x8000000000000000L; //最小的long类型常量
    
        @Native public static final long MAX_VALUE = 0x7fffffffffffffffL; // 最大的long类型常量
    
        ...
    
    }

    5. @Repeatable注解

    Java 1.8新添加的注解,用于指示被标识的注解可以重复地标识一个目标。它的源码如下,

    package java.lang.annotation;
    
    /**
     * 该注解用于指示它标识的注解类型可以重复使用。@Repeatable的值指示重复标识注解的注解容器。
     */
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Repeatable {
        /**
         * 指示被标识注解的注解容器
         */
        Class<? extends Annotation> value();
    }

    通过文字描述是挺费解的,我的理解是,@Repeatable注解的value指示的是,被表示注解的注解容器,例如下面图示,@Languages为@Language的注解容器。不过,还需要实际的例子来验证我们的想法。

    下面通过全栈开发Java开发所掌握的语言(Language)来举例,示例整体如下,注解@Language指示开发者拥有的语言技能,注解@Languages是注解@Language的注解容器。但是,这里有一个疑问,当一个开发者只有一个语言技能,那么@Language将放在哪里?是否还是会放在@Languages容器内呢?

    通过下面的main函数的第13,21,29行来验证我们的猜想,解答我们的疑问。A.class.isAnnotationPresent(AN.class)方法用于校验A类中是否存在AN类注解。

    下面是main函数的输出,

    全栈开发掌握的语言:
    Java
    Vue
    Shell
    Java开发掌握的语言(Language):
    Java

    通过输出结果知道,第13行判断为trueFullStackDeveloper的注解@Language已经放到@Languages内的value容器中;第21行判断为false,则JavaDeveloper的注解@Language未放到@Languages中的value容器中。说明,当@Language标识为1个则不会放到@Languages的value容器中,当重复次数大于等于2时,才会放到@Languages的value容器中。

     

    6. @Inherited注解

    自Java 1.5起提供该注解,用于指示一个注解类型是否会被继承。同样,举一个例子如下,若是第8判断结果为true,则说明FrontendDeveloper通过继承VueDeveloper继承到了@Language注解。

    控制台打印结果如下,说明FrontendDeveloper继承到了@Language注解。

    前端开发掌握的语言:
    Vue

    如果注释掉@Inherited,则控制台没有打印任何信息。则FrontendDeveloper没有继承到@Language注解。

     

    至此,java的java.lang.annotation包下的主要注解以及辅助枚举类都已经解释,并且通过了自定义注解以及运行样例进行想法验证。希望本篇博客能够帮助到有需要的人。

    展开全文
  • spring-AOP注解源码分析

    2019-08-26 10:45:58
    后面为了提高自己的代码质量,也学会了注解的编写,不过一直没有去到底是怎么实现的,这回总算弥补上年轻时代的缺憾了。 2.代理实例化的流程 2.1 ProxyFactory 代理工厂 代理工厂类继承于ProxyCreatorSupport...
  • 它到底是个什么,我们来看看源码的第一段注解怎么说明完第一段,总结下AQS是一个同步基础框架,基于一个先进先出队列。锁机制基于一个状态值,它是原子值。AQS子类负责定义与操作这个状态值,但必须通过...
  • 申明:本文不是讲解Spring如何使用注解,本文只是通过一个简单的实现,来理解Spring是如何...笔者没有过Spring的源码。只能从自己的角度来谈谈Spring是怎么实现的。感兴趣的同学可以在过本文之后,深入的了解Spr...
  • 吵着要学AI小伙伴,可以看看这些牛批开源项目 再整理几个开源项目:练手/毕设/私活都不愁了 C/C++领域练手开源项目,小伙伴们接好 5个值得学习和练手企业级开源项目! 互联网开发十大顶级开源项目巡礼! 推荐...
  • 它到底是个什么,我们来看看源码的第一段注解怎么说明完第一段,总结下 AQS是一个同步基础框架,基于一个先进先出队列。 锁机制基于一个状态值,它是原子值。 AQS子类负责定义与操作...
  • 最近起spring源码,突然想知道没有web.xml配置,spring是怎么通过一个继承于AbstractAnnotationConfigDispatcherServletInitializer类来启动自己。鉴于能力有限以及第一次看源码和发博客,不到之处请望谅~我...
  • 在学习Spring 框架时,Bean初始化用到了反射,在破坏单例模式时也用到了反射,在获取标注的注解时也会用到反射······当然了,反射在日常开发中,我们没碰到过多少,至少我没怎么用过。但面试是造火箭现场,...
  • 今日背景 :red_apple:一个用于...如何扫描类文件,学习Java注解, Java字节码,动态代理,重新认识接口,一些设计模式,学习使用Git ,渐进明白了单元测试本质等。了自己看法。慢慢我发现我居然能得明白Sprin
  • 它到底是个什么,我们来看看源码的第一段注解怎么说明完第一段,总结下AQS是一个同步基础框架,基于一个先进先出队列。锁机制基于一个状态值,它是原子值。AQS子类负责定义与操作这个状态值,但必须通过...
  • 注解我们经常会用到,或者在jdk源码中也会看到,例如: @Deprecated 以及我们在spring或者springboot中经常用到@Controller、@Service、@Repository、@...先下自定义注解的模板:(模板后面有解释) import java
  • 它到底是个什么,我们来看看源码的第一段注解怎么说明 完第一段,总结下 AQS是一个同步基础框架,基于一个先进先出队列。 锁机制基于一个状态值,它是原子值。 ...
  • java开发中对于json数据中时间格式化一直都是程序员头疼问题,这里作者使用 @JsonComponent是怎么处理日期。另外还可以根据时区动态展示不同时间,你会了吗?学习目标快速学会通过注解@JsonComponent自定义日期...
  • 介绍原理前我们来看看怎么用。后面介绍其原理。我们实现这么一个小功能:根据不同环境,实例化不同bean。 springboot通常都是通过-Dspring.profiles.active=dev来区分环境,如果我们想实现线上代码逻辑与...
  •  Annotation比起xml是各有优势,Annotation用起来就是简洁,少了大量配置文件。配置文件则很容易就看出类...这里讲解spring在用Annotation时候,是怎么读取Annotation,又像IOC中注入BeanDefinition。...
  • [移动办公]修复了IOS通讯录搜索框按钮不到问题 [移动办公]修复了android sdk环境升级,developtools、Android support等官方库升级问题 [移动办公]修复了Android流程启动身份选择样式问题 [移动办公]修复了...
  • Vert.x构建服务间消息发送系统(这个目前可以用ZeroMQ方便替换,以便再看看能力,留着吧) Zookeeper分布式发现与注册 Springboot非web方式启动,注解编程,方便嫁接Spring系列和其他第三方框架 Kryo,...
  • 流程图模块主要分拆成这几个模块...也了点源码,也没得出确切答案我猜想是:main函数运行每次使用种子都是相同,而通过接口调用时种子更新了源码的注解起来run main函数时JDK不认为是a call疑問2为什...
  • 接触java一年多,一直在研究js,最近闲下来准备看看spring基本原理,目前先研究下自定义注解 spring有个转发dispatchServlet,所有请求都是由它转发,那么它是怎么工作呢。。我没看源码,自己想了一种...
  • 注解(Annotation)

    2019-02-19 23:22:52
    不管是JDK中源码还是自己开发项目,都会用注解java5引入概念,那么先来详细了解下什么是注解,有什么分类。怎么用? 参考资料 https://www.cnblogs.com/xdp-gacl/p/3622275.html 什么是注解 注解可以...
  • 我想你也会像我一样有这种感觉:Spring的注解太多了,这些注解怎么这么厉害?....其实支撑整个庞大Spring框架背后是Java的基本知识:反射、设计模式等等。其实注解本身没有什么用,是程序在运行时赋予了它意义,.....
  • 反射是一个非常重要知识点,在学习Spring 框架时,Bean初始化用到了反射,在破坏单例模式时也用到了反射,在获取标注的注解时也会用到反射······ 当然了,反射在日常开发中,我们没碰到过多少,至少我没...
  • java大厂面试题整理

    2020-11-24 14:02:01
    是否过hashMap的源码?底层实现是怎么样子的 答: 说了1.8底层实现是数组+单链表/红黑树,接着又问红黑树的特性是什么?怎么实现的 c. 反射了解么?能说下注解的原理么? 答: 了解,反射类似于预加载,捕获异常...

空空如也

空空如也

1 2 3 4
收藏数 62
精华内容 24
关键字:

java注解的源码怎么看

java 订阅