上一篇提到通过反射去找setter方法时代码有问题,所以加了这个测试以确保程序没问题
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<bean id="gordon" class="gordon.study.spring.sample.Employee">
<property name="name" value="Gordon" />
<property name="age" value="32" />
<property name="male" value="true" />
<property name="birthday" value="1982-07-05" />
<property name="salary" value="817.52" />
</bean>
</beans>
显然,测试目前无法通过,报 java.lang.NoSuchMethodException: gordon.study.spring.sample.Employee.setAge(java.lang.String)
(注意,birthday是java.util.Date类型。这样配置在spring中是会报错的)
既然我们不知道setter方法的参数是什么,那就只能获得当前class的所有方法,通过名字去找匹配的setter了。
一个潜在但不常见的问题是:如果存在同名的setter方法怎么办?比如 setMale(boolean isMale) 和 setMale(String s)(期待"Y“或”N“输入)同时存在。
由于Java在编译器这端不支持只有返回类型不相同的函数签名,所以get方法只能有一个。这就简化了问题,程序逻辑将设为:
1,如果有一个匹配的getter,那getter的返回类型决定了生效的setter方法;
2,如果没有getter,那参数为String的setter被选中;
3,随机选择一个。不做额外的智能匹配工作。
代码依然是体力活,唯一注意的是 Integer.class 与 int.class 不相同,即 Integer.class == int.class 为 false。所以写类型转换的代码的时候要注意
public class SimpleTypeConverter {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd");
public static Object convertTo(Class<?> type, String propertyValue) throws Exception {
if (type == String.class) {
return propertyValue;
} else if (type == Integer.class || type == int.class) {
return Integer.parseInt(propertyValue);
// For multiple setter methods with same name:
// 1. If there is a getter/is(only for boolean/Boolean) method. The setter
// method which
// needs the same type param as getter's return type should be used.
// 2. Use the setter method which takes String type as param.
// 3. Return a random setter method. Do not do any smart try for type
// convert.
private Method findSetterMethod(Method[] methods, Class<?> beanClass, String property) {
Method getterMethod = getGetterMethodReturnType(methods, beanClass, property);
Class<?> returnType = null;
if (getterMethod != null) {
returnType = getterMethod.getReturnType();
}
String setterStr = getSetterMethodString(property);
Method setterMethodStringParam = null;
Method someSetterMethod = null;
for (Method method : methods) {
if (method.getName().equals(setterStr) && method.getParameterTypes().length == 1) {
if (returnType != null && method.getParameterTypes()[0].equals(returnType)) {
return method;
} else if (method.getParameterTypes()[0].equals(String.class)) {
setterMethodStringParam = method;
} else {
someSetterMethod = method;
}
}
}
if (setterMethodStringParam != null) {
return setterMethodStringParam;
} else if (someSetterMethod != null) {
return someSetterMethod;
} else {
throw new RuntimeException("Bean " + beanClass.getName() + " does not have the property " + property);
}
}
=====================================================================
至于scope,现在就两种:singleton和prototype
singleton是默认的,表示spring容器只会生成一个实例。而对于prototype类型,容器会在每次getBean的时候创建一个新的实例。
实现的话,只要在BeanDefinition中加上scope字段,其默认值是 singleton。对于propotype类型的bean,容器在创建完它们之后就不应该再继续持有这些对象的引用,以便垃圾收集。
public Object getBean(String beanName) throws Exception
if (beans.containsKey("beanName")) {
return beans.get(beanName);
}
Object bean = createBean(beanName);
if (beanDefinitions.get(beanName).getScope() == 0) {
beans.put(beanName, bean);
}
return bean;
}