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
类对象
java.lang.reflect.Constructor
构造函数
java.lang.reflect.Field
属性
java.lang.reflect.Method
方法
注意
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 的属性、方法和事件提供了支持。
核心对象
快速入门
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)
这个方法。
类型转换
核心对象
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) {
}
}
复制代码
事件监听
核心对象
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 内省机制。
评论 (1 条评论)