接上篇遇到的bug,总结以下在解决这个bug中遇到的其他问题和学到的新知识,在代码中使用的是ibatis提供的SqlMapClient,因此在setter中进行了new的转化,将demo部署到服务器上之后,启动jboss服务器,服务器无法正常启动,报错主要内容如下:
Bean property 'mysqlReaderMapClient' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
以下是关于这个bug找到的一些资料:
https://jira.springsource.org/browse/SPR-5024
https://jira.springsource.org/browse/SPR-2727
https://jira.springsource.org/browse/SPR-5254
开始因为我们对这个问题并没有一个比较直观的认识与理解,只能走一步看一步,其中仔细看了一些资料,编写了一些测试代码,将主要学习到的知识介绍如下:
第一篇的主题是:Introspection problem: setter not found when corresponding getter uses a covariant return type,第二篇的主题是:Dealing with covariant properties in CachedIntrospectionResult,通过阅读这两篇,主要新接触了两个概念,第一个是“java桥接方法”、“协变返回类型”,将这两个对我而言比较新的名词和大家分享一下:
一、 Java Bridge Method
主要参考了以下两篇感觉比较好的blog:
http://jlife.iteye.com/blog/638407
http://berdy.iteye.com/blog/810488
下面讲一下自己在实践中的一些想法吧,桥接主要是为了兼容jdk 1.4采取的一种措施,桥接方法解决的问题是jdk1.5开始支持泛型,关于泛型的概念,常见的比如:
List<String> strList = new ArrayList<String>();
这就是对泛型比较简单的使用,泛型的好处让从集合中取数据的程序员能够知道自己取的是什么,稍微复杂一些的泛型的应用如下:
public abstract class A<T>{
abstract T get(T t);
}
public class B extends A<String>{
String get(String t);
}
通过泛型指定B中的get返回的类型必须为指定的类型或者子类,其本质就是将原来要在运行期间的检查工作提前到了编译阶段,如果返回的类型不是指定的类型的话,就要报错,为了满足这个特性,同时要能够在老的版本中运行,首先用一个简单的例子去说明这个问题:
在父类中该T会被转换成Object:
public abstract class A<Object>{
abstract Object get(Object obj);
}
而子类则是:
public class B{
String get(String str);
}
这样在编译时肯定会报错,原因是B继承自A,而A是abstract class,而B并没有overriding掉A中的Object get(Object obj),为了解决这个问题,编译器做了一些工作,当发现我们指定类T的类型时,编译器会在字节码中添加一个桥接方法来overriding掉父类的方法,可以通过查看编译器的字节码可以发现,也可以通过反射来查看类中的方法:
在已有的A.java和B.java的基础上我们添加一个测试类BridgeTest.java,代码如下:
import java.lang.reflect.Method;
public class TestBridge {
public static void main(String[] args) {
Class<B> clazz = B.class;
Method[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
System.out.println(getMethodInfo(m) + " is Bridge Method? " + m.isBridge());
}
}
public static String getMethodInfo(Method m){
StringBuilder sb = new StringBuilder();
sb.append(m.getReturnType()).append(" ");
sb.append(m.getName());
Class[]params = m.getParameterTypes();
for (int i = 0; i < params.length; i++) {
sb.append(params[i].getName()).append(" ");
}
return sb.toString();
}
}
运行后的结果如下:
class java.lang.String getjava.lang.String is Bridge Method? false
class java.lang.Object getjava.lang.Object is Bridge Method? True
通过分析结果,get返回String的方法不是桥接的,而返回Object是桥接的,这就是编译器做的一些工作。
二、 Covariant Return Type
翻译成中文是协变式返回类型,在介绍这个概念之前先介绍下jdk1.4和jdk1.5中关于函数签名的定义:
In Java 1.4, and earlier, one method can override another if the signatures match exactly.
In Java 5, a method can override another if the arguments match exactly but the return type of the overriding method, if it is a subtype of the return type of the other method.
也就是说,在jdk1.5中,对于“重写”的判断放宽了条件,子类中方法返回的类型是父类中方法返回类型的子类也算是重写。
可以用下面一个简单的例子来说明:
public class A{
Father get();
}
public class B{
Son get();
}
其中class Son extends Father,这就是协变式返回类型。
Bean property 'mysqlReaderMapClient' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
以下是关于这个bug找到的一些资料:
https://jira.springsource.org/browse/SPR-5024
https://jira.springsource.org/browse/SPR-2727
https://jira.springsource.org/browse/SPR-5254
开始因为我们对这个问题并没有一个比较直观的认识与理解,只能走一步看一步,其中仔细看了一些资料,编写了一些测试代码,将主要学习到的知识介绍如下:
第一篇的主题是:Introspection problem: setter not found when corresponding getter uses a covariant return type,第二篇的主题是:Dealing with covariant properties in CachedIntrospectionResult,通过阅读这两篇,主要新接触了两个概念,第一个是“java桥接方法”、“协变返回类型”,将这两个对我而言比较新的名词和大家分享一下:
一、 Java Bridge Method
主要参考了以下两篇感觉比较好的blog:
http://jlife.iteye.com/blog/638407
http://berdy.iteye.com/blog/810488
下面讲一下自己在实践中的一些想法吧,桥接主要是为了兼容jdk 1.4采取的一种措施,桥接方法解决的问题是jdk1.5开始支持泛型,关于泛型的概念,常见的比如:
List<String> strList = new ArrayList<String>();
这就是对泛型比较简单的使用,泛型的好处让从集合中取数据的程序员能够知道自己取的是什么,稍微复杂一些的泛型的应用如下:
public abstract class A<T>{
abstract T get(T t);
}
public class B extends A<String>{
String get(String t);
}
通过泛型指定B中的get返回的类型必须为指定的类型或者子类,其本质就是将原来要在运行期间的检查工作提前到了编译阶段,如果返回的类型不是指定的类型的话,就要报错,为了满足这个特性,同时要能够在老的版本中运行,首先用一个简单的例子去说明这个问题:
在父类中该T会被转换成Object:
public abstract class A<Object>{
abstract Object get(Object obj);
}
而子类则是:
public class B{
String get(String str);
}
这样在编译时肯定会报错,原因是B继承自A,而A是abstract class,而B并没有overriding掉A中的Object get(Object obj),为了解决这个问题,编译器做了一些工作,当发现我们指定类T的类型时,编译器会在字节码中添加一个桥接方法来overriding掉父类的方法,可以通过查看编译器的字节码可以发现,也可以通过反射来查看类中的方法:
在已有的A.java和B.java的基础上我们添加一个测试类BridgeTest.java,代码如下:
import java.lang.reflect.Method;
public class TestBridge {
public static void main(String[] args) {
Class<B> clazz = B.class;
Method[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
System.out.println(getMethodInfo(m) + " is Bridge Method? " + m.isBridge());
}
}
public static String getMethodInfo(Method m){
StringBuilder sb = new StringBuilder();
sb.append(m.getReturnType()).append(" ");
sb.append(m.getName());
Class[]params = m.getParameterTypes();
for (int i = 0; i < params.length; i++) {
sb.append(params[i].getName()).append(" ");
}
return sb.toString();
}
}
运行后的结果如下:
class java.lang.String getjava.lang.String is Bridge Method? false
class java.lang.Object getjava.lang.Object is Bridge Method? True
通过分析结果,get返回String的方法不是桥接的,而返回Object是桥接的,这就是编译器做的一些工作。
二、 Covariant Return Type
翻译成中文是协变式返回类型,在介绍这个概念之前先介绍下jdk1.4和jdk1.5中关于函数签名的定义:
In Java 1.4, and earlier, one method can override another if the signatures match exactly.
In Java 5, a method can override another if the arguments match exactly but the return type of the overriding method, if it is a subtype of the return type of the other method.
也就是说,在jdk1.5中,对于“重写”的判断放宽了条件,子类中方法返回的类型是父类中方法返回类型的子类也算是重写。
可以用下面一个简单的例子来说明:
public class A{
Father get();
}
public class B{
Son get();
}
其中class Son extends Father,这就是协变式返回类型。