`
Cwind
  • 浏览: 265867 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
博客专栏
793bb7df-a2a9-312d-8cb8-b66c3af482d1
LeetCode题解
浏览量:53716
社区版块
存档分类
最新评论

Java PropertyDescriptor 应用及源码分析

    博客分类:
  • Java
阅读更多
前文 从Introspector谈Java内省机制 提到了通过Introspector.getBeanInfo()方法获取属性描述符数组,进而读取属性值的方式,但未对PropertyDescriptor的应用和实现作进一步阐释,在此作个补完。
 
1. 概述
PropertyDescriptor描述Java Bean中通过一对存储器方法(getter / setter)导出的一个属性。我们可以通过该PropertyDescriptor对bean中的该属性进行读取和写入操作,也可以设置其getter / setter。
 
2. 关键接口及内部属性
public PropertyDescriptor(String name, Class<?> beanClass) throws IntrospectionException
public PropertyDescriptor(String name, Class<?> beanClass, String getMethodName, String setMethodName) throws IntrospectionException
public PropertyDescriptor(String name, Method readMethod, Method writeMethod) throws IntrospectionException

public Class<?> getPropertyType()
public Method getReadMethod()
public Method getWriteMethod()

public void setReadMethod(Method readMethod) throws IntrospectionException
public void setWriteMethod(Method writeMethod)
public boolean equals(Object o) 
相关的PropertyDescriptor内部属性如下:
    Class<?> propertyType;     //该属性的类型
    Method getMethod;     //getter
    Method setMethod;     //setter
 还有继承自其父类FeatureDescriptor的功能,用于指定该属性的编程名称
 
3. 简单应用
现有Person类如下:
package com.cwind.property;

public class Person {
        private String name ;
        private int age ;
       
        public Person(){ this.name = ""; this.age = 0; }
        public Person(String name, int age) { super(); this.name = name; this. age = age; }

        public String getName() { return name; }
        public void setName(String name) { this. name = name; }

        public int getAge() { return age; }
        public void setAge(int age) { this. age = age; }
       
        public String getNameInUpperCase(){
               return this .name .toUpperCase();
       }
        public void setNameToLowerCase(String name){
               this.name = name.toLowerCase();
       }
}
该类中除了name和age两个属性的标准getter和setter之外,还有增加了一个获取大写name的get方法和一个将name设置为小写的set方法。
在测试类中,首先获得这两个方法对象。
Class personClass = Class.forName("com.cwind.property.Person");
Method read = personClass.getMethod("getNameInUpperCase", null);
Method write = personClass.getMethod("setNameToLowerCase", String.class );

//然后可以通过两种方式构造PropertyDescriptor
PropertyDescriptor prop1 = new PropertyDescriptor( "name", Person.class );     //使用其标准getter和setter
PropertyDescriptor prop2 = new PropertyDescriptor( "name", read, write);     //使用read和write两个方法对象所自定义的getter和setter

//下面构建一个Person对象
Person person = new Person("Kobe" , 36);
System. out.println(prop1.getReadMethod().invoke(person, null));     // --实际调用Person.getName(), result: Kobe
System. out.println(prop2.getReadMethod().invoke(person, null));     // --实际调用Person.getNameInUpperCase(), result: KOBE
                     
prop1.getWriteMethod().invoke(person, "James");     // --实际调用Person.setName(), person.name被设置为James
prop2.getWriteMethod().invoke(person, "James");     // --实际调用Person.setNameToLowerCase(), person.name被设置为james
4. 源码分析
构造函数1:
public PropertyDescriptor(String name, Class<?> beanClass)
        throws IntrospectionException {
        setName(name);     //设置属性编程名,本例中即'name'
        if (name.length() == 0){
            throw new IntrospectionException("empty property name");     
// 编程名为空则抛出异常
        }
        String caps = Character.toUpperCase(name.charAt(0)) + name.substring(1);     
// 标准getter应为getName()或isName(), 先将首字母大写
        findMethods(beanClass, "is" + caps, "get" + caps, "set" + caps);     
// 参数依次为:类类型,可能的getter函数名1,可能的getter函数名2,setter函数名
        if (getMethod == null){   // findMethods()设置PropertyDescriptor的getMethod和setMethod属性
            throw new IntrospectionException(
                "Cannot find a is" + caps + " or get" + caps + " method");
        }
        if (setMethod == null){
            throw new IntrospectionException(
                "Cannot find a " + caps + " method" );
        }
        propertyType = checkMethods(getMethod, setMethod);     
// checkMethods()函数用来检测getMethod得到的类型与setMethod的参数类型是否匹配,若匹配则置propertyType为该类型
    } 
构造函数2:
public PropertyDescriptor(String name, Class<?> beanClass, String getMethodName, String setMethodName) throws IntrospectionException
 
其实现与构造函数1类似,只是调用findMethods时直接查找指定的getter和setter函数名:
        findMethods(beanClass, getMethodName, null, setMethodName);
 
构造函数3则更加直观,直接设置方法对象
public PropertyDescriptor(String name, Method readMethod, Method writeMethod) throws IntrospectionException
 
两个比较重要的私有辅助函数分别为findMethods()和checkMethods(),分别看一下其实现
 
findMethods用来按指定的getter和setter函数名在指定Class中查找getMethod和setMethod,并设置PropertyDescriptor的相关属性 
private void findMethods(Class beanClass, String getMethodName1, String getMethodName2, String setMethodName) throws IntrospectionException {
        try {
            // 首先查找getMethodName1指定的getter (isXXX)
            if (getMethodName1 != null) {
                try {
                    getMethod = beanClass.getMethod(getMethodName1, new Class[0]);
                }
                catch (NoSuchMethodException e)
                {}
            }
            // 若失败,则查找getMethodName2指定的getter (getXXX)
            if (getMethod == null && getMethodName2 != null) {
                try {
                    getMethod = beanClass.getMethod(getMethodName2, new Class[0]);
                }
                catch (NoSuchMethodException e)
                {}
            }
            if (setMethodName != null) {
                if (getMethod != null) {
                    // 如果得到了getMethod,则通过其返回值类型决定setMethod的参数类型
                    Class propertyType = getMethod.getReturnType();
                    if (propertyType == Void.TYPE) { 
// 若getter的返回值为Void类型则抛出异常
                        String msg = "The property's read method has return type 'void'";
                        throw new IntrospectionException(msg);
                    }

                    Class[] setArgs = new Class[] { propertyType }; 
                    try {
                        setMethod = beanClass.getMethod(setMethodName, setArgs); 
// 通过函数名和参数类型获得setMethod
                    }
                    catch (NoSuchMethodException e)
                    {}
                }
                else if (getMethodName1 == null && getMethodName2 == null) {
                    // getMethodName1和2均为空,则此属性为只写属性,此时遍历bean中的函数,返回第一个名称与setMethodName一致且返回类型为Void的单参数函数
                    Method[] methods = beanClass.getMethods();
                    for (int i = 0; i < methods.length; i++) {
                        if (methods[i].getName().equals(setMethodName)
                            && methods[i].getParameterTypes().length == 1
                            && methods[i].getReturnType() == Void.TYPE) {
                            setMethod = methods[i];
                            break;
                        }
                    }
                }
            }
        }
        catch (SecurityException e) {
            String msg = "SecurityException thrown on attempt to access methods.";     // 作者在纠结要不要修改异常类型
            throw new IntrospectionException(msg);
        }
    }
checkMethods方法
private Class<?> checkMethods(Method readMethod, Method writeMethod) throws IntrospectionException {
        Class<?> newPropertyType = propertyType;
         // 合法的read方法应该无参同时带有一个非空的返回值类型
        if (readMethod != null) {
            if (readMethod.getParameterTypes().length > 0) {
                throw new IntrospectionException("read method has unexpected parameters");
            }
            newPropertyType = readMethod.getReturnType();
             if (newPropertyType == Void.TYPE) {
                throw new IntrospectionException("read method return type is void");
            }
        }
         // 合法的write方法应该包含一个类型相同的参数
        if (writeMethod != null) {
            if (writeMethod.getParameterTypes().length != 1) { // 参数不能超过一个
                String msg = "write method does not have exactly one parameter" ;
                throw new IntrospectionException(msg);
            }
            if (readMethod == null) {
                // 若无read方法,属性类型就应为writeMethod的参数类型
                newPropertyType = writeMethod.getParameterTypes()[0];
            }
            else {
                // 检查read方法的返回值类型是否与write方法的参数类型相匹配
                if (newPropertyType != null
                    && !newPropertyType.isAssignableFrom(
                        writeMethod.getParameterTypes()[0])) {
                     throw new IntrospectionException("read and write method are not compatible");
                }
            }
        }
         return newPropertyType;
    }
最后提一句PropertyDescriptor.equals(), 只有当属性类型、标志、读写方法和PropertyEditorClass均相同时才认为两个PropertyDescriptor相等
return samePropertyType
                && sameFlags
                && sameReadMethod
                && sameWriteMethod
                && samePropertyEditorClass;
 
  • 大小: 14.9 KB
2
1
分享到:
评论

相关推荐

    Java通过PropertyDescriptor反射调用set和get方法

    在实际应用中,`PropertyDescriptor`和反射结合使用非常常见,尤其是在处理JavaBean或者进行数据绑定时。例如,当需要动态地操作未知类型的JavaBean属性,或者在框架(如Spring)中实现数据绑定和属性映射时,`...

    Thinking in Java习题答案及源码

    12. **内省(Introspection)**:了解并使用java.beans包下的API,如PropertyDescriptor、MethodDescriptor等。 13. **I/O流的NIO**:非阻塞I/O,选择器(Selector)、通道(Channel)、缓冲区(Buffer)等。 14. ...

    java深度复制源代码

    - **反射机制**:该工具类大量使用了Java反射API(如`Introspector.getBeanInfo()`、`PropertyDescriptor`等)来获取和设置JavaBean的属性。 - **泛型**:使用了泛型`&lt;T&gt;`,使得该工具类可以应用于任意类型的...

    Java 反射创建get set方法及反射方法的调用

    ### Java反射创建get set方法及反射方法的调用 #### 概述 在Java编程语言中,反射(Reflection)是一种强大的技术,它允许程序在运行时检查类、接口、字段和方法的信息,并能够动态地创建对象和调用方法。本文将...

    Java反射中java.beans包学习总结.docx

    在Java编程语言中,反射(Reflection)是一种强大的工具,它允许程序在运行时检查和...通过`PropertyEditor`、`BeanInfo`等组件,我们可以实现更复杂的类型转换、事件处理和界面构建,增强应用程序的灵活性和可扩展性。

    java基础类库文件rt.jar

    Java基础类库,也就是Java doc里面看到的所有的类的class文件。

    openbean.jar AndroidStudio 导入java.beans.*

    在Android开发过程中,有时我们需要利用Java的一些特定库来扩展应用的功能。`openbean.jar`是一个包含JavaBeans组件的库,特别适用于在Android Studio项目中使用`java.beans`包中的类和接口。`java.beans`包提供了...

    Java常用工具类包包含各种常用开发工具

    - `java.beans`包提供了诸如`PropertyDescriptor`和`BeanInfo`等类,用于获取和设置Java Bean的属性值。 - `org.springframework.beans.BeanUtils`是Spring框架的一部分,提供了更强大和灵活的Bean操作,如复制...

    commons-beanutils 源码

    总结来说,Apache Commons BeanUtils是Java开发中的重要工具,通过深入研究其源码,我们可以了解到JavaBean模式的实现细节,以及如何利用反射和类型转换来增强代码的灵活性。同时,源码中的设计模式和异常处理策略也...

    Java反射与内省-PPT

    它通过`java.lang.Class`类和相关的类及接口实现。例如,你可以获取一个类的公共方法列表,创建该类的实例,或者调用特定的方法,即使在编译时不知道具体的类信息。这对于创建通用代码,如框架和库,非常有用,因为...

    java.beans.*

    `java.beans.PropertyDescriptor` 类用于封装 Java 对象的属性,提供了获取和设置属性值的方法。 3. **Event**: 事件是 Bean 中发生的有意义的事情。`java.beans.EventSetDescriptor` 描述了一个事件集,而 `java....

    「一入 Java 深似海 」系列课程 - 第七期 第二节《Java Beans》1

    为了更好地理解和使用 Java Beans,你需要熟悉相关的 API,如 `java.beans` 包中的类和接口,例如 `PropertyDescriptor`、`EventSetDescriptor` 和 `Introspector`。同时,了解如何实现事件监听器接口(如 `...

    Java中的内省与反射.doc

    本文将详细介绍这两者的基本原理、应用场景以及如何在Java中使用它们。 #### 二、反射 ##### 2.1 概念解释 反射是一种允许程序在运行时检查和修改自身结构和行为的能力。简单来说,反射提供了以下几种能力: - ...

    Java 内省(Introspector)深入理解

    Java内省(Introspector)是Java语言提供的一种机制,用于在运行时分析Java对象的属性、方法和事件。这个特性对于开发人员来说非常有用,因为它允许程序动态地检查和修改对象的状态,而不必了解对象的具体实现细节。...

    JAVA的内省机制(introspector)与反射机制(reflection).docx

    import java.beans.PropertyDescriptor; import java.lang.reflect.Method; public class myBeanIntrospector { public myBeanIntrospector() { try { // 实例化一个 Bean bean beanObj = new bean(); // ...

    java加强笔记

    ThreadLocal类及应用技巧 - `ThreadLocal`类提供了一种线程局部变量的解决方案,可以为每个线程提供独立的变量副本。 ##### 7. 多个线程之间共享数据的方式探讨 - 可以通过显式锁、同步容器等手段来实现多个线程间...

    良葛格java jdk 5.0学习笔记.rar

    8. **内省增强**:JavaBeans的内省API在Java 5.0中得到增强,使得反射操作更加方便,例如通过`PropertyDescriptor`和`Field`可以直接访问和修改对象属性。 9. **并发改进**:Java 5.0引入了`java.util.concurrent`...

    winform 属性可扩展性源码

    三、实现源码分析 在提供的压缩包文件"属性可扩展性"中,可能包含以下几个部分: 1. 自定义属性类:定义新的Attribute子类,如MyCustomAttribute,用于描述自定义属性的特性。 2. 控件类:创建一个新的WinForm控件...

    Java基础学习44.pdf

    Java是世界上最流行的编程语言之一,尤其在企业级应用开发中占据主导地位。为了在IT行业中找到一份工作,熟练掌握Java基础知识至关重要。这份“Java基础学习44.pdf”资源旨在帮助初学者快速掌握Java的核心概念,特别...

Global site tag (gtag.js) - Google Analytics