在 @readThroughSingleCache 的实现 中有一个方法:
final Method methodToCache = getCacheBase().getMethodToCache(pjp);
作用是获取需要缓存的方法。
具体如下:
public Method getMethodToCache(final JoinPoint jp) throws NoSuchMethodException { final Signature sig = jp.getSignature(); if (!(sig instanceof MethodSignature)) { throw new InvalidAnnotationException("This annotation is only valid on a method."); } final MethodSignature msig = (MethodSignature) sig; final Object target = jp.getTarget(); // cannot use msig.getMethod() because it can return the method where annotation was declared i.e. method in // interface // 得到方法的名称和他的参数 String name = msig.getName(); Class<?>[] parameters = msig.getParameterTypes(); Method method = findMethodFromTargetGivenNameAndParams(target, name, parameters); if (method.isBridge()) { if (getLogger().isDebugEnabled()) { getLogger().debug("Method is bridge. Name {}, params: {}", name, parameters); } parameters = bridgeMethodMappingStore.getTargetParamsTypes(target.getClass(), name, parameters); method = findMethodFromTargetGivenNameAndParams(target, name, parameters); } return method; }
这正好就用到了桥接方法。
在jdk 1.5之前,还没有泛型,
创建了一个集合对象,这时候什么样子的数据元素都可以放进去,用的时候再强制转换类型。编译器在编译期间是可以通过的,但是如果在运行时,转换类型不容许,那是会出错的。
那在JDK 1.5 之后引入了泛型。 使用者可以定义集合的类型。这时候就必须是自己定义的类型集合,你定义的String类型的,而你又想加入Integer的元素是不容许的。这样的话,就在编译器编译检查这钟情况了。
问题来了,如何兼容JDK 1.5 前后这种情况呢? 就是说编译器现在编译期间都提示这种错误了
举例说明:
abstract class A<T> { abstract T get(T t); } class B extends A<String> { @Override String get(String s) { return ""; } } 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 get] is Bridge Method? false [class java.lang.Object get] is Bridge Method? true
奇怪吧,有两个方法了,明明我只写了一个方法啊!!
下面来说说java5编译器是如何来编译上面的代码:
Java代码 abstract class A<T> { abstract T get(T t); }
对于A类,编译器看到<>中指定的T参数后,会用Object把类中的其他T参数替换。因为在jdk中根本就不存在T这个类嘛。替换后就成了下面这样子。
Java代码 abstract class A { abstract Object get(Object obj); }
上面这个过程称为类型擦除。对于B类,它继承了A类,指定了T参数为String。如果还按照以前那么编译,那编译的类就是:
Java代码 class B extends A { String get(String s) { return ""; } }
这样在运行时肯定会报错,因为B继承了A,而A又是asbtract类,B还没overriding A中Object get()方法。如何解决这个错误呢?java5编译器在编译的时候做了些手脚。当编译器发现你指定了类型参数,便会在编译的字节码中添加一个桥接方法。这个可以查看B的反编译代码就知道了。
Java代码 class B extends A { //编译器添加的方法 Object get(Object s) { return (Object) get((String) s); } String get(String s) { return ""; } }
对了,就是 在B类中编辑器给新增了一个方法。
桥接方法: 就是连接两个东西的方法。
这个方法可以用于为了升级所带来的不兼容问题。