spring中的注解原理,spring常见注解作用
00-1010前言1、内置(通用)批注1.1 @ over rode 1.2 @ request mapping 1.3 @ request body 1.4 @ get mapping 1.5 @ path variable 1.6 @ request param 1.7 @ components can 1.8 @ component 1.9 @ service 1.10 @ repository II。元注释@目标@保留@文档化@继承III。自定义注释四。反射机制概述4.1动态语言和静态语言4.2Java反射(Java,理解类并获取类实例5.1类实例5.2获取类实例5.3获取类对象的类型6、类加载和ClassLoader6.1加载过程6.2类加载器7、获取运行时类的完整对象8、通过反射获取泛型信息9、通过反射获取注释信息。
00-1010注释不是程序,但是可以解释程序,可以被其他程序(比如编译器)读取。
注释的格式:以@ comment的名称存在于代码中,也可以添加一些参数值如@ suppress warnings(value= unchecked )。
注释可以用于包、类、方法、字段等。它们的作用是给它们添加额外的辅助信息,使它们可以通过反射机制被访问。
目录
00-1010表示方法旨在覆盖超类中的方法声明,这将覆盖或实现超类中声明的方法。
前言
@RequestMapping注释主要用于将Web请求与请求处理类中的方法进行映射。请注意,它具有以下属性:
由值3360或其别名值3360或其别名参数3360映射的请求URL根据HTTP参数的存在、默认值或值来过滤请求
一、内置(常用)注解
@RequestBody用于请求处理方法的参数列表中。它可以将请求体中的参数绑定到一个对象。请求体参数通过HttpMessageConverter传递,根据请求体中的参数名匹配对象的属性名并绑定值。此外,还可以通过@Valid注释检查请求体中的参数。
1.1@Overrode
@GetMapping注释用于处理HTTP GET请求,并将请求映射到特定的处理方法。具体来说,@GetMapping是一个组合标注,相当于@ RequestMapping的快捷方式(method=requestmethod.get)。
1.2@RequestMapping
@PathVariable注释是将方法中的参数绑定到请求URI中的模板变量。您可以通过@RequestMapping注释指定URI的模板变量,然后使用@PathVariable注释将方法中的参数绑定到模板变量。
1.3@RequestBody
@RequestParam注释用于将方法的参数与Web请求交付的参数绑定在一起。使用@RequestParam可以方便地访问HTTP请求参数的值。
1.4@GetMapping
@ComponentScan批注用于配置Spring需要扫描的组件批注所批注的类的包。通过配置包的basePackages属性或value属性,可以配置要扫描的包路径。value属性是basePackages的别名。
1.5@PathVariable
@Component注释用于注释一个普通的组件类。它没有明确的业务范围,只是通知Spring需要将带注释的类包含在Spring Bean容器中并进行管理。
1.6@RequestParam
@Service标注是@Component的扩展(特例),用于标注业务逻辑类。像@Component注释一样,这个注释标记的类将由Spring自动管理。
1.7@ComponentScan
@Repository注释也是@Component注释的扩展。和@Component注释一样,这个注释所标记的类会被Spring自动管理。@Repository注释用于标记DAO层的数据持久化类。
00-1010四个元音符如下
:@Target、@Retention、@Documented、@Inherited 。
再次强调下元注解是java API提供,是专门用来定义注解的注解。
@Target
描述注解能够作用的位置,ElementType取值:
ElementType.TYPE,可以作用于类上ElementType.METHOD,可以作用于方法上ElementType.FIELD,可以作用在成员变量上
@Retention
表示需要在什么级别保存该注释信息(生命周期):
RetentionPolicy.RUNTIME
:内存中的字节码,VM将在运行时也保留注解,因此可以通过反射机制读取注解的信息
@Documented
描述注解是否被抽取到api文档中。
@Inherited
描述注解是否被子类继承。
三、自定义注解
学习自定义注解对于理解Spring框架十分有好处,即使在实际项目中可能不需要使用自定义注解,但可以帮助我们掌握Spring的一些底层原理,从而提高对整体项目的把握。
/** * 自定义注解 * @author Created by zhuzqc on 2022/5/31 23:03 */public class CustomAnnotation { /** * 注解中可以为参数赋值,如果没有默认值,那么必须为注解的参数赋值 * */ @MyAnnotation(value = "解释") public void test(){ }}/** * @author zhuzqc *///自定义注解必须的元注解target,指明注解的作用域(此处指明的是在类和方法上起作用)@Target({ElementType.TYPE,ElementType.METHOD})//元注解retention声明该注解在何时起作用(此处指明的是在运行时起作用)@Retention(RetentionPolicy.RUNTIME)@interface MyAnnotation{ //注解中需声明参数,格式为:参数类型 + 参数名(); String value() default "";}
四、反射机制概述
4.1动态语言与静态语言
4.1.1动态语言
是一种在运行时可以改变其结构的语言,例如新的函数、对象甚至代码可以被引进,已有的函数可以被删除或是进行其它结构上的变化。
主要的动态语言有:Object-C、C#、PHP、Python、JavaScript 等。
以 JavaScript 语言举例:
/** * 由于未指定var的具体类型,函数在运行时间可以改变var的类型 * */function f(){ var x = "var a = 3; var b = 5; alert(a+b)"; eval(x)}
4.2.2静态语言
与动态语言相对的、运行时结构不可变的语言就是静态语言,如 Java、C、C++ 等。
Java 不是动态语言,但 Java 可以称为准动态语言。即 Java 有一定的动态性,可以利用反射机制获得类似于动态语言的特性,从而使得 Java 语言在编程时更加灵活。
4.2Java Reflection(Java 反射)
Reflection(反射)是 Java 被视为准动态语言的关键:反射机制允许程序在执行期间借助 Reflection API 获取任何类的内部信息,并能直接操作任意对象的内部属性及方法。
Class c = Class.forName("java.lang.String")
加载完类后,在堆内存的方法区就产生了一个Class类型的对象(一个类只有一个Class对象),这个类就包含了完整的类的结构信息。我没可以通过这个对象,像镜子一样看到类的结构,这个过程形象地被称之为反射。
通过代码更易于理解:
/** * 反射的概念 * @author Created by zhuzqc on 2022/6/1 17:40 */public class ReflectionTest extends Object{ public static void main(String[] args) throws ClassNotFoundException { //通过反射获取类的Class对象 Class c = Class.forName("com.dcone.zhuzqc.demo.User"); //一个类在内存中只有唯一个Class对象 System.out.println(c.hashCode()); }}/** * 定义一个实体类entity * */@Dataclass User{ private String userName; private Long userId; private Date loginTime;}
由于该类继承 Object,在 Object 类中有 getClass() 方法,该方法被所有子类继承:
@HotSpotIntrinsicCandidatepublic final native Class<?> getClass();
注:该方法的返回值类型是一个 Class 类,该类是 Java 反射的源头。
反射的优点:运行期类型的判断、动态加载类、提高代码灵活度。
4.2.1反射机制主要功能
在运行时判断、调用任意一个类的对象信息(成员变量和方法等);在运行时获取泛型信息;在运行时处理注解;生成动态代理。4.2.2主要API
java.lang.Class:代表一个类java.lang.reflect.Field:代表类的成员变量java.lang.reflect.Method:代表类的方法java.lang.reflect.Constructor:代表类的构造器
五、理解Class类并获取Class实例
5.1Class类
前面提到,反射后可以得到某个类的属性、方法和构造器、实现的接口。
对于每个类而言,JRE都为其保留一个不变的 Class 类型的对象;一个加载的类在 JVM 中只会有一个 Class 实例;Class 类是Reflection的根源,想要通过反射获得任何动态加载的、运行的类,都必须先获取相应的 Class 对象。
5.2获取Class类实例
有以下5种方式可以获取Class类的实例:
1.若已知具体的类,可以通过类的class属性获取,该fang'shi最为安全可靠,且程序性能最高。
//类的class属性Class classOne = User.class;
2. 已知某个类的实例,通过调用该实例的getClass方法获取Class对象。
//已有类对象的getClass方法 Class collatz = user.getClass();
3.已知一个类的全类名,且该类在类路径下,可以通过静态方法forName()获取。
Class c = Class.forName("com.dcone.zhuzqc.demo.User");
4.内置基本数据类型可以直接使用类名.Type获取。
//内置对象才有的TYPE属性,较大的局限性Class<Integer> type = Integer.TYPE;
5.利用ClassLoader(类加载器)获取。
5.3可获得Class对象的类型
1.class:外部类、成员(成员内部类,静态内部类),局部内部类,匿名内部类;
//类可以反射 Class c1 = Person.class;
2.interface:所有接口;
//接口可以反射 Class c2 = Comparable.class;
3.[]:数组;
//数组可以反射 Class c3 = String[].class; Class c4 = int[][].class;
4.enum:枚举;
//枚举可以反射 Class c6 = ElementType.class;
5.annotation:注解(@interface);
//注解可以反射 Class c5 = Data.class;
6.基本数据类型;
//基本数据类型(包装类)可以反射 Class c7 = int.class; Class c8 = Integer.class;
7.void。
//void可以反射 Class c9 = void.class;
六、类的加载与ClassLoader
6.1类的加载过程
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下3个步骤来对该类进行初始化。
1.类的加载(Load):将类的 class 文件字节码内容读入内存,并将这些静态数据转换成方法区运行时的数据结构,同时创建一个java.lang.Class对象,此过程由类加载器完成;
2.类的链接(Link):将类的二进制数据合并到 JRE 中,确保加载的类信息符合 JVM 规范,同时 JVM 将常量池内的引用替换为地址。
3.类的初始化(Initialize):JVM 负责对类进行初始化,分为类的主动引用和被动引用。
类的主动引用
虚拟器启动时,先初始化main方法所在的类;new 类的对象;调用类的静态(static)成员和静态(static)方法;使用java.lang.reflect包的方法对类进行反射调用;如果该类的父类没有被初始化,则会先初始化它的父类。类的被动引用
当访问到一个静态域时,只有真正声明这个域的类才会被初始化;通过数组定义类的引用,不会触发此类的初始化;引用常量不会触发此类的初始化
6.2类加载器
JVM支持两种类型的类加载器,分别为引导类加载器(BootstrapClassLoader)和自定义类加载器(User-Defined ClassLoader)。
从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类,类加载器。
但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器。
无论类加载器的类型如何划分,在程序中我们最常见的类加载器始终只有3个,具体如下图所示:
类加载器
所以具体为引导类加载器(BootstrapClassLoader)和自定义类加载器(包括ExtensionClassLoader、Application ClassLoader(也叫System ClassLoader)、User Defined ClassLoader)。
public class Test03 { public static void main(String[] args) { //获取系统类的加载器 ClassLoader sysLoader = ClassLoader.getSystemClassLoader(); System.out.println(sysLoader); //获取系统类的父类加载器 ClassLoader parent = sysLoader.getParent(); System.out.println(parent); }}
七、获取运行时类的完整对象
通过反射获取运行时类的完整结构:Field、Method、Constructor、Superless、Interface、Annotation等。
即:实现的全部接口、所继承的父类、全部的构造器、全部的方法、全部的成员变量(局部变量)、注解等。
/** * @author Created by zhuzqc on 2022/6/5 0:16 */public class Test04 { public static void main(String[] args) throws ClassNotFoundException { Class c1 = Class.forName("com.dcone.zhuzqc.demo.User"); //获取所有属性 Field field[]; field = c1.getDeclaredFields(); for (Field f:field){ System.out.println(f); } //获得类的方法 Method method[]; method = c1.getDeclaredMethods(); for (Method m:method){ System.out.println(m); } }}
八、反射获取泛型信息
Java 中采用泛型擦除的机制来引入泛型,Java 中的泛型仅仅是给编译器 javac 使用的,目的是确保数据的安全性以及免去强制类型转换的问题。一旦编译完成,所有和泛型相关的类型全部擦除。
在Java中可以通过反射获取泛型信息的场景有如下三个:
(1)成员变量的泛型(2)方法参数的泛型(3)方法返回值的泛型在Java中不可以通过反射获取泛型信息的场景有如下两个:
(1)类或接口声明的泛型(2)局部变量的泛型要获取泛型信息,必须要注意ParameterizedType类,该类中的getActualTypeArguments()方法可以有效获取泛型信息。
下面以获取成员方法参数的泛型类型信息为例:
public class Demo { public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException { // 获取成员方法参数的泛型类型信息 getMethodParametricGeneric(); }
/** * 获取方法参数的泛型类型信息 * * @throws NoSuchMethodException */ public static void getMethodParametricGeneric() throws NoSuchMethodException { // 获取MyTestClass类中名为"setList"的方法 Method setListMethod = MyClass.class.getMethod("setList", List.class); // 获取该方法的参数类型信息(带有泛型) Type[] genericParameterTypes = setListMethod.getGenericParameterTypes(); // 但我们实际上需要获取返回值类型中的泛型信息,所以要进一步判断,即判断获取的返回值类型是否是参数化类型ParameterizedType for (Type genericParameterType : genericParameterTypes) { ParameterizedType parameterizedType = (ParameterizedType) genericParameterType; // 获取成员方法参数的泛型类型信息 Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { Class realType = (Class) actualTypeArgument; System.out.println("成员方法参数的泛型信息:" + realType); } } }
九、反射获取注解信息
在开发中可能会遇到这样的场景:获取类的属性释义,这些释义定义在类属性的注解中。
/** * 定义一个实体类entity * */@Dataclass User{ @ApiModelProperty(value = "姓名") private String userName; @ApiModelProperty(value = "用户id") private Long userId; @ApiModelProperty(value = "登录时间") private Date loginTime;}
那么可以如何获取注解中的属性信息呢?
解决方案:
这里我们使用反射,以及java.lang下的两个方法:
//如果指定类型的注释存在于此元素上, 方法返回true java.lang.Package.isAnnotationPresent(Class<? extends Annotation> annotationClass) //如果是该类型的注释, 方法返回该元素的该类型的注释java.lang.Package.getAnnotation(Class< A > annotationClass)
public static void main(String[] args) throws ClassNotFoundException { Class c1 = Class.forName("com.dcone.zhuzqc.demo.User"); if(User.class.isAnnotationPresent(ApiModel.class)){ System.out.println(User.class.getAnnotation(ApiModel.class).value()); } // 获取类变量注解 Field[] fields = User.class.getDeclaredFields(); for (Field f : fields) { if(f.isAnnotationPresent(ApiModelProperty.class)){ System.out.print(f.getAnnotation(ApiModelProperty.class).name() + ","); } } }
拓展1:获取方法上的注解
@Bean("sqlSessionFactory") public String test(@RequestBody User user) throws ClassNotFoundException { Class c2 = Class.forName("com.dcone.zhuzqc.demo.User"); // 获取方法注解: Method[] methods = User.class.getDeclaredMethods(); for(Method m : methods){ if (m.isAnnotationPresent((Class<? extends Annotation>) User.class)) { System.out.println(m.getAnnotation(ApiModelProperty.class).annotationType()); } } return "test"; }
拓展2:获取方法参数上的注解
@Bean("sqlSessionFactory") public String test(@RequestBody User user) throws ClassNotFoundException { Class c2 = Class.forName("com.dcone.zhuzqc.demo.User"); // 获取方法参数注解 Method[] methods2 = User.class.getDeclaredMethods(); for (Method m : methods2) { // 获取方法的所有参数 Parameter[] parameters = m.getParameters(); for (Parameter p : parameters) { // 判断是否存在注解 if (p.isAnnotationPresent(ApiModelProperty.class)) { System.out.println(p.getAnnotation(ApiModelProperty.class).name()); } } } return "test"; }
以上就是一文搞懂Spring中的注解与反射的详细内容,更多关于Spring注解 反射的资料请关注盛行IT其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。