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 内省机制。
1.我喝了那么多次优乐美奶茶,也没见周杰伦问我是他的谁 2.以后谁敢欺负我,就...
-更多关于数智化转型、数据中台内容请加入 阿里云数据中台交流群—数智俱乐部 和...
注册域名 解析需要多久?在 域名 状态正常的情况下,域名解析的时间是很快的,一...
1.????背景 客户A是一家运行CDH 5.14.2的金融服务公司。他们拥有运行关键工作负...
国外 域名 如何 申请?要申请国外域名,直接去提供国外 域名注册 服务商的平台就...
支持列表 实例 支持自动化发放裸金属服务器,远程Console登录。 支持租户自主管...
客户简介 115科技是一家成立9年、以云存储起步的高新技术企业,旗下核心产品之一...
一个企业可以注册多少 域名 ?一个企业可以注册的域名数量没有限制,很多企业为...
对于Java 应用,程序员之间一个认识口口相传: 要看一个Java程序跑的快不快,需要...
9月10日,在2020腾讯全球数字生态大会上,腾讯研究院联合腾讯云发布了《数字中国...