java反射一定要和注解一起使用吗

java反射一定要和注解一起使用吗,第1张

不是一定,看你要做什么,配合注解使用可以更加灵活。比如你做个动态代理,需要用注解吗?当然不需要。再比如说像spring的自动装配,我们可以指定它的扫描的路径,但是这个指定的路径下的bean都需要自动装配吗?答案是否定的,那怎么办?这个时候就需要注解了,通过注解让开发人员自己决定哪些需要容器管理哪些不需要,不知道你是否已经接触过spring。但是总结下来就是反射是不一定要和注解一起使用的。

1概念

IOC(控制反转)是一种设计思想,旨在实现调用类和实现类的松耦合,调用类只依赖接口,在编译阶段并不知道具体使用哪个实现类,而是将这个控制权交给第三方容器,在运行时由容器将实例变量(具体的实现类)注入到对象中(调用类)。

DI(依赖注入)是具体的实现技术,是指由第三方组件负责将实例变量(实现类)传入到对象(调用类)中去。

2IoC的两种设计模式

21 反射:在运行状态中,根据提供的类的路径或类名,通过反射来动态地获取该类所有属性的方法

22 工厂模式:把IoC容器当作一个工厂,在配置文件或注解中给出定义,然后利用反射技术,根据给出的类名生成相应的对象

DI的两种注入方式

31构造器注入

• 构造器注入是在Bean构造过程中执行的

32 Setter注入

• Setter注入是在Bean示例创建完毕之后执行

总结

Spring引入Autowire(自动装配)机制就是为了解决<bean>标签下<property>标签或<constructor-arg>标签过多的问题

对于强依赖,可使用构造器注入,对于弱依赖,推荐使用Setter注入

51什么是反射

JAVA中反射是动态获取信息以及动态调用对象方法的一种反射机制。

Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。而这也是Java被视为动态语言的一个关键性质。

Java反射的功能是在运行时判断任意一个对象所属的类,在运行时构造任意一个类的对象,在运行时判断任意一个类所具有的成员变量和方法,在运行时调用任意一个对象的方法,生成动态代理。

反射的用途

当我们在使用 IDE(如 Eclipse\IDEA)时,当我们输入一个队长或者类并向调用它的属性和方法时,一按 (“”)点号,编译器就会自动列出她的属性或方法,这里就会用到反

专门讲反射的书……java reflection in action 没有中文版。中文版讲这个的应该是没有的。

注解不需要专门一本书的,想要看看的话,thinking in java 中有讲解注解的这一章。第4版。

这是一个Annotation

Annotation接口的实现类: Documented, Inherited, Retention, Target 都是用来定义自己定义的Annotation类的。

1 注解(Annotation)类,以@interface 修饰 ,不能显示(explicit)extends或implements任何类

如:

java 代码

public @interface DefineAnnotation {

}

这种没有任何属性的Annotation类,也叫标识Annotation

2 定义属性

java 代码

//属性必须加个小括号

public String value() ;

//有默认值的属性

public String value() default "aaa";

完整定义如下:

java 代码

//注解Annotation类不能显示(explicit)extends或implements任何类

//不定义任何属性就叫maket annotation

public @interface DefineAnnotation {

//定义一个属性,有属性的话,必须赋值,除非有默认default

public String value() default "aaa";

}

3使用Annotation,有默认值的可以不用传参数,也可以传递参数。没有默认值的,必须传递参数。

如:

java 代码

public class TestAnnotation {

// @DefineAnnotation 有默认值的第一种使用方式

// @DefineAnnotation() 有默认值的第二种使用方式

@DefineAnnotation("ttitfly")

public void say(){

Systemoutprintln("say hello");

}

public static void main(String[] args){

TestAnnotation ta = new TestAnnotation();

tasay();

}

}

4 Retention (保存)

所有的Annotation类都实现了Annotation接口

@Retention本身就是个Annotation(注解)类

它的值是个enum枚举类型的RetentionPolicy,该枚举类型RetentionPolicy有三个值RUNTIME (会被JVM加载,并可以通过反射来获得到Annotation类的信息) ,CLASS (不会被JVM加载),Source

@Retention的值标识自己定义的Annotation(注解)类 是属于哪种保存策略,将来哪个类如果使用了这个自定义的注解类,将会使用这种保存策略

