当前位置: 首页 > news >正文

弹幕怎么做视频网站济南公司网站建设公司哪家好

弹幕怎么做视频网站,济南公司网站建设公司哪家好,网站解除域名绑定,国产crm系统文章目录 一、Spring自定义注解1、什么是注解#x1f468;‍#x1f3eb;2、注解的目的或作用#x1f49e;3、JDK内置注解#x1f4ab; 【内置元注解 一共八个固定注解】4、元注解 #x1f3af;5、自定义注解#x1f4f8;5、Java反射API和类加载过程51、什么是反射基本原… 文章目录 一、Spring自定义注解1、什么是注解‍2、注解的目的或作用3、JDK内置注解 【内置元注解 一共八个固定注解】4、元注解 5、自定义注解5、Java反射API和类加载过程51、什么是反射基本原理52、反射的应用场景有哪些53、API方法1、获得Class对象2、从 Class 中获取信息 54、应用实例参考 6、自定义注解实战‍ 二、AOP切面开发2.1 为什么用AOP编程2.2 AOP的基础术语2.3 常见的AOP五种通知2.4 实际运用环绕通知AOP参考 三、【事务踩坑812场景】Spring声明事务编程事务最好Transaction参考 首先了解基本的自定义注解(配置项参考网址) 切面(配置项参考) 切面由浅入深 具体的Transaction详解 一、Spring自定义注解 第 1-5 小节均偏向于理论知识若只是想要了解如何自定义注解和如何应用注解请跳转至第6小节开始阅读。 在本篇中主要是针对注解的概念和运行时注解进行解释说明附带有三个实战的案例尽可能的让大家能够理解透彻并且能够加以应用。 1、什么是注解‍ Java 注解(Annotation)用于为 Java 代码提供元数据。作为元数据注解不直接影响你的代码执行但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。–官方文档 1.1、注解 Annotion(注解)是一个接口程序可以通过反射来获取指定程序元素的Annotion对象然后通过Annotion对象来获取注解里面的元数据。 我们常常使用的注解Data、Controller等等这些都是注解创建一个注解也很简单创建一个类然后将class改为 interface就是一个注解啦。 1.2、注解出现的位置 Java代码中的包、类型、构造方法、方法、成员变量、参数、本地变量的声明都可以用注解来修饰。注解本质上可以看作是一种特殊的标记程序在编译或者运行时可以检测到这些标记而进行一些特殊的处理。 1.3、关于注解的处理 我们一般将利用反射来处理注解的方式称之为运行时注解。 另外一种则是编译时注解如我们常常使用的 lombok 里的注解Data它能够帮我们省略set/get方法我们在Class上加上这个注解后在编译的时候lombok其实是修改了.class文件的将set/get方法放进去了不然的话你可以看看编译完后的.class文件。诸如这种我们常称为编译时注解也就是使用javac处理注解。 –图来自于极客学院 这幅图就是从.java文件到class文件的再到class文件被 JVM 加载的过程。 而其中的注解抽象语法树这一阶段就是去解析注解然后根据定义的注解处理器进行相关的逻辑处理。 这一块不是我的关注点略过略过啦朋友们好奇可以去研究研究噢 2、注解的目的或作用 生成文档。这是最常见的也是 Java 最早提供的注解。如param、return等等跟踪代码依赖性实现替代配置文件功能。**作用就是减少配置如 Spring中Bean的装载注入而且现在的框架基本上都是使用注解来减少配置文件的数量同时这样也使得编程更加简洁代码更加清晰。在编译时进行格式检查。**如Override放在方法前如果你这个方法并不是覆盖了超类方法则编译时就能检查出标识作用。**当Java编译时或运行时检测到这里的注解做什么的处理自定义注解一般如此。 携带信息。 注解的成员提供了程序元素的关联信息Annotation 的成员在 Annotation类型中以无参数的方法的形式被声明。其方法名和返回值定义了该成员的名字和类型。在此有一个特定的默认 语法允许声明任何Annotation成员的默认值。一个Annotation可以将namevalue对作为没有定义默认值的Annotation 成员的值当然也可以使用namevalue对来覆盖其它成员默认值。这一点有些近似类的继承特性父类的构造函数可以作为子类的默认构造函数但是也 可以被子类覆盖。 这么一大段话其实就是关于注解中成员的解释。 说了这么多其实一句话也能表达完。 注解就是一张便利贴它贴在那里你看到的那一刻就明白该做什么事啦。 如出门前门上贴着一张便利贴上面写着出门记得带钥匙当你看到的那一刻你就会去检查一下自己是否带钥匙啦。 在Java中也是一样的你定义了一个注解注解上可以写一些东西然后你再将它贴在某个上面说明白执行规则当编译到这里的时候需要干嘛干嘛又或者是当运行到这里的时候需要干嘛干嘛。 因为注解写的东西的不同或者是处理注解的规则不同而产生了不同的注解及作用。 3、JDK内置注解 【内置元注解 一共八个固定注解】 Java中 内置的注解有5类具体包括 Deprecated过时注解用于标记已过时 被抛弃的元素类、方法等 Override复写注解用于标记该方法需要被子类复写 SuppressWarnings阻止警告注解用于标记的元素会阻止编译器发出警告提醒 SafeVarargs参数安全类型注解用于提醒开发者不要用参数做不安全的操作 阻止编译器产生 unchecked警告Java 1.7 后引入 4、元注解 何为元注解就是注解的注解就是给你自己定义的注解添加注解你自己定义了一个注解但你想要你的注解有什么样的功能此时就需要用元注解对你的注解进行说明了。 接着上一个比喻 注解有很多很多吗门上贴一个冰箱上贴一个书桌上贴一个等等 元注解勒就是把他们整合起来称呼的像上面这些可以统称为生活类注解啊。所以也就是注解的注解。 4.1、Target 在 Target 注解中指定的每一个 ElementType 就是一个约束它告诉编译器这 个自定义的注解只能用于指定的类型。 说明了注解所修饰的对象范围注解可被用于 packages、types类、接口、枚举、Annotation类型、类型成员方法、构造方法、成员变量、枚举值、方法参数和本地变量如循环变量、catch参数。 4.2、Retention 定义了该注解的生命周期 某些注解仅出现在源代码中而被编译器丢弃 源码级 而另一些却被编译在class文件中 字节码级 编译在class文件中的注解可能会被虚拟机忽略而另一些在class被装载时将被读取请注意并不影响class的执行因为注解与class在使用上是被分离的。绝大多数开发者都是使用RUNTIME因为我们期望在程序运行时能够获取到这些注解并干点有意思的事儿而只有RetentionPolicy.RUNTIME能确保自定义的注解在运行时依然可见。运行级 使用这个元注解可以对自定义注解的“生命周期”进行限制。 RetentionPolicy.SOURCE 一般开发者很少用到大都是Java内置的注解。如Override Target(ElementType.METHOD) Retention(RetentionPolicy.SOURCE) public interface Override { }Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) Retention(RetentionPolicy.SOURCE) public interface SuppressWarnings { }这些注解只是在编译的时候用到一旦编译完成后运行时没有任何意义所以他们被称作源码级别注解。 如果有了解过 lombok 一些简单原理的开发者 都知道它是通过注解在编译时自动生成一部分代码让源码看起来更简洁字节码却很强大。 当然这种方式有它自身的缺陷譬如不一致性问题排解时的困扰以及依赖问题不是本篇重点扯回来。 提供信息给编译器 编译器可以利用注解来检测出错误或者警告信息打印出日志。 编译阶段时的处理 软件工具可以用来利用注解信息来自动生成代码、文档或者做其它相应的自动处理。 运行时处理 某些注解可以在程序运行的时候接受代码的提取自动做相应的操作。 4.3、Documented 用于描述其它类型的annotation应该被作为被标注的程序成员的公共API因此可以被例如 javadoc此类的工具文档化。是一个标记注解没有成员。 4.4、Inherited 是一个标记注解阐述了某个被标注的类型是被继承的。使用了Inherited修饰的注解类型被用于一个class时该class的子类也有了该注解。 4.5、Repeatable 允许一个注解可以被使用一次或者多次Java 8。 5、自定义注解 自定义注解实际上就是一种类型而已,也就是引用类型Java中除了8种基本类型之外,我们见到的任何类型都是引用类型 5.1、定义注解 自定义注解过程 声明一个类MyAnnotation 把class关键字改为interface 这样我们就声明了一个自定义的注解当我们用interface声明一个注解的时候实际上是声明了一个接口这个接口自动的继承了java.lang.annotation.Annotation但是我们只需要interface这个关键字来声明注解编译器会自动的完成相关的操作不需要我们手动的指明继承Annotation接口 另外在定义注解时不能再继承其他的注解或接口。 我举了四个例子这四个注解分别是放在 类接口、枚举类上、构造函数、方法级别、成员属性上的。 Documented //定义可以被文档工具文档化 Retention(RetentionPolicy.RUNTIME)//声明周期为runtime运行时可以通过反射拿到 Target(ElementType.TYPE)//注解修饰范围为类、接口、枚举 public interface ClassAnnotation {public String name() default defaultService;public String version() default 1.1.0; }Documented Target(ElementType.CONSTRUCTOR) Retention(RetentionPolicy.RUNTIME) public interface ConstructorAnnotatin {String constructorName() default ;String remark() default 构造器; }Documented Target(ElementType.FIELD) Retention(RetentionPolicy.RUNTIME) public interface FieldAnnotation {public String name() default defaultName;public String value() default defaultValue; }DocumentedRetention(RetentionPolicy.RUNTIME)Target(ElementType.METHOD)public interface MethodAnnotation {public String name() default defaultName;public MethodTypeEnum type() default MethodTypeEnum.TYPE1;}public enum MethodTypeEnum {TYPE1,TYPE2 }5.2、注解的成员变量 成员以无参数无异常的方式声明 String constructorName() default “”; 可以使用default为成员指定一个默认值 public String name() default “defaultName”; 成员类型是受限的合法的类型包括原始类型以及String、Class、Annotation、Enumeration JAVA的基本数据类型有8种byte(字节)、short(短整型)、int(整数型)、long(长整型)、float(单精度浮点数类型)、double(双精度浮点数类型)、char(字符类型)、boolean(布尔类型 public MethodTypeEnum type() default MethodTypeEnum.TYPE1; 注解类可以没有成员没有成员的注解称为标识注解例如JDK注解中的Override、Deprecation 如果注解只有一个成员并且把成员取名为value()则在使用时可以忽略成员名和赋值号“” 例如JDK注解的SuppviseWarnings 如果成员名 不为value则使用时需指明成员名和赋值号 5.3、使用注解 因为我们在注解中声明了属性,所以在使用注解的时候必须要指明属性值 ,多个属性之间没有顺序,多个属性之间通过逗号分隔 ClassAnnotation(name personBean, version 1.2.1) public class Person {// 告诉大家是可以用的,但是影响我测试,我就又注释掉了. // ConstructorAnnotatin(constructorNamePerson()) // public Person(String description) { // this.description description; // }FieldAnnotation(name description, value This is my personal annotation)private String description;public String getDescription() {return description;}public void setDescription(String description) {this.description description;}MethodAnnotation(name sayHello, type MethodTypeEnum.TYPE2)public void sayHello() {System.out.println(Hello Annotation!);} }5、Java反射API和类加载过程 类的三个主要特性是它的字段变量、方法和构造函数。为了描述和访问对象这三个特性在反射API中由单独的类表示java.lang.reflect.Fieldjava.lang.reflect.Method以及java.lang.reflect.Constructor。我们可以通过class对象查找类的这些成员。 51、什么是反射基本原理 Java反射API位于java.lang.reflect包。顾名思义反射是类或对象检查自身的能力。反射允许Java代码查看对象更准确地说对象的类并确定其结构。在安全管理器所施加的限制内您可以找出一个类有哪些构造函数、方法和字段以及它们的属性。 在运行状态中对于任意一个类都能够知道这个类中的所有属性和方法对于任意一个对象都能够调用它的任意一个方法和属性这种动态获取的信息以及动态调用对象的方法的功能称为java的反射机制。 Class加载过程· Java类加载主要分为这么几个阶段 编译阶段 将.java源文件编译成.class字节码文件。 运行阶段 当JVM第一次加载某一个用到的类时会使用ClassLoader类加载器加.class文件加载到内存中。连接-验证阶段对class进行验证 头文件元数据等。连接-准备阶段收集字节码中的静态属性与静态代码块并把静态属性赋值默认值。连接-解析阶段解析阶段主要是把符号引用转换为地址应用。初始化初始化阶段主要是运行class的静态代码块内容。 所有阶段都运行完成之后此时JVM会把二进制的class文件加载到方法区同时创建一个Class对象放入堆区。这个时候仅仅是完成了类的加载过程之后才是创建对象。 52、反射的应用场景有哪些 在运行时判断任意一个对象所属的类。 在运行时构造任意一个类的对象。 在运行时判断任意一个类所具有的成员变量和方法. 在运行时调用任意一个对象的方法。 生成动态代理。 Spring 框架中的反射机制主要是通过 Java 的反射 API 实现的它允许在运行时动态地加载、检查、创建、访问和修改类、对象、属性和方法等信息。 在 Spring 中反射机制常常用于实现依赖注入、AOP、事务管理等功能。具体来说通过反射机制可以实现以下几个方面的功能 实例化对象通过 Class 对象的 newInstance() 方法可以创建一个类的实例对象。 获取类信息通过 Class 对象的 getMethods()、getFields()、getConstructors() 等方法可以获取类的属性、方法、构造函数等信息。 调用方法通过 Method 对象的 invoke() 方法可以调用方法。 访问属性通过 Field 对象的 get()、set() 方法可以访问和修改对象的属性。 在 Spring 中反射机制常用于实现依赖注入。例如通过读取 XML 配置文件中的 bean 定义信息可以获取类的全限定名、构造函数参数、属性值等信息然后使用反射机制动态地创建类的实例对象并将其注入到其他对象中。 另外在 Spring AOP 中也常常用到反射机制。例如在基于方法的 AOP 中通过获取目标对象的 Method 对象可以在方法执行前后进行一些增强操作。 总之Spring 框架中的反射机制为实现各种高级特性提供了基础支持也为开发人员提供了方便的工具和技术。 53、API方法 1、获得Class对象 每个类被加载之后系统就会为该类生成一个对应的Class对象。通过该Class对象就可以访问到JVM中的这个类。 在Java程序中获得Class对象通常有如下三种方式 使用 Class 类的forName(String clazzName)静态方法。该方法需要传入字符串参数该字符串参数的值是某个类的全限定名必须添加完整包名。 调用某个类的class属性来获取该类对应的 Class 对象。 调用某个对象的getClass()方法。该方法是java.lang.Object类中的一个方法。 //第一种方式 通过Class类的静态方法——forName()来实现 class1 Class.forName(com.lvr.reflection.Person); //第二种方式 通过类的class属性 class1 Person.class; //第三种方式 通过对象getClass方法 Person person new Person(); Class? class1 person.getClass();对于方式一和方式二都是直接根据类来取得该类的 Class 对象相比之下方式二有如下的两种优势 代码跟安全。程序在编译阶段就能够检查需要访问的 Class 对象是否存在。 线程性能更好。因为这种方式无须调用方法所以性能更好。 可以通过类的类类型创建该类的对象实例。 Class.newInstance(); // Foot foot (Foot) c1.newInstance();2、从 Class 中获取信息 一旦获得了某个类所对应的Class 对象之后就可以调用 Class 对象的方法来获得该对象的和该类的真实信息了。 1、获取 Class 对应类的成员变量 Field[] getDeclaredFields(); // 获取 Class 对象对应类的所有属性与成员变量的访问权限无关。 Field[] getFields(); // 获取 Class 对象对应类的所有 public 属性。 Field getDeclaredField(String name); // 获取 Class 对象对应类的指定名称的属性与成员变量的访问权限无关。 Field getField(String name); // 获取 Class 对象对应类的指定名称的 public 属性。 2、获取 Class 对应类的方法 Method[] getDeclaredMethods(); // 获取 Class 对象对应类的所有声明方法于方法的访问权限无关。 Method[] getMethods(); // 获取 Class 对象对应类的所有 public 方法包括父类的方法。 Method getMethod(String name, Class?...parameterTypes); // 返回此 Class 对象对应类的、带指定形参列表的 public 方法。 Method getDeclaredMethod(String name, Class?…parameterTypes); // 返回此 Class 对象对应类的、带指定形参列表的方法与方法的访问权限无关。 3、获取 Class 对应类的构造函数 Constructor?[] getDeclaredConstructors(); // 获取 Class 对象对应类的所有声明构造函数于构造函数的访问权限无关。 Constructor?[] getConstructors(); // 获取 Class 对象对应类的所有 public 构造函数。 Constructor getDeclaredConstructor(Class?...parameterTypes); // 返回此 Class 对象对应类的、带指定形参列表的构造函数与构造函数的访问权限无关。 Constructor getConstructor(Class?…parameterTypes); // 返回此 Class 对象对应类的、带指定形参列表的 public 构造函数。 4、获取 Class 对应类的 Annotation注释 A getAnnotation(Class annotationClass); // 尝试获取该 Class 对对象对应类存在的、指定类型的 Annotation如果该类型的注解不存在则返回 null。 A getDeclaredAnnotation(Class annotationClass); // 这是Java8新增的方法该方法尝试获取直接修饰该 Class 对象对应类、指定类型的Annotation如果该类型的注解不存在则返回 null。 Annotation[] getAnnotations(); // 返回修饰该 Class 对象对应类存在的所有Annotation。 Annotation[] getDeclaredAnnotations(); // 返回直接修饰该 Class 对应类的所有Annotation。 A[] getAnnotationsByType(Class annotationClass); // 获取修饰该类的、指定类型的多个Annotation。 A[] getDeclaredAnnotationsByType(Class annotationClass); // 获取直接修饰该类的、指定类型的多个Annotation。 5、获取 Class 对应类的内部类 Class?[] getDeclaredClasses(); // 返回该 Class 对象对应类包含的全部内部类。 6、获取 Class 对应类的外部类 Class? getDeclaringClass(); // 返回该 Class 对象对应类所在的外部类。 7、获取 Class 对应类所实现的接口 Class?[] getInterfaces(); 8、获取 Class 对应类所继承的父类 Class? super T getSuperClass(); 9、获取 Class 对应类的修饰符、所在包、类名等基本信息 int getModifiers(); // 返回此类或接口的所有修饰符。修饰符由 public、protected、private、final、static、abstract 等对应的常量组成返回的整数应使用 Modifier 工具类的方法来解码才可以获取真实的修饰符。 Package getPackage() // 获取该类的包。 String getName() // 以字符串的形式返回此 Class 对象所表示的类的名称。 String getSimpleName() // 以字符串的形式返回此 Class 对象所表示的类的简称。 10、判断该类是否为接口、枚举、注解类型等 boolean isAnnotation() // 返此 Class 对象是否是一个注解类型由interface定义。 boolean isAnnotationPresent(Class? extends Annotation annotationClass) // 判断此 Class 对象是否使用了Annotation修饰。 boolean isAnonymousClass() // 此 Class 对象是否是一个匿名类。 boolean isArray() //此 Class 对象是否是一个数组。 boolean isEnum() // 此 Class 对象是否是一个枚举由 enum 关键字定义。 boolean isInterface() // 此 Class 对象是否是一个接口由 interface 关键字定义。 boolean isInstance(Object obj) // 判断 obj 是否是此 Class 对象的实例。该方法完全可以替代 instanceof 操作符。 54、应用实例 获取Class对象的几种方式 //一、获取class的几种方式Class? cls1 Class.forName(com.example.module01.reflect.Human); //常用于动态的加载某个class例如从配置文件获取ClassHuman cls2 Human.class;//常用于调用某个需要class类型参数的方法时的参数传递Class? extends Human cls3 new Human().getClass();//常用于在明确类型的情况下获取classClass? cls4 RefTest01.class.getClassLoader().loadClass(com.example.module01.reflect.Human);//常用于动态System.out.println(cls1 cls2 ? cls1 cls3 ? cls1 cls4 : 不相等 : 不相等); //trueConstructor类API // 2.1 构造方法相关ClassStudent clazz Student.class;Constructor?[] declaredConstructors clazz.getDeclaredConstructors();System.out.println(声明的构造方法个数 declaredConstructors.length);Constructor?[] constructors clazz.getConstructors();//获取所有的构造方法for (Constructor? constructor : constructors) {Class? declaringClass constructor.getDeclaringClass();//获取此构造方法的声明类Class?[] parameterTypes constructor.getParameterTypes();//获取不带泛型的构造方法Type[] genericParameterTypes constructor.getGenericParameterTypes();//获取带泛型的构造方法Object instance null; // instance constructor.newInstance();//调用newInstanceSystem.out.println(构造方法的声明位置 declaringClass ,构造方法参数个数 parameterTypes.length ,带泛型的构造方法参数个数 genericParameterTypes.length 创建的对象 instance);}Method API //2.2 method方法API Method[] methods clazz.getMethods(); //可以获取所有的公开的方法包括本类的和继承的 for (Method method : methods) {System.out.println(公开的方法 method.getName() method.getDeclaringClass()); } Method[] declaredMethods clazz.getDeclaredMethods();//获取声明在本类中的所有方法包括静态方法 for (Method declaredMethod : declaredMethods) {declaredMethod.setAccessible(true);//破解private权限declaredMethod.invoke(clazz.newInstance());//反射执行方法System.out.println(本类所有方法 declaredMethod); }System.out.println(Field);Field API //2.3 属性相关Field[] fields clazz.getFields();for (Field field : fields) {System.out.println(公开的属性 field.getName());}for (Field declaredField : clazz.getDeclaredFields()) {Student student clazz.newInstance();declaredField.setAccessible(true);//设置可访问declaredField.set(student,1);//给属性赋值Object value declaredField.get(student);System.out.println(本类的所有属性 declaredField.getName() val value);}获取父类 接口API相关信息通过获取superClass给继承的父类 属性赋值//2.4获取父类、接口信息Class? super Student superclass clazz.getSuperclass();Class?[] interfaces clazz.getInterfaces();System.out.println(父类是 superclass); //Object类的superClass是null。System.out.println(本类实现的接口 interfaces.length);//2.5 实现Spring自动注入效果将父类的protected类型属性也自动注入。ClassStudent stuClass Student.class;//通过反射创建对象Student instance stuClass.newInstance();//获取本类的方法for (Field field : stuClass.getDeclaredFields()) {//开启访问权限field.setAccessible(true);//动态赋值field.set(instance,111);}//继续获取父类的class对象Class? super Student superClass stuClass.getSuperclass();//继续获取父类的成员属性Field[] declaredFields superClass.getFields();for (Field declaredField : declaredFields) {//给父类属性赋值declaredField.set(instance,222);}//打印输出System.out.println(instance);参考 Java–反射机制一——反射 APIJava反射API详解Java反射-反射API、类加载过程 5.4、浅提一下反射【重点】 想要去获取注解就不得不提到反射啦但 Java 反射会带来一定的耗时因此使用运行注解需要考虑对性能的影响。 我们声明一个Student类用来描述学生对象的信息的 class Student{String name;String school;//...set/get }当我们创建一个学生对象时学生对象的信息是保存在 Student 类中所以 Student 类会提供获取这些信息的方法。 在Java类中每个类都会有对应的Class要想执行反射操作必须先要获取指定类名的Class 了解Class对象 类是程序的一部分每个类都有一个 Class 对象。换言之每当我们编写并且编译 了一个新类就会产生一个 Class 对象更恰当的说是被保存在一个同名的 .class 文件中。为了生成这个类的对象Java 虚拟机 (JVM) 先会调用 “类加载器” 子系统把 这个类加载到内存中。 Class类简单说就是用来描述类对象的信息的 类对象的信息包括 类的基本信息包名、修饰符、类名、基类实现的接口 属性的信息修饰符、属性类型、属性名称、属性值 方法的信息修饰符、返回类型、方法名称、参数列表、抛出的异常 构造方法的信息修饰符、类名、参数列表、抛出的异常 注解的相关信息 因为类对象的相关信息全部保存在Class类 所以Class类会提供获取这些信息的方法 一旦某个类的 Class 对象被载入内存它就可以用来创建这个类的所有对象。 通过 Class 获取类的相关信息通过Class创建对象通过 Class 调用对象上面的属性调用对象上面的方法这种操作就称为反射要想执行反射操作必须先要获取到指定的类名的 Class 获取Class的不同方式 获取基本类型的Class 1基本类型Class如 int.Class获取的就是 int 类型的 Class 获取引用类型的Class 1引用类型的Class如String.Class获取的就是String类对应的Class 2通过对象来获取如String objhelloClass calz obj.getClass()获取的就是String类对应的Class 3Class.forName(java.lang.String)获取的就是对应的Class5.5、提取注解 public class TestClassAnnotation {private static Person person new Person();public static void main(String[] args) {Class? clazz person.getClass();//因为注解是作用于类上面的所以可以通过isAnnotationPresent来判断是否是一个具有指定注解的类if (clazz.isAnnotationPresent(ClassAnnotation.class)) {System.out.println(This is a class with annotation ClassAnnotation!);//通过getAnnotation可以获取注解对象ClassAnnotation annotation clazz.getAnnotation(ClassAnnotation.class);if (null ! annotation) {System.out.println(BeanName annotation.name());System.out.println(BeanVersion annotation.version());} else {System.out.println(the annotation that we get is null);}} else {System.out.println(This is not the class that with ClassAnnotation);}} }This is a class with annotation ClassAnnotation! BeanName personBean BeanVersion 1.2.1scss复制代码public class AnnotationTest {public static void main(String[] args) throws ClassNotFoundException {Class? clazz Class.forName(com.nzc.my_annotation.shang.Person);System.out.println(类注解解析);printClassAnno(clazz);System.out.println(成员变量注解解析);printFieldAnno(clazz);System.out.println(成员方法注解解析);printMethodAnno(clazz);System.out.println(构造器注解解析);printConstructorAnno(clazz);}/*** 打印类的注解*/private static void printClassAnno(Class? clazz) throws ClassNotFoundException {//判断是否有AuthorAnnotatin注解if(clazz.isAnnotationPresent(ClassAnnotation.class)) {//获取AuthorAnnotatin类型的注解ClassAnnotation annotation clazz.getAnnotation(ClassAnnotation.class);System.out.println(annotation.name()\tannotation.version());}}/*** 打印成员变量的注解*/private static void printFieldAnno(Class? clazz) throws ClassNotFoundException {Field[] fields clazz.getDeclaredFields();for (Field field : fields) {if(field.isAnnotationPresent(FieldAnnotation.class)) {FieldAnnotation annotation field.getAnnotation(FieldAnnotation.class);System.out.println(annotation.name()\tannotation.value());}}}/*** 打印成员变量的注解*/private static void printMethodAnno(Class? clazz) throws ClassNotFoundException {Method[] methods clazz.getDeclaredMethods();for (Method method : methods) {if(method.isAnnotationPresent(MethodAnnotation.class)) {MethodAnnotation annotation method.getAnnotation(MethodAnnotation.class);System.out.println(annotation.name()\tannotation.type());}}}/*** 打印成员变量的注解*/private static void printConstructorAnno(Class? clazz) throws ClassNotFoundException {Constructor?[] constructors clazz.getDeclaredConstructors();for (Constructor? constructor : constructors) {if(constructor.isAnnotationPresent(ConstructorAnnotatin.class)) {ConstructorAnnotatin annotation constructor.getAnnotation(ConstructorAnnotatin.class);System.out.println(annotation.constructorName()\tannotation.remark());}}System.out.println(无);}}ini复制代码类注解解析 personBean 1.2.1 成员变量注解解析 description This is my personal annotation 成员方法注解解析 sayHello TYPE2 构造器注解解析 无6、自定义注解实战‍ 注解大多时候与反射或者 AOP 切面结合使用它的作用有很多比如标记和检查最重要的一点就是简化代码降低耦合性提高执行效率。 6.1、自定义注解 SpringMVC 拦截器实现权限控制功能 还有一种应用场景权限判断或者说是登录校验。 这个是我当时还没有学习市面上的权限框架就是使用了这种自定义注解拦截器的方式来实现简单的权限控制。 注意此案例不可CV直接运行代码很容易实现大家理解思路即可。 定义注解 Target({ElementType.METHOD,ElementType.TYPE}) // 这个注解可以放在也可以放在方法上的。 Retention(RetentionPolicy.RUNTIME) public interface Authority {Role[] roles() ; }arduino复制代码public enum Role {SADMIN,ADMIN,TEACHER,STUDENT }使用注解 Authority(roles {Role.ADMIN, Role.SADMIN}) // 放在类上 说明这个类下所有的方法都需要有这个权限才可以进行访问 RestController RequestMapping(/admin) public class AdminController {GetMapping(/hello)public String Hello(){return hello 你最近还好吗;} }Controller RequestMapping(/student) public class StudentController {Authority(roles {Role.STUDENT}) // 放在方法上则说明此方法需要注解上的权限才能进行访问GetMapping(/test)public String test(){return 你好我已经不是一名学生啦;}}编写 SpringMVC 拦截器及处理注解的Handler 在其中进行 Token 的判断和访问方法的权限判断看方法上是否有注解有的话 就和当前用户对比成功就可以访问失败就直接拒绝。 当时用的是SSM框架所以才会看到有 response.sendRedirect(contextPath “/login”);这样的。 public class LoginInterceptor extends HandlerInterceptorAdapter {private static final Logger log LoggerFactory.getLogger(WebExceptionHandler.class);Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String url request.getRequestURI(); // log.info(request.getMethod() 请求URLurl);//从Token中解析User信息User user TokenUtil.verifyToken(request);String contextPath request.getContextPath();//user 为空则 表示 Token 不存在if (user ! null) {if (user.getRole().equals(sadmin)) {//检查方法上 是否有注解的 Role.SADMIN 或者 Role.ADMIN 权限 , 没有则检查类上有没有 如果符合要求则放行if (HandlerUitl.checkAuthority(handler, new Role[]{Role.SADMIN, Role.ADMIN})) {request.setAttribute(user, user);return true;}}if (user.getRole().equals(admin)) {if (HandlerUitl.checkAuthority(handler, new Role[]{Role.ADMIN})) {request.setAttribute(user, user);return true;}else {response.sendRedirect(contextPath /login);}}if (user.getRole().equals(teacher)) {if (HandlerUitl.checkAuthority(handler, new Role[]{Role.TEACHER})) {return true;} else {response.sendRedirect(contextPath /login);}}if (user.getRole().equals(student)) {if (HandlerUitl.checkAuthority(handler, new Role[]{Role.STUDENT})) {return true;} else {response.sendRedirect(contextPath /student/login);}}}else {response.sendRedirect(contextPath /login);}return false;} }用于检查 方法 或者 类 是否需要权限 并和 拥有的权限做对比 如果方法上有 则以方法的 优先 public class HandlerUitl {public static boolean checkAuthority(Object handler, Role[] roles1){if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod (HandlerMethod) handler;// 获取方法上的注解Authority authority handlerMethod.getMethod().getAnnotation(Authority.class);// 如果方法上的注解为空 则获取类的注解if (authority null) {authority handlerMethod.getMethod().getDeclaringClass().getAnnotation(Authority.class);}// 如果标记了注解则判断权限if (authority ! null) {Role[] roles authority.roles();//如果 方法权限为 0 则通过if(roles.length0){return true;}//判断 拥有的权限 是否 符合 方法所需权限for(int i 0; i roles.length; i){for(int j 0; j roles1.length; j){if(roles[i]roles1[j]){ // System.out.println(可以访问);return true;}}}}return false;}return true;}}6.2、自定义注解AOPRedis 防止重复提交 先简单说一下防止重复提交注解的逻辑 在需要防止重复提交的接口的方法加上注解。 发送请求写接口携带 Token 请求的路径 Token 拼接程 keyvalue 值为生成的 UUID 码 然后 set Redis 分布式锁能获取到就顺利提交分布式锁默认 5 秒过期不能获取就是重复提交了报错。 定义注解 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface NoRepeatSubmit {/*** 设置请求锁定时间* return*/int lockTime() default 5; }定义处理注解的切面类 java复制代码import com.eshop.api.ApiResult; import com.eshop.common.aop.NoRepeatSubmit; import com.eshop.common.util.RedisLock; import com.eshop.common.util.RequestUtils; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.Assert;import javax.servlet.http.HttpServletRequest; import java.util.UUID;/*** 重复提交aop*/ Aspect Component Slf4j public class RepeatSubmitAspect {Autowiredprivate RedisLock redisLock;Pointcut(annotation(noRepeatSubmit))public void pointCut(NoRepeatSubmit noRepeatSubmit) {}Around(pointCut(noRepeatSubmit))public Object around(ProceedingJoinPoint pjp, NoRepeatSubmit noRepeatSubmit) throws Throwable {int lockSeconds noRepeatSubmit.lockTime();HttpServletRequest request RequestUtils.getRequest();Assert.notNull(request, request can not null);String bearerToken request.getHeader(Authorization);String[] tokens bearerToken.split( );String token tokens[1];String path request.getServletPath();String key getKey(token, path);String clientId getClientId();boolean isSuccess redisLock.tryLock(key, clientId, lockSeconds);log.info(tryLock key [{}], clientId [{}], key, clientId);if (isSuccess) {log.info(tryLock success, key [{}], clientId [{}], key, clientId);// 获取锁成功Object result;try {// 执行进程result pjp.proceed();} finally {// 解锁redisLock.releaseLock(key, clientId);log.info(releaseLock success, key [{}], clientId [{}], key, clientId);}return result;} else {// 获取锁失败认为是重复提交的请求log.info(tryLock fail, key [{}], key);return ApiResult.fail(重复请求请稍后再试);}}private String getKey(String token, String path) {return token path;}private String getClientId() {return UUID.randomUUID().toString();}}import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import redis.clients.jedis.Jedis; import redis.clients.jedis.params.SetParams;import java.util.Collections;/*** Redis 分布式锁实现*/ Service public class RedisLock {private static final Long RELEASE_SUCCESS 1L;private static final String LOCK_SUCCESS OK;private static final String SET_IF_NOT_EXIST NX;// 当前设置 过期时间单位, EX seconds; PX millisecondsprivate static final String SET_WITH_EXPIRE_TIME EX;// if get(key) value return del(key)private static final String RELEASE_LOCK_SCRIPT if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end;Autowiredprivate StringRedisTemplate redisTemplate;/*** 该加锁方法仅针对单实例 Redis 可实现分布式加锁* 对于 Redis 集群则无法使用** 支持重复线程安全** param lockKey 加锁键* param clientId 加锁客户端唯一标识(采用UUID)* param seconds 锁过期时间* return*/public boolean tryLock(String lockKey, String clientId, long seconds) {return redisTemplate.execute((RedisCallbackBoolean) redisConnection - {Jedis jedis (Jedis) redisConnection.getNativeConnection();SetParams setParams new SetParams();String result jedis.set(lockKey, clientId, setParams.nx().px(seconds));if (LOCK_SUCCESS.equals(result)) {return true;}return false;});}/*** 与 tryLock 相对应用作释放锁** param lockKey* param clientId* return*/public boolean releaseLock(String lockKey, String clientId) {return redisTemplate.execute((RedisCallbackBoolean) redisConnection - {Jedis jedis (Jedis) redisConnection.getNativeConnection();Object result jedis.eval(RELEASE_LOCK_SCRIPT, Collections.singletonList(lockKey),Collections.singletonList(clientId));if (RELEASE_SUCCESS.equals(result)) {return true;}return false;});} }使用注解 /*** 添加收藏*/ NoRepeatSubmit PostMapping(/collect/add) ApiOperation(value 添加收藏,notes 添加收藏) public ApiResultBoolean collectAdd(Validated RequestBody StoreProductRelationQueryParam param){// 处理业务逻辑return ApiResult.ok(); }本篇主要是针对Java运行时的注解的讲解及应用但是你想一想我们使用lombok的注解时它的实现原理又是什么样的呢为什么可以帮我们自动生成代码呢是谁给我们做了这件事情呢 二、AOP切面开发 结合AspectJ 切入点指示符 自定义注解实现各种 面向切面反射 的去冗余话的业务编程。 类似于 在任何时候都可以按照 切点通知(增强的代码) 面向切面(多个连接点合成的地方) 进行动态代理生成对应 方法执行的编程技巧。 2.1 为什么用AOP编程 为什么使用AOP编程范式 1分离功能需求和非功能需求 2集中处理某一关注点 3侵入性少增强代码可读性及可维护性。 下边记录AOP切面在springboot中的使用。 AOP的应用场景 权限控制、缓存控制、事务控制、分布式追踪、异常处理等 2.2 AOP的基础术语 1Target目标类要被代理的类例如UserService 2JoinPoint连接点所谓的连接点是指那些被拦截到的方法 3PointCut切入点被增强的连接点所谓的增强其实就是添加的新功能 4Advice通知、增强增强代码 5Weaving织入是指把增强的advice应用到目标对象target来创建新的代理对象proxy的 过程。 6proxy代理类 7Aspect切面是切入点pointcut和通知advice的结合。 2.3 常见的AOP五种通知 1前置通知Before在我们执行目标方法之前运行 2后置通知After在我们执行目标方法结束之后不管有没有异常 After(valueexecution(* com.example.aspectJ.demo1.ProductDao.findAll(..))) public void after(){System.out.println(最终通知); }3返回通知AfterReturning在我们的目标方法正常返回值后运行 4异常通知AfterThrowing在我们的目标方法出现异常后运行 5环绕通知Around动态代理需要手动执行jionPoint.process(),其实就是执行我们的目标方法执行之前相当于前置通知执行之后就相当于我们的后置通知 2.4 实际运用环绕通知 Around 环绕通知等于前置通知 返回通知 异常通知 后置通知 环绕通知是通知类行中功能最强大的它是JoinPoint的子接口环绕通知需要携带 ProceedingJoinPoint 类型的参数。 环绕通知的几个重点 1环绕通知类似于动态代理的全过程ProceedingJoinPoint pjp 类型的参数可以决定是否执行目标方法也就是说必须要手动调用 pjp.proceed() 方法目标方法才能执行 如果忘记这样做就会导致通知被执行了 , 但目标方法没有被执行 且让环绕通知必须要有返回值返回值即目标方法的返回值。 2如果环绕通知没有返回值会出现空指针异常的情况。 // ···· 此处省略引入包 Aspect // 标注是一个切面 Component public class Aspact {// 定义一个切入点关于切入点如何定义Pointcut(annotation(com.lxc.springboot.annotation.MyAnnotation))public void pointFn(){}Around(pointFn())public void aroundFn(ProceedingJoinPoint pjp) {String methodName pjp.getSignature().getName();System.out.println(环绕通知执行了);Object result null;try{// 前置通知System.out.println(【目标方法】methodName);// 执行目标方法result pjp.proceed();// 结果通知System.out.println(目标方法返回结果为result);}catch (Throwable e) {// 异常通知System.out.println(e.getMessage());}// 后置通知System.out.println(后置通知执行);} }除了环绕通知和异常通知来看下 前置通知、结果通知和后置通知的执行顺序前置–结果通知–后置通知。 AOP参考 AOP开发入门Spring AOP 所有切入点指示符详解(execution,within,this,target,args,within,target,args,annotation) 3.官方AOP英文文档 三、【事务踩坑812场景】Spring声明事务编程事务最好 JDBC实现事务伪代码 1//Get database connection 2Connection connection DriverManager.getConnection(); 3//Set autoCommit is false 4connection.setAutoCommit(false); 5//use sql to operate database 6......... 7//Commit or rollback 8connection.commit()/connection.rollback 9 10connection.close();需要在各个业务代码中编写代码如commit()、close()来控制事务。 但是Spring不乐意这么干了这样对业务代码侵入性太大了所有就用一个事务注解Transactional来控制事务底层实现是基于切面编程AOP实现的而Spring中实现AOP机制采用的是动态代理具体分为JDK动态代理和CGLIB动态代理两种模式。 Spring的bean的初始化过程中发现方法有Transactional注解就需要对相应的Bean进行代理生成代理对象。然后在方法调用的时候会执行切面的逻辑而这里切面的逻辑中就包含了开启事务、提交事务或者回滚事务等逻辑。 另外注意一点的是Spring 本身不实现事务底层还是依赖于数据库的事务。没有数据库事务的支持Spring事务是不会生效的。 具体过程原理 Transactional实现原理三要素切面、切点、通知 InfrastructureAdvisorAutoProxyCreator后置处理器拦截所有Bean 遍历所有类型为Advisor的切面 返回满足切点条件的切面列表 选择代理方法 生成代理 调用通知的invoke()方法 开启事务调用其它通知的invoke()方法如果没有执行目标方法执行异常回滚事务执行成功提交事务 执行目标方法 了解Transactional注解实现原理不仅可以让我们对切面、切点、通知有一个清晰的认识还可以让我们通过其思想实现类似功能如Cache注解实现应用缓存Async注解实现业务异步执行 接下来我们进入正题看看哪些场景会导致Spring事务失败 注意仅有图中红色的才生效如果不重写注解Transactional(rollbackForException.class)如果方法上加了这个注解那么当这个方法抛出异常时运行时和非运行时就会回滚数据库里面的数据也会回滚。如果不配置rollbackFor属性那么事物只会在遇到运行时异常才会回滚。 默认情况下SPRING只有在抛出的异常为运行时且为unchecked 异常才会回滚事务也就是抛出的异常为RuntimeException 的子类(Errors也会)时才会回滚事务而抛出 checked 异常则不会回滚事务 当然可以通过 Transactional rollbackFor进行配置。 6.1checked异常 一般是指程序不能直接控制的外界情况是指在编译的时候就需要检查的一类异常用户程序中必须采用try-catch机制处理或者通过throws交由调用者来处理。这类异常主要是指除了Error以及RuntimeException及其子类之外的异常。 6.2unchecked异常一般是那些不在编译的时就要处理的一类异常。在JAVA体系里所有的Error以及RuntimeException及其子类都是unchecked异常。 — 总结 事务内部调用EnableAspectJAutoProxy(exposeProxy true)在启动类中添加会由Cglib代理实现。 或者自身使用 proxy ((ServiceA)AopContext.currentProxy()).doSave(user); 自己既想要保留事务tryCatch应对业务异常又想要事务回滚使用 //假设这是一个service类的片段 try{ //出现异常 } catch (Exception e) { // 捕获异常打印异常或其他处理。但不抛出新的异常 e.printStackTrace(); // 手动回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } //此时return语句能够执行 return xxx; 少用Transactional注解将查询(select)方法放到事务外事务中避免远程调用事务中避免一次性处理太多数据非事务执行我们可以看到add方法的访问权限被定义成了private这样会导致事务失效spring要求被代理方法必须是public的。说白了在AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute方法中有个判断如果目标方法不是public则TransactionAttribute返回null即不支持事务。 【注意第一种和self. 自注入方法——是指在方法内部调用另一个事务失效】外部经过spring容器调用service的方法事务才生效service类内部方法间相互调用事务不生效也就是传说中的自调用失效问题。主要原因是 Spring数据库事务的约定,其实现原理是AOP,而AOP的原理是动态代理,在自调用的过程中,是类自身的调用,而不是代理对象去调用,那么就不会产生AOP,这样 Spring就不能把你的代码织入到约定的流程中,于是就产生了现在看到的失败场景。 Transaction参考 1.Transactional 事务注解实现原理 2.你必须懂也可以懂的Transactional原理
http://www.sczhlp.com/news/178150/

