注:本文主要记录自 兄弟连_马剑威 JavaSE第三季 第49讲 内省机制
1、JavaBean的概念
- 什么是JavaBean?
- Bean理解为组件的意思,JavaBean就是Java组件,在广泛的理解就是一个类,对于组件来说,关键在于要具有“能够被IDE构建工具侦测其属性和事件”的能力,通常在Java中
一个JavaBean要具备这样的命名规则:
- 对于一个名称为xxx的属性,通常你要写两个方法:getXxx() 和 setXxx() .任何浏览这些方法的工具,都会把get或set后面的第一个字母自动转换为小写。
- 对于布尔型属性,可以使用以上get和set方式,不过也可以把get替换成is
- Bean的普通方法不必遵循以上命名规则,不过他们必须是public的
- 对于事件,要使用Swing中处理监听器的方式。如addWindowListener,removeWindowListener
2、内省的基本概念
- 内省(IntroSpector)是Java语言对Bean类属性、事件的一种缺省处理方法。例如类A中有属性name,那我们可以通过getName,setName来得到其值或者设置新的值。
- 通过getName/setName来访问name属性,这就是默认的规则。
- Java中提供了一套API用来访问某个属性的getter/setter方法,通过这些API可以使你不需要理解这个规则,这些API存放于包java.beans中,一般的做法是通过类Introspector的getBeanInfo方法来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来调用这些方法。
3、IntroSpector相关API
1、Introspector类:
Introspector类为通过工具学习有关受目标JavaBean支持的属性、事件和方法的知识提供了一个标准方法。
static BeanInfo getBeanInfo(Class<?> beanClass)
在JavaBean上进行反省,了解其所有属性、公开的方法和事件
2、BeanInfo类
该类实现此BeanInfo接口并提供有关其bean的方法、属性、事件等显式信息。
MethodDescriptor[] getMethodDescriptors()
获得 beans MethodDescriptor
PropertyDescriptor[] getPropertyDescriptors()
获得beans PropertyDescriptors
3、PropertyDescriptor类:
PropertyDescriptor描述JavaBean通过一对存储器方法导出的一个属性
Method getReadMethod()
获得应该用于读取属性值的方法
Method getWriteMethod()
获得应该用于写入属性值的方法
4、MethodDescriptor类:
MethodDescriptor 描述了一种特殊方法
即Java Bean支持从其他组件对其进行外部访问
Method getMethod()
获得此MethodDescriptor封装的方法
实例:
/**
* javaBean
*
*/
public class Dog {
private int age;
private String name;
public int getAge() {
return age;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
}
/**
* 内省
*
*/
public class IntroSpectorDemo {
/**
* @param args
*/
public static void main(String[] args) {
// Dog dog=new Dog();//使用者与被使用者耦合
//不耦合可以使用工厂模式
Dog dog=DogFactory.getDog("dog");
}
}
/**
* 工厂类
*
*/
public class DogFactory {
public static Dog getDog(String dogname) {
if("dog".equals(dogname)){
return new Dog();//此处也耦合
}
return null;
}
}
但在如上的工厂类里,仍是会用到 new Dog();产生耦合
所以需要采取另外的途径,
如在反射中的:Class.forName("class的全路径")
创建配置文件 bean.properties,使用配置文件的目的是修改方便,相比定义在类中的静态参数而言,当某天代码参数发生变化时,只要修改配置文件中的参数,重启系统即可。而在代码中的参数修改则需要重新修改源码,编译,替换。【注:配置文件如果要正常显示中文,需要将配置文件的编码格式更改为:utf-8】
<pre name="code" class="java">#配置javaBean的全名
dog=IntroSpectorDemo.Dog
dog.name=小白
dog.age=4
在工厂类中通过读取配置文件信息,并利用内省机制生成Dog实例
/**
* 工厂类
* @author zhan414
*
*/
public class DogFactory {
private static Properties config=new Properties();//处理properties文件的工具类,负责读写属性文件
static{
//读取属性文件
InputStream inputStream=Thread.currentThread().getContextClassLoader().getResourceAsStream("bean.properties");
//加载属性文件
try {
config.load(inputStream);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Dog getDog(String dogname){
//从Properties对象中根据key获取value
String className =config.getProperty("dog");
try {
//根据类全名获取类信息对象Class对象
Class dogClass = Class.forName(className);
//实例化对象
Dog dog=(Dog) dogClass.newInstance();
//内省:获取bean信息
BeanInfo beanInfo=Introspector.getBeanInfo(dogClass);
//通过bean信息获取所有属性描述器,数组
PropertyDescriptor[] pds=beanInfo.getPropertyDescriptors();
//循环遍历属性描述器
for (PropertyDescriptor propertyDescriptor : pds) {
if ("name".equals(propertyDescriptor.getName())) {//判断是否是name属性
String nameValue=config.getProperty("dog.name");
//通过属性描述器获取该属性的写操作(set方法)
Method method=propertyDescriptor.getWriteMethod();
//在dog对象上调用方法
method.invoke(dog, nameValue);
}else if ("age".equals(propertyDescriptor.getName())) {
String ageValue=config.getProperty("dog.age");
Method method=propertyDescriptor.getWriteMethod();//内省
method.invoke(dog, Integer.valueOf(ageValue));
}
//返回对某属性的method对象,相比反射要方便许多
//eg:Method setNameMethod=personClass4.getMethod("setName", String.class);
// setNameMethod.invoke(p5, "小花");//相当于 p5.setName("小花");
}
return dog;
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IntrospectionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
测试:
public class IntroSpectorDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
Dog dog=DogFactory.getDog("dog");
System.out.println("dog.name= "+dog.getName()+" , dog.age= "+dog.getAge());
}
}
输出:
dog.name= å°ç½ , dog.age= 4
可以看到name为乱码,这是因为配置文件中 dog.name的值为中文,未转成 unicode格式
解决方法:修改DogFactory中的properties文件输入静态块,配置输入流的编码格式,如下:
static {
try {
// 读取属性文件
InputStream inputStream = Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream("bean.properties");
BufferedReader bf = new BufferedReader(new InputStreamReader(
inputStream, "utf-8"));
// 加载属性文件
config.load(bf);
//关闭流
inputStream.close();
bf.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
修改后便可输出中文
eclipse properties文件中文格式使用配置: