内省一般写业务代码的时候是用不到的,大部分是在写一些框架或者工具的时候会用到。
比如说 spring 初始化 bean 的场景:
spring 提供 ioc 的机制,使用者在 xml 中配置 bean,spring 启动的时候加载 bean
假设有下面的类:
public class User {
private String name;
private int 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;
}
}
我们正常使用的话,可以这样写:
User u = new User();
u.setName("chengfan");
u.setAge(22);
这是完全没有问题的,但是,当我们把这个类交给 spring 的时候,问题就出现了: spring 是一个已有的框架, 它并不知道 User
这个类,也并不知道它有哪些方法、哪些属性。
所以 spring 需要使用一种特殊的手段来拿到这些信息,我们熟知的方法是反射。
spring.xml 如下:
<bean id="user" class="cn.fanhub.jysk.spring.introspector.User">
<property name="name" value="chengfan" />
<property name="age" value="22" />
</bean>
利用反射,我们会这样写:
@Test
public void test1 () throws Exception {
//模拟从 xml 中获得到了数据
//<bean id="user" class="cn.fanhub.jysk.spring.introspector.User">
// <property name="name" value="chengfan" />
// <property name="age" value="22" />
//</bean>
String clazz = "cn.fanhub.jysk.spring.introspector.User";
Map<String, Object> properties = new HashMap<>();
properties.put("Name", "chengfan");
properties.put("Age", 22);
reflect(clazz, properties);
}
@SuppressWarnings("unchecked")
public void reflect(String clazz, Map<String, Object> properties) throws Exception {
//反射创建实例
Class target = Class.forName(clazz);
Object bean = target.newInstance();
//初始化容器时,调用setter注入
for (Entry<String, Object> entry : properties.entrySet()) {
String _setName = "set" + entry.getKey();
if ("name".equalsIgnoreCase(entry.getKey())) {
Method setMethod = target.getMethod(_setName, String.class);
setMethod.invoke(bean, entry.getValue().toString());
} else {
Method setMethod = target.getMethod(_setName, int.class);
setMethod.invoke(bean, Integer.parseInt(entry.getValue().toString()));
}
}
// show
for (Entry<String, Object> entry : properties.entrySet()) {
String _getName = "get" + entry.getKey();
if ("name".equalsIgnoreCase(entry.getKey())) {
Method setMethod = target.getMethod(_getName);
System.out.println(setMethod.invoke(bean));
} else {
Method setMethod = target.getMethod(_getName);
System.out.println(setMethod.invoke(bean));
}
}
}
/*
22
chengfan
*/
都是硬编码,不具备任何观赏性,只是为了演示如何实现。
虽然这样可以达到目的,但是我们获取 get 和 set 方法的手段不够优雅。
使用内省的方式实现如上功能:
@Test
public void test1 () throws Exception {
//模拟从 xml 中获得到了数据
//<bean id="user" class="cn.fanhub.jysk.spring.introspector.User">
// <property name="name" value="chengfan" />
// <property name="age" value="22" />
//</bean>
String clazz = "cn.fanhub.jysk.spring.introspector.User";
Map<String, Object> properties = new HashMap<>();
properties.put("name", "chengfan");
properties.put("age", 22);
introspector(clazz, properties);
}
public void introspector(String clazz, Map<String, Object> properties) throws Exception {
//反射创建实例
Class target = Class.forName(clazz);
Object bean = target.newInstance();
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
Method setMethod = pd.getWriteMethod();
String fieldName = pd.getName();
if ("name".equalsIgnoreCase(fieldName)) {
setMethod.invoke(bean, properties.get(fieldName));
} else if ("age".equalsIgnoreCase(fieldName)){
setMethod.invoke(bean, properties.get(fieldName));
}
}
// show
for (PropertyDescriptor pd : pds) {
System.out.println(pd.getReadMethod().invoke(bean));
}
}
/*
22
chengfan
class cn.fanhub.jysk.spring.introspector.User // 先忽略它
*/
很明显,内省显得更加专业。当然,它的操作依旧不方便,比如参数类型的判断。
首发自 个人网站
网友评论