Java基础扩展提升总结:内省机制

内省机制

一、什么是内省

1、内省和反射有什么区别?

反射是在运行状态把Java类中的各种成分映射成相应的Java类,可以动态的获取所有的属性以及动态调用任意一个方法,强调的是运行状态。内省(IntroSpector)是Java 语言对 Bean 类属性、事件的一种缺省处理方法。 

JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为“值对象”(Value Object),或“VO”。方法比较少。这些信息储存在类的私有变量中,通过set()、get()获得。内省机制是通过反射来实现的,BeanInfo用来暴露一个bean的属性、方法和事件,以后我们就可以操纵该JavaBean的属性。

2、内省中的核心类

在Java内省中,用到的基本上就是上述几个类。 通过BeanInfo这个类就可以获取到类中的方法和属性。例如类 A 中有属性 name, 那我们可以通过 getName,setName 来得到其值或者设置新的值。通过 getName/setName 来访问 name 属性,这就是默认的规则。 Java 中提供了一套 API 用来访问某个属性的 getter/setter 方法,通过这些 API 可以使你不需要了解这个规则(但你最好还是要搞清楚),这些 API 存放于包 java.beans 中,一般的做法是通过类 Introspector 的 getBeanInfo方法 来获取某个对象的 BeanInfo 信息,然后通过 BeanInfo 来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后我们就可以通过反射机制来调用这些方法,这就是内省机制。

JDK内省类库

  • java.beans.Introspector:Introspector 类为通过工具学习有关受目标 Java Bean 支持的属性、事件和方法的知识提供了一个标准方法。
  • java.beans.BeanInfo接口:希望提供有关其 bean 的显式信息的 bean 实现者可以提供某个 BeanInfo 类,该类实现此 BeanInfo 接口并提供有关其 bean 的方法、属性、事件等显式信息。
  • java.beans.PropertyDescriptor:PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性。

二、示例一

使用内省,把注解中各个JavaBean中的字段值注入到方法的JavaBean对象中。

(1)JavaBean类(Person)

package StuIntrospector.StuAnnoIntro;

public class Person {
    private String name;
    private int age;
    private String phone;

    public Person(String name, int age, String phone) {
        this.name = name;
        this.age = age;
        this.phone = phone;
    }

    public Person() {
    }

//省略setter和getter

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", phone='" + phone + '\'' +
                '}';
    }
}

(2)自定义的注解

package StuIntrospector.StuAnnoIntro;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String name();
    int age();
    String phone();
}

(3)使用注解的类

package StuIntrospector.StuAnnoIntro;

public class TestAnnotation {

    @MyAnnotation(name = "suxing",age = 21,phone = "19876545676")
    public static void getPerson(Person person){
        System.out.println(person);
    }
}

到这里再明确一下问题,就是如何使用内省和反射技术,把注解中的值,自动装入到方法的形参person中去。

(4)主类(测试类)

package StuIntrospector.StuAnnoIntro;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) {
        try {
            Class testAnnoClazz = Class.forName("StuIntrospector.StuAnnoIntro.TestAnnotation");
            Method method1 = testAnnoClazz.getMethod("getPerson", Person.class);
            MyAnnotation myAnnotation = method1.getAnnotation(MyAnnotation.class);

            Class personClazz = Class.forName("StuIntrospector.StuAnnoIntro.Person");
            Person person = (Person)personClazz.newInstance();
            BeanInfo beanInfo = Introspector.getBeanInfo(personClazz);
            PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
            for (int i = 0;i<descriptors.length;i++){
                if("name".equals(descriptors[i].getName())){
                    Method method2 = descriptors[i].getWriteMethod();
                    method2.invoke(person,myAnnotation.name());
                }else if("age".equals(descriptors[i].getName())){
                    Method method2 = descriptors[i].getWriteMethod();
                    method2.invoke(person,myAnnotation.age());
                }else if("phone".equals(descriptors[i].getName())){
                    Method method2 = descriptors[i].getWriteMethod();
                    method2.invoke(person,myAnnotation.phone());
                }
            }
            //System.out.println(person);
            method1.invoke(testAnnoClazz,person);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 代码分析:

上述代码,第一步:首先使用反射机制获取使用我们自定义注解的类的Class对象,然后获取我们自定义注解所在的方法,再根据方法获取注解在该方法上的注解。

第二步:使用反射机制获取JavaBean类的Class类对象,然后实例化一个JavaBean对象。

第三步:使用内省类(Introspector)获取Bean的信息类(BeanInfo),然后根据BeanInfo类获取该Bean的所有属性(getter和setter方法),然后遍历这些属性,根据属性的名称,把注解中的各个字段通过反射调用设置到person对象中去,最后通过反射调用被注解修饰的方法,并传入person对象。

(5)测试结果

Person{name='suxing', age=21, phone='19876545676'}

三、示例二

读取配置文件中类的信息,并根据这些信息使用内省机制,构造该类的实例。

(1)配置文件(student.properties)

student = StuIntrospector.StudentFactory.Student
name = suxing
age = 21
gender = male

(2)JavaBean(Student类)

package StuIntrospector.StudentFactory;

public class Student {
    private String name;
    private int age;
    private String gender;

    public Student(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public Student() {}

    //getter和setter    

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                '}';
    }
}

(3)主类(生成Student对象的工厂类StudentFactory)

package StuIntrospector.StudentFactory;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

public class StudentFactory {

    public static Properties stuProperties = new Properties();

    static {
        InputStream inputStream =Thread.currentThread().getContextClassLoader().getResourceAsStream("student.properties");
        try {
            stuProperties.load(inputStream);
            //stuProperties.load(new BufferedInputStream(new FileInputStream(new File("student.properties"))));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Student getStudent(String name){
        String className = stuProperties.getProperty(name);
        System.out.println(className);

        try {
            Class classStudent = Class.forName(className);
            Student student = (Student) classStudent.newInstance();
            BeanInfo beanInfo = Introspector.getBeanInfo(classStudent);
            PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();

            for (int i = 0;i<descriptors.length;i++){
                if ("name".equals(descriptors[i].getName())){
                    String nameValue = stuProperties.getProperty("name");
                    Method method = descriptors[i].getWriteMethod();
                    method.invoke(student,nameValue);
                }else if("age".equals(descriptors[i].getName())){
                    String ageValue = stuProperties.getProperty("age");
                    Integer age = Integer.parseInt(ageValue);
                    Method method = descriptors[i].getWriteMethod();
                    method.invoke(student,age);
                }else if("gender".equals(descriptors[i].getName())){
                    String genderValue = stuProperties.getProperty("gender");
                    Method method = descriptors[i].getWriteMethod();
                    method.invoke(student,genderValue);
                }
            }
            return student;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

上述代码, 

第一步,使用当前线程的类加载器把配置文件加载到内存,然后根据配置文件上的类名,获取该类(Student)的Class类对象,并创建一个该对象的实例,

第二步:根据第一步获取的Class类对象,获取该Bean的信息BeanInfo类,然后根据BeanInfo类获取Bean中的所有属性,遍历这些属性,根据属性名称,为student对象设置与配置文件中对应的属性值,然后返回该student对象。

(4)测试类

package StuIntrospector.StudentFactory;

public class TestStudent {
    public static void main(String[] args) {
        Student student = StudentFactory.getStudent("student");
        System.out.println(student);
    }
}

测试结果:

StuIntrospector.StudentFactory.Student
Student{name='suxing', age=21, gender='male'}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值