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());
}
}
类对象
方法 | 作用 |
---|---|
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() | 返回所有的构造器,不包括超类 |
构造函数
方法 | 作用 |
---|---|
T newInstance(Object… initargs) | 构造一个这个构造器所属类的新实例 |
Class getDeclaringClass() | 返回一个用于描述类中定义的构造器、方法或域的Class对象 |
Class[] getExceptionTypes() | 返回一个描述方法抛出的异常类型的Class对象数组 |
int getModifiers() | 返回一个描述构造器、方法或域的修饰符的整型数值,使用java.lang.reflect.Modifier 类中的方法分析这个返回值 |
String getName() | 返回一个描述构造器、方法或域的名称字符串 |
Class[] getParameterTypes() | 返回一个描述参数类型的Class对象数组 |
属性
方法 | 作用 |
---|---|
Object get(Object obj) | 返回obj对象中用Field对象表示的域值 |
void set(Object obj, Object value) | 用一个新值value设置obj对象中Field对象表示的域 |
方法
方法 | 作用 |
---|---|
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 内省机制的核心类是 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的属性、方法和事件提供了支持。
核心对象
对象 | 描述 |
---|---|
BeanInfo | Java 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 内省机制。