当前位置:主页 > 查看内容

Java反射与内省

发布时间:2021-08-18 00:00| 位朋友查看

简介:Java反射Class Class类的实例表示在运行中的Java应用程序的类和接口。enum是一个类 annotation是一个接口。每一个数组都是一个类 这个类由相同元素的数组和维数所共享。对于基础数据类型boolean、byte、char、short、int、long、float、double和关键字void都……
Java反射Class

Class类的实例表示在运行中的Java应用程序的类和接口。enum是一个类 annotation是一个接口。每一个数组都是一个类 这个类由相同元素的数组和维数所共享。对于基础数据类型boolean、byte、char、short、int、long、float、double和关键字void都代表一个类。

类没有公共的构造函数 那么Java虚拟机加载类的时候会调用defineClass方法来构造。

Bean.getClass.newInstance()方法默认调用无参构造函数初始化对象。如果没有就抛出一个异常。

java.lang.reflect.Constructor.newInstance(Object... param)可以通过带参构造函数初始化对象。

java.lang.reflect包下的三个类Field、Method、Constructor分别描述类的域、方法和构造器。

Field的getType方法用于描述域所属类型的Class对象。

Class类中的getFields、getMethods、getConstructors方法将返回public的域、方法和构造器数组 其中包括超类的public成员。

Class类的getDeclaredFields、getDeclaredMethods、getDeclaredConstructors等Declared方法将返回类中所有的域、方法和构造器数组。包括private和protected成员 但是不包括超类的成员。

setAccessible()方法是AccessibleObject类中的一个方法 它是Field、Method、Constructor的公共超类。

