java'class'文字如何为同一个类返回Class对象的不同实例?

时间:2011-09-01 01:53:01

标签: java annotations classloader

我有一个非常令人费解的情况,并寻找任何想法。

我正在运行一个小型的Spring MVC应用程序,我在我的控制器“ AnsController ”上使用 RequestMapping 注释。当RequestDispatcher正在扫描我的bean以获取 RequestMapping 注释时,在某些时候它会归结到这一行:

clazz.getAnnotation(RequestMapping.class)

(clazz = AnsController.class)

上面的行找不到注释,即使它在那里。

我开始在Eclipse调试器中对此进行调查,发现了一个非常令人费解的问题。以上行失败的原因是b / c RequestMapping.class 返回一个Class对象,它似乎描述了正确的注释,但是有一个不同的内部id和hashCode然后是Class对象存储在< AnsController.class 上的em> annotations 数组!

我编写了一个测试servlet,我放置了上面的代码行,我可以看到存储在 annotations 数组中的Class和 RequestMapping.class 是同一个对象。

然而在 RequestDispatcher servlet中, RequestMapping.class 似乎为同一个注释实例化了另一个Class实例(我可以告诉b / c内部id很多高于注释映射中Class对象的id。

换句话说,在我的Test Servlet中调用 RequestMapping.class 导致一个不同的Class对象,而不是在 RequestDispatcher servlet中调用完全相同的代码。

假设使用相同的类加载器,这是否可能?这是否足以证明应该由不同的类加载器生成应该表示同一个注释的Class对象的这些不同实例?

我无法在书面上找到任何可以证实我的假设,即每个类只允许一个Class对象的实例,但这似乎是合理的......或者我错了?

5 个答案:

答案 0 :(得分:4)

这似乎是合理的,是的,但不幸的是它并不总是这样。请Java Language Specification:

  

在运行时,Java虚拟机使用类加载器加载类和接口。每个类加载器都定义了自己的类和接口集。因此,两个加载器可以加载相同的类或接口定义,但在运行时生成不同的类或接口。

答案 1 :(得分:2)

它们必须由不同的类加载器加载。这通常不是问题 - RequestDispatcher的加载器应该是Controller的加载器的父级;当一个类(RequestMapping here)被请求时,子加载器应首先询问父加载器;因此两者都应该看到相同的RequestMapping类。

如果这被打破了,那么一切都会崩溃; Controller看到的任何Spring类都不同,框架和控制器不可能互动。

检查控制器的类加载器,看看为什么它没有框架类加载器作为父级。

答案 2 :(得分:1)

对于给定的类T,每个类加载器只能有一个Class实例代表T。你的问题是“如果使用相同的类加载器,这应该是否可行?”,答案就是“否”。

但它在Web服务器上下文中也不是一个很好的假设 - 大多数Web服务器启动了数以万计的类加载器,您很可能会获得一个类的多个副本,每个类加载器一个。

答案 3 :(得分:1)

如果您尝试在HandlerInterceptorAdapter子类中执行此操作,则应使用handler参数来获取该类。我用这种方法从控制器中取出注释没有问题。

...
public void preHandle(HttpServletRequest request, HttpServletResponse response,
                      Object handler)
{
    Class clazz = handler.getClass();
    ...
}
...

答案 4 :(得分:1)

我们有它。在被这里的所有答案说服后,我有一个类加载器的混乱,我能够找到问题。我在一年前做过一次黑客攻击我的屁股:)。

当时我编写了一个Eclipse插件,用于启动Jetty 6服务器,并在适当的位置部署应用程序。该插件通过命令行-classpath开关将所有Web项目构建依赖项放在AppClasspath上。这对于我使用它的应用程序来说是可取的,但是它允许我在特定情况下大大简化开发模式下的类加载策略。然而,在这种情况下,我最终使用了AppClassLoaderWebClassLoader上的春季罐子,b / c春季罐子在WEB-INF/lib

一旦我意识到这一点,我就必须在我的码头配置中将parentLoaderPriority设置为WebAppContexttrue,问题就消失了。它当然仍然是一个黑客,但对于我在这里做的快速和肮脏的应用程序来说已经足够了。

感谢所有有用的回复!