首页 » PHP教程 » php注解扫描技巧_进阶Spring中的注解与反射

php注解扫描技巧_进阶Spring中的注解与反射

访客 2024-12-07 0

扫一扫用手机浏览

文章目录 [+]

表明的格式:以@注释名在代码中存在,还可以添加一些参数值例如@SuppressWarnings(value="unchecked")。

表明可在package、class、method、field等上面利用,浸染是为它们添加了额外的赞助信息,从而可以通过反射机制实现对这些元数据的访问。

php注解扫描技巧_进阶Spring中的注解与反射

一、内置(常用)表明1.1@Overrode

表示某方法旨在覆盖超类中的方法声明,该方法将覆盖或实现在超类中声明的方法。

php注解扫描技巧_进阶Spring中的注解与反射
(图片来自网络侵删)
1.2@RequestMapping

@RequestMapping表明的紧张用场是将Web要求与要求处理类中的方法进行映射,把稳有以下几个属性:

value:映射的要求URL或者其别名value:映射的要求URL或者其别名params:根据HTTP参数的存在、缺省或值对要求进行过滤1.3@RequestBody

@RequestBody在处理要求方法的参数列表中利用,它可以将要求主体中的参数绑定到一个工具中,要求主体参数是通过HttpMessageConverter通报的,根据要求主体中的参数名与工具的属性名进行匹配并绑定值。
此外,还可以通过@Valid表明对要求主体中的参数进行校验。

1.4@GetMapping

@GetMapping表明用于处理HTTP GET要求,并将要求映射到详细的处理方法中。
详细来说,@GetMapping是一个组合表明,它相称于是@RequestMapping(method=RequestMethod.GET)的快捷办法。

1.5@PathVariable

@PathVariable表明是将方法中的参数绑定到要求URI中的模板变量上。
可以通过@RequestMapping表明来指定URI的模板变量,然后利用@PathVariable表明将方法中的参数绑定到模板变量上。

1.6@RequestParam

@RequestParam表明用于将方法的参数与Web要求的通报的参数进行绑定。
利用@RequestParam可以轻松的访问HTTP要求参数的值。

1.7@ComponentScan

@ComponentScan表明用于配置Spring须要扫描的被组件表明注释的类所在的包。
可以通过配置其basePackages属性或者value属性来配置须要扫描的包路径。
value属性是basePackages的别名。

1.8@Component

@Component表明用于标注一个普通的组件类,它没有明确的业务范围,只是关照Spring被此表明的类须要被纳入到Spring Bean容器中并进行管理。

1.9@Service

@Service表明是@Component的一个延伸(特例),它用于标注业务逻辑类。
与@Component表明一样,被此表明标注的类,会自动被Spring所管理。

1.10@Repository

@Repository表明也是@Component表明的延伸,与@Component表明一样,被此表明标注的类会被Spring自动管理起来,@Repository表明用于标注DAO层的数据持久化类。

二、元表明

4个元个元表明分别是:@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紧张APIjava.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类的实例:

若已知详细的类,可以通过类的class属性获取,该fang'shi最为安全可靠,且程序性能最高。
//类的class属性 Class classOne = User.class;

2. 已知某个类的实例,通过调用该实例的getClass方法获取Class工具。
```java //已有类工具的getClass方法 Class collatz = user.getClass();
已知一个类的全类名,且该类在类路径下,可以通过静态方法forName()获取。
Class c = Class.forName("com.dcone.zhuzqc.demo.User");内置基本数据类型可以直策应用类名.Type获取。
//内置工具才有的TYPE属性,较大的局限性 Class<Integer> type = Integer.TYPE;利用ClassLoader(类加载器)获取。
5.3可得到Class工具的类型class:外部类、成员(成员内部类,静态内部类),局部内部类,匿名内部类;//类可以反射 Class c1 = Person.class;interface:所有接口;//接口可以反射 Class c2 = Comparable.class;[]:数组;//数组可以反射 Class c3 = String[].class; Class c4 = int[][].class;enum:列举;//列举可以反射 Class c6 = ElementType.class;annotation:表明(@interface);//表明可以反射 Class c5 = Data.class;基本数据类型;//基本数据类型(包装类)可以反射 Class c7 = int.class; Class c8 = Integer.class;void。
//void可以反射 Class c9 = void.class;六、类的加载与ClassLoader6.1类的加载过程

当程序主动利用某个类时,如果该类还未被加载到内存中,则系统会通过如下3个步骤来对该类进行初始化。

类的加载(Load):将类的 class 文件字节码内容读入内存,并将这些静态数据转换成方法区运行时的数据构造,同时创建一个java.lang.Class工具,此过程由类加载器完成;类的链接(Link):将类的二进制数据合并到 JRE 中,确保加载的类信息符合 JVM 规范,同时 JVM 将常量池内的引用更换为地址。
类的初始化(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"; } JAVA 复制 全屏

分类: Java后端开拓

标签:

相关文章