构造函数创建对象
Class ? aClass Class.forName( com.lang.pojo.User 
// 获取所有public的构造函数
Constructor ? [] constructors aClass.getConstructors();
// 获取所有的构造函数 包括private的
Constructor ? [] declaredConstructors aClass.getDeclaredConstructors();
for (Constructor ? declaredConstructor : declaredConstructors) {
 // 设置暴力访问 如果不设置那么private方法就不可以访问
 declaredConstructor.setAccessible(true);
 int parameterCount declaredConstructor.getParameterCount();
 if (parameterCount 0) {
 // 没有参数 调用无参构造函数
 Object o declaredConstructor.newInstance();
 System.out.println(o);
 } else {
 // 可以构建对象 参数为可变参数
 Object o declaredConstructor.newInstance( jack , 10, 美国加州 
 System.out.println(o);
}
获取返回值类型
Class ? aClass Class.forName( com.lang.pojo.User 
// 获取所有public的构造函数
Constructor ? [] constructors aClass.getConstructors();
// 获取所有的构造函数 包括private的
Constructor ? [] declaredConstructors aClass.getDeclaredConstructors();
for (Constructor ? declaredConstructor : declaredConstructors) {
 // 设置暴力访问 如果不设置那么private方法就不可以访问
 declaredConstructor.setAccessible(true);
 // 获取方法的返回值类型
 AnnotatedType type declaredConstructor.getAnnotatedReturnType();
 Type type1 type.getType();
 // 获取返回值的名称 com.lang.pojo.User
 String typeName type1.getTypeName();
 System.out.println(typeName);
}
获取参数类型
Class ? aClass Class.forName( com.lang.pojo.User 
// 获取所有public的构造函数
Constructor ? [] constructors aClass.getConstructors();
// 获取所有的构造函数 包括private的
Constructor ? [] declaredConstructors aClass.getDeclaredConstructors();
for (Constructor ? declaredConstructor : declaredConstructors) {
 // 设置暴力访问 如果不设置那么private方法就不可以访问
 declaredConstructor.setAccessible(true);
 Type[] parameterTypes declaredConstructor.getGenericParameterTypes();
 for (Type type : parameterTypes) {
 System.out.println(type.getTypeName());
}
核心方法汇集java.lang.Class

类对象

方法作用static Class ? forName(String className)返回描述类名为className的Class对象T newInstance()返回这个类的一个新的实例Field[] getFields()返回public的域 包括超类Field getField(String name)返回指定名称的public的域Field[] getDeclaredFields()返回所有的域 不包括超类Field getDeclaredField(String name)返回指定名称的域 私有也可以返回Method[] getMethods()返回public的方法 包括超类Method[] getDeclaredMethods()返回所有的方法 不包括超类Constructor ? [] getConstructors()返回public的构造器 不包括超类Constructor ? [] getDeclaredConstructors()返回所有的构造器 不包括超类java.lang.reflect.Constructor

构造函数

方法作用T newInstance(Object… initargs)构造一个这个构造器所属类的新实例Class getDeclaringClass()返回一个用于描述类中定义的构造器、方法或域的Class对象Class ? [] getExceptionTypes()返回一个描述方法抛出的异常类型的Class对象数组int getModifiers()返回一个描述构造器、方法或域的修饰符的整型数值 使用java.lang.reflect.Modifier类中的方法分析这个返回值String getName()返回一个描述构造器、方法或域的名称字符串Class ? [] getParameterTypes()返回一个描述参数类型的Class对象数组java.lang.reflect.Field

属性

方法作用Object get(Object obj)返回obj对象中用Field对象表示的域值void set(Object obj, Object value)用一个新值value设置obj对象中Field对象表示的域java.lang.reflect.Method

方法

方法作用Object invoke(Object obj, Object… args)执行这个对象的方法

注意

java.lang.reflect的类中很多方法都是通用的 这里列举出来的只是工作使用比较频繁的。如果对于Java Bean的操作可以使用内省技术更加便捷。

提示

在启动时 包含main方法的类被加载。那么它就会加载所有需要的类。这些被加载的类又会继续加载它们需要的类 以此类推。对于一个大型的应用程序来说 这样程序启动就需要消耗很多的时间。不过可以确保main方法包含的类没有显式的引用其他类 等启动后调用Class.forName手动加载其他类。

内省

Java官方对Java Beans内省的定义

At runtime and in the builder environment we need to be able to figure out which properties, events, and methods a Java Bean supports. We call this process introspection.

从 Java Bean 的角度来看 这里的对象就是 Bean 对象 主要关注点是属性、方法和事件等 也就是说在运行时可以获取相应的信息进行一些处理 这就是 Java Beans 的内省机制。

与反射的区别

By default we will use a low level reflection mechanism to study the methods supported by a target bean and then apply simple design patterns to deduce from those methods what properties, events, and public methods are supported.

Java Beans 内省其实就是对反射的一种封装 。

Java Beans内省机制

核心类库

Java Beans 内省机制的核心类是 Introspector

The Introspector class provides a standard way for tools to learn about the properties, events, and methods supported by a target Java Bean.

这个内省工具类提供了标准的工具方法对于了解Java Bean的属性、方法和事件提供了支持。

核心对象

对象描述BeanInfoJava Bean 信息类PropertyDescriptor属性描述类MethodDescriptor方法描述类EventSetDescriptor事件描述集合

快速入门

Java Bean

public class User {
 private String username;
 private Integer age;
 // getter/setter
 // toString
}

Test Demo

 Test
public void test1() throws IntrospectionException {
 //获取 User Bean 信息
 BeanInfo userBeanInfo Introspector.getBeanInfo(User.class);
 //属性描述
 PropertyDescriptor[] propertyDescriptors userBeanInfo.getPropertyDescriptors();
 System.out.println( 属性描述 
 Stream.of(propertyDescriptors).forEach(System.out::println);
 //方法描述
 System.out.println( 方法描述 
 MethodDescriptor[] methodDescriptors userBeanInfo.getMethodDescriptors();
 Stream.of(methodDescriptors).forEach(System.out::println);
 //事件描述
 System.out.println( 事件描述 
 EventSetDescriptor[] eventSetDescriptors userBeanInfo.getEventSetDescriptors();
 Stream.of(eventSetDescriptors).forEach(System.out::println);
}

Result Info

属性描述 
java.beans.PropertyDescriptor[name age; propertyType class java.lang.Integer; readMethod public java.lang.Integer introspector.bean.User.getAge(); writeMethod public void introspector.bean.User.setAge(java.lang.Integer)]
java.beans.PropertyDescriptor[name class; propertyType class java.lang.Class; readMethod public final native java.lang.Class java.lang.Object.getClass()]
java.beans.PropertyDescriptor[name username; propertyType class java.lang.String; readMethod public java.lang.String introspector.bean.User.getUsername(); writeMethod public void introspector.bean.User.setUsername(java.lang.String)]
方法描述 
java.beans.MethodDescriptor[name getClass; method public final native java.lang.Class java.lang.Object.getClass()]
java.beans.MethodDescriptor[name setAge; method public void introspector.bean.User.setAge(java.lang.Integer)]
java.beans.MethodDescriptor[name getAge; method public java.lang.Integer introspector.bean.User.getAge()]
java.beans.MethodDescriptor[name wait; method public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException]
java.beans.MethodDescriptor[name notifyAll; method public final native void java.lang.Object.notifyAll()]
java.beans.MethodDescriptor[name notify; method public final native void java.lang.Object.notify()]
java.beans.MethodDescriptor[name getUsername; method public java.lang.String introspector.bean.User.getUsername()]
java.beans.MethodDescriptor[name wait; method public final void java.lang.Object.wait() throws java.lang.InterruptedException]
java.beans.MethodDescriptor[name hashCode; method public native int java.lang.Object.hashCode()]
java.beans.MethodDescriptor[name setUsername; method public void introspector.bean.User.setUsername(java.lang.String)]
java.beans.MethodDescriptor[name wait; method public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException]
java.beans.MethodDescriptor[name equals; method public boolean java.lang.Object.equals(java.lang.Object)]
java.beans.MethodDescriptor[name toString; method public java.lang.String introspector.bean.User.toString()]
事件描述 

可以看出通过内省机制可以获取 Java Bean 的属性、方法描述 这里事件描述是空的 关于事件相关会在后面介绍 。由于 Java 类都会继承 Object 类 可以看到这里将 Object 类相关的属性和方法描述也输出了 如果想将某个类的描述信息排除可以使用 java.beans.Introspector#getBeanInfo(java.lang.Class, java.lang.Class) 这个方法。

类型转换

核心对象

对象描述PropertyEditor属性编辑器顶层接口PropertyEditorSupport属性编辑器实现类PropertyEditorManager属性编辑器管理

Java Bean

public class User {
 private String username;
 private Integer age;
 private Date createTime;
 // getter/setter
 // toString
}

日期类型转换器

/**
 * 日期属性编辑器
public class DatPropertyEditor extends PropertyEditorSupport {
 Override
 public void setAsText(String text) {
 try {
 setValue((text null) ? null : new SimpleDateFormat( yyyy-MM-dd ).parse(text));
 } catch (ParseException e) {
 e.printStackTrace();
}

在之前的例子中内省设置属性值都是直接通过 PropertyDescriptor获取属性的写方法通过反射去赋值 而如果需要对值进行类型转换 则需要通过 PropertyEditorSupport#setAsText 调用 setValue 方法 然后 setValue 方法触发属性属性修改事件

public class PropertyEditorSupport implements PropertyEditor {
 public void setValue(Object value) {
 this.value value;
 firePropertyChange();
}

要注意这里的 value 实际上是临时存储在 PropertyEditorSupport中 PropertyEditorSupport 则作为事件源 从而得到类型转换后的 value 再通过 PropertyDescriptor 获取属性的写方法通过反射去赋值。

 Test
public void test6() throws IntrospectionException, FileNotFoundException {
 Map String,Object properties ImmutableMap.of( age ,1, username , zhangsan , createTime , 2020-01-01 
 User user new User();
 //获取 User Bean 信息 排除 Object
 BeanInfo userBeanInfo Introspector.getBeanInfo(User.class, Object.class);
 //属性描述
 PropertyDescriptor[] propertyDescriptors userBeanInfo.getPropertyDescriptors();
 Stream.of(propertyDescriptors).forEach(propertyDescriptor - {
 //获取属性名称
 String property propertyDescriptor.getName();
 Object value properties.get(property);
 if (Objects.equals( createTime , property)) {
 //设置属性编辑器
 propertyDescriptor.setPropertyEditorClass(DatPropertyEditor.class);
 //创建属性编辑器
 PropertyEditor propertyEditor propertyDescriptor.createPropertyEditor(user);
 //添加监听器
 propertyEditor.addPropertyChangeListener(evt - {
 //获取转换后的value
 Object value1 propertyEditor.getValue();
 setPropertyValue(user, propertyDescriptor, value1);
 propertyEditor.setAsText(String.valueOf(value));
 return;
 setPropertyValue(user, propertyDescriptor, value);
 System.out.println(user);
 * 设置属性值
private void setPropertyValue(User user, PropertyDescriptor propertyDescriptor, Object value1) {
 try {
 propertyDescriptor.getWriteMethod().invoke(user, value1);
 } catch (IllegalAccessException | InvocationTargetException ignored) {
}
事件监听

核心对象

对象描述PropertyChangeEvent属性变化事件PropertyChangeListener属性 生效 变化监听器PropertyChangeSupport属性 生效 变化监听器管理器VetoableChangeListener属性 否决 变化监听器VetoableChangeSupport属性 否决 变化监听器管理器

Java Bean

public class User {
 private String username;
 private Integer age;
 * 属性 生效 变化监听器管理器
 private PropertyChangeSupport propertyChangeSupport new PropertyChangeSupport(this);
 * 启动属性 生效 变化
 * param propertyName 
 * param oldValue 
 * param newValue
 private void firePropertyChange(String propertyName, String oldValue, String newValue) {
 PropertyChangeEvent event new PropertyChangeEvent(this, propertyName, oldValue, newValue);
 propertyChangeSupport.firePropertyChange(event);
 * 添加属性 生效 变化监听器
 public void addPropertyChangeListener(PropertyChangeListener listener){
 propertyChangeSupport.addPropertyChangeListener(listener);
 * 删除属性 生效 变化监听器
 public void removePropertyChangeListener(PropertyChangeListener listener){
 propertyChangeSupport.removePropertyChangeListener(listener);
 * 获取属性 生效 变化监听器
 public PropertyChangeListener[] getPropertyChangeListeners() {
 return propertyChangeSupport.getPropertyChangeListeners();
 public void setUsername(String username) {
 String oldValue this.username;
 this.username username;
 firePropertyChange( username , oldValue, username);
 // getter/setter
 // toString
}

Test Demo

 Test
public void test3(){
 User user new User();
 user.setAge(1);
 user.setUsername( zhangsan 
 user.addPropertyChangeListener(System.out::println);
 user.setUsername( lisi 
 user.setUsername( wangwu 
}

Result

java.beans.PropertyChangeEvent[propertyName name; oldValue zhangsan; newValue lisi; propagationId null; source User{username lisi , age 1}]
java.beans.PropertyChangeEvent[propertyName name; oldValue lisi; newValue wangwu; propagationId null; source User{username wangwu , age 1}]

可以看到在添加了监听器后 当 username 属性发生变化的时候会出发监听事件。

再看看另外一种监听器 VetoableChangeListener。在 User 中添加监听器

Java Bean(User)

/**
 * 属性 否决 变化监听器
private VetoableChangeSupport vetoableChangeSupport new VetoableChangeSupport(this);
 * 启动属性 否决 变化
 * param propertyName
 * param oldValue
 * param newValue
private void fireVetoableChange(String propertyName, String oldValue, String newValue) throws PropertyVetoException {
 PropertyChangeEvent event new PropertyChangeEvent(this, propertyName, oldValue, newValue);
 vetoableChangeSupport.fireVetoableChange(event);
 * 添加属性 否决 变化监听器
public void addVetoableChangeListener(VetoableChangeListener listener){
 vetoableChangeSupport.addVetoableChangeListener(listener);
 * 删除属性 否决 变化监听器
public void removeVetoableChangeListener(VetoableChangeListener listener){
 vetoableChangeSupport.removeVetoableChangeListener(listener);
public void setUsername(String username) throws PropertyVetoException {
 String oldValue this.username;
 fireVetoableChange( username ,oldValue,username);
 this.username username;
 firePropertyChange( username , oldValue, username);
}

Test Demo

 Test
public void test3() throws PropertyVetoException {
 User user new User();
 user.setAge(1);
 user.addVetoableChangeListener(evt - {
 System.out.println(evt.getNewValue() ,, evt.getOldValue());
 if (Objects.equals(evt.getNewValue(), evt.getOldValue())) {
 throw new PropertyVetoException( 当前属性值未发生任何变化 , evt);
 user.addPropertyChangeListener(System.out::println);
 user.setUsername( lisi 
 user.setUsername( zhangsan 
 user.setUsername( zhangsan 
}

运行时发现一直无法抛出异常。查看源码发现 PropertyChangeSupport 和 VetoableChangeSupport 当新旧值相等时不会触发监听 于是修改测试代码

Test Demo

 Test
public void test3() throws PropertyVetoException {
 User user new User();
 user.setAge(1);
 user.addVetoableChangeListener(evt - {
 System.out.println(evt.getNewValue() ,, evt.getOldValue());
 if (Objects.isNull(evt.getNewValue())) {
 throw new PropertyVetoException( username 不能为null , evt);
 user.addPropertyChangeListener(System.out::println);
 user.setUsername( lisi 
 user.setUsername(null);
}

Result

lisi,,null
java.beans.PropertyChangeEvent[propertyName username; oldValue null; newValue lisi; propagationId null; source User{username lisi , age 1}]
null,,lisi
java.beans.PropertyVetoException: username 不能为null
 at introspector.test.IntrospectorTest.lambda$test3$1(IntrospectorTest.java:78)
 at java.beans.VetoableChangeSupport.fireVetoableChange(VetoableChangeSupport.java:375)

可以发现当符合“否决”属性变化的条件时 会抛出 PropertyVetoException 异常阻断属性的变化。

在之前的示例中 userBeanInfo 输出的 EventSetDescriptor 为空 这是因为并未到 User 类中增加事件。现在再测试一下获取 EventSetDescriptor

 Test
public void test1() throws IntrospectionException {
 BeanInfo userBeanInfo Introspector.getBeanInfo(User.class, Object.class);
 EventSetDescriptor[] eventSetDescriptors userBeanInfo.getEventSetDescriptors();
 Stream.of(eventSetDescriptors).forEach(System.out::println);
}
java.beans.EventSetDescriptor[name propertyChange; inDefaultEventSet; listenerType interface java.beans.PropertyChangeListener; getListenerMethod public java.beans.PropertyChangeListener[] introspector.bean.User.getPropertyChangeListeners(); addListenerMethod public void introspector.bean.User.addPropertyChangeListener(java.beans.PropertyChangeListener); removeListenerMethod public void introspector.bean.User.removePropertyChangeListener(java.beans.PropertyChangeListener)]
java.beans.EventSetDescriptor[name vetoableChange; inDefaultEventSet; listenerType interface java.beans.VetoableChangeListener; addListenerMethod public void introspector.bean.User.addVetoableChangeListener(java.beans.VetoableChangeListener); removeListenerMethod public void introspector.bean.User.removeVetoableChangeListener(java.beans.VetoableChangeListener)]

在 Java 生态飞速发展的今天 很多底层技术细节都被高级框架所屏蔽 而 Java Beans 就是其中一种。也许平时根本就用不到 但是其代码设计和思想理念不应该被忽视。Dubbo 2.7 之后提出了“服务自省”的概念 其灵感就来源于 Java Beans 内省机制。


本文转自网络,原文链接:https://developer.aliyun.com/article/787309
本站部分内容转载于网络,版权归原作者所有,转载之目的在于传播更多优秀技术内容,如有侵权请联系QQ/微信:153890879删除,谢谢!
上一篇:阿里 Nacos 注册中心 配置启动说明 下一篇:没有了

推荐图文


随机推荐