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的属性、方法和事件提供了支持。
核心对象
对象 | 描述 |
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 内省机制。