如:

java 代码

import javalangannotationRetention;

import javalangannotationRetentionPolicy;

//所有的Annotation类都实现了Annotation接口

//@Retention本身就是个Annotation(注解)类

//它的值是个enum枚举类型的RetentionPolicy,该枚举类型RetentionPolicy有三个值RUNTIME (会被JVM加载,并可以通过反射来获得到Annotation类的信息) ,CLASS (不会被JVM加载),Source

//@Retention的值标识自己定义的Annotation(注解)类 是属于哪种保存策略,将来哪个类如果使用了这个自定义的注解类,将会使用这种保存策略

@Retention(RetentionPolicyRUNTIME)

public @interface MyAnnotation {

String hello() default "ttitfly";

String world();

}

java 代码

//使用自己定义的Annotation类

public class MyTest {

//一个方法可以有多个注解类

@Deprecated

@MyAnnotation(hello="china",world="earth")

public void say(){

Systemoutprintln("say hello");

}

}

java 代码

import javalangannotationAnnotation;

import javalangreflectMethod;

由于时间关系我也没有写全,这里提供一个思路吧。代码如下:

Accountjava:

@Data

public class Account {

private int id;

private String name;

// @PowerfulAnnotation注解是我臆想的

@PowerfulAnnotation("tokenid")

private String tokenId;

@PowerfulAnnotation("tokenkey")

private String key;

}

PowerfulAnnotationjava:

@Target(ElementTypeFIELD)

@Retention(RetentionPolicyRUNTIME)

@Documented

public @interface PowerfulAnnotation {

String value() default "";

}

测试类Mainjava:

public class Main {

public static void main(String[] args) throws Exception {

Account account = new Account();

String ori = "{\n" +

"\"id\": 11111,\n" +

"\"name\": \"小李\",\n" +

"\"token\": {\n" +

"\"id\": 22222222,\n" +

"\"key\": \"ddddddddd\"\n" +

"}\n" +

"}";

Gson gson = new Gson();

//字符串json转JsonObject

JsonObject jsonObject = gsonfromJson(ori, JsonObjectclass);

//反射获取目标对象属性

for (Field field : accountgetClass()getDeclaredFields()) {

String fieldName = fieldgetName();

Class fieldClass = fieldgetType();

Systemoutprint("当前field名:[" + fieldName + "],");

Systemoutprintln("当前field类型:[" + fieldClass + "]");

Annotation annotation = fieldgetDeclaredAnnotation(PowerfulAnnotationclass);

//检查是否有PowerfulAnnotation注解

if (annotation != null) {

PowerfulAnnotation powerful = (PowerfulAnnotation) annotation;

String powerfulValue = powerfulvalue();

Systemoutprintln("发现PowerfulAnnotation注解,值为:[" + powerfulValue + "]");

String[] tmp = powerfulValuesplit("\\");

//声明一个临时JsonObject,将用于获取下一层json对象

JsonObject tmpJson = jsonObject;

for (int i = 0; i < tmplength; i++) {

//目标值是在powerfulValue的最后一个字段,例如powerfulValue为tokenid的话,目标的值就是id,所以先获取token这个jsonObject,并赋值给临时tmpJson

if (i != tmplength - 1) {

tmpJson = jsonObjectget(tmp[i])getAsJsonObject();

} else {

//到达powerfulValue的最后一个字段,检查其类型,并赋值给目标对象

Object value = checkFieldType(tmpJson, tmp[i], fieldClass);

//从目标对象中获取目标属性

Field targetField = accountgetClass()getDeclaredField(fieldgetName());

targetFieldsetAccessible(true);//解除私有限制

Systemoutprintln("将[" + powerfulValue + "]的值[" + value + "]赋给目标对象的[" + fieldName + "]");

//将值赋值给目标属性

targetFieldset(account, value);

}

}

}

//属性上没有PowerfulAnnotation注解

else {

//检查当前属性的类型

Object value = checkFieldType(jsonObject, fieldName, fieldClass);

//从目标对象中获取目标属性

Field targetField = accountgetClass()getDeclaredField(fieldgetName());

targetFieldsetAccessible(true);//解除私有限制

Systemoutprintln("直接将值[" + value + "]赋给目标对象的[" + fieldName + "]");

//将值赋值给目标属性

targetFieldset(account, value);

}

Systemoutprintln("\n");

}

Systemoutprintln("目标对象最终值:" + account);

}

/

检查当前属性的类型

(这里由于时间关系,我没有写全,只检查了String、int、boolean类型,全类型应包括boolean、char、byte、short、int、long、float、double,你有时间自己补充一下)

如果发现当前属性是一个对象,那么应该将JsonObject转换成对应的对象再返回(由于时间关系,这里我也没有试过,总之思路是这样)

/

private static Object checkFieldType(JsonObject field, String fieldName, Class fieldClass) {

if (fieldClass == Stringclass) {

return fieldget(fieldName)getAsString();

}

if (fieldClass == intclass) {

return fieldget(fieldName)getAsInt();

}

if (fieldClass == booleanclass) {

return fieldget(fieldName)getAsBoolean();

}

return new Gson()fromJson(fieldget(fieldName), fieldClass);

}

}