相关文章:

  • 详情页在线设计网站推荐云南外贸建站推广
  • 网站建设项目结构分析报告做任务给佣金的网站
  • if 和 else 的用法
  • The 4th Universal Cup
  • 深圳网站建设61916做网站大概要多少
  • 南京建设厅官方网站软件开发专业考研
  • 天山路街道网站建设公司网站建设后期维护
  • 泰安网站设计网页设计需要学什么软件知乎
  • 网站还在建设就已经可以访问了_影响后期百度在线设计培训
  • 北京社区网站建设网站做交叉连接
  • 成都做网站的公司php网站建设系统
  • 网站设计时尚网络宣传的方法渠道
  • 做下载网站微信运营模式
  • 建设银行招聘网站甘肃分行滨州正规网站建设哪家专业
  • 开发板编程软件网站如何进行代码优化
  • asp网站系统html编辑器安卓版 中文
  • 长春火车站附近美食天津高端网站制作
  • 网站如何做备份商城网站项目案例
  • 网站建设 环讯传媒网站优化建设哈尔滨
  • 百度联盟的网站怎么做塘厦仿做网站
  • 多梦主题建设的网站wordpress主题免费分享
  • 微模板网站建设适合个人做的网站
  • 邹城网站建设公司做网站和做app
  • 笔记本电脑可以做网站服务器龙岩seo公司
  • 信用体系建设网站维运工作制度电商网站排名
  • 一级a做爰电影片免费网站千锋教育培训多少钱
  • 给我一个网站贴吧网站rss地址生成
  • 上海建设工程安全监理网站计算机怎么建设网站
  • 做网站一般是什么工作郑州seo推广
  • 网站和app的区别网站建设的七大优缺点