代码还没写完,主要集中在没有对JsonArray进行处理,当json串里包含数组时会报错,另外一些没写完的我在注释里写了点,你可以参照一下。整体思路还是利用java反射机制进行。

以上代码运行结果:

对于局部变量的注解只能在源码级别上进行处理,类文件并不描述局部变量。因此,所有的局部变量注解在编译完一个类的时候就会被遗弃掉。同样地,对包的注解不能在源码级别之外存在。

在文件package-infojava中注解一个包,该文件只包含以注解先导的包声明

包注解需要在特定文件:package-infojava,中添加,获取如下:

Package pkg = TestAnnotationclassgetPackage();

boolean hasAnnotation=pkgisAnnotationPresent(Xpcclass);

if (hasAnnotation){

    Xpc xpc=pkggetAnnotation(Xpcclass);

    Systemoutprintln("package:" + pkggetName());

    Systemoutprintln("id=" + xpcid());

    Systemoutprintln("name=" + xpcname());

    Systemoutprintln("gid=" + xpcgid());

}

因为一次在做项目的时候需要扫描接口的信息,其中包括参数名,遇到了点障碍就想着把这个解决方案和问题讲一下。

我们要查看的方法如下

java18以后,官方提供了反射的方法能获取到接口的参数名称。示例如下。其中getParameters方法是18才开始提供的。并且需要在javac编译时,加上-parameters参数才行。

通过javap -p -v可以查看class的字节码,如下

其中MethodParameters就是18后在字节码中记录参数名的地方。但是18之前是怎么实现的呢?

spring中有个ParameterNameDiscoverer接口,他有6个实现类。如下:

Aspect开头的都是对增强类的信息获取。我用不到。

PrioritizedParameterNameDiscoverer是一个链表,就是记录一系列的Discoverer。

这个Discoverer就是封装了JDK18的getParameters

这个类是重点,它通过asm获取了class文件的LocalVariableTable信息。class,字节码如下:

其中有一行字节码记录了LocalVariableTable信息,LocalVariableTable里不仅保存了参数名,还保存了其他局部变量信息。spring通过slot来判定哪些是参数以及参数的顺序。

但是LocalVariableTable不是类的必须信息,所以不是编译后必须存在的。只有在javac时-g或-g:vars时,才会保存LocalVariableTable信息。

在idea工具中,我们可以通过如下方式,关闭编译时,自动生成LocalVariableTable来尝试查看字节码。

这个Discoverer就是在18时多添加了个StandardReflectionParameterNameDiscoverer。

在ParameterNameDiscoverer接口上有这么段注释:

它告诉我们,不是任何时候都能获取到参数名的,只能尝试去获取。

当我们关闭了class debug信息,并且将编译级别设置为16时,启动一个简单的spring boot项目。在idea中关闭 *** 作如下:

controller如下:

我们会发现这时候访问该接口传递info参数会报如下错误:

所以,spring mvc中也是有可能获取不到方法参数名的。如果我们需要使用spring mvc的话,最好通过Require等注解来绑定。

以上就是关于java反射一定要和注解一起使用吗全部的内容,包括:java反射一定要和注解一起使用吗、IoC/DI的理解、麻烦大家给我推荐一些介绍Java反射、内省、注解这类内容的书等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/9740426.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-01
下一篇 2023-05-01

发表评论

登录后才能评论

评论列表(0条)

保存