java“类”如何实现?文字返回同一类的 Class 对象的不同实例?

发布于 2024-12-02 13:01:26 字数 1071 浏览 2 评论 0原文

我的情况非常令人困惑,正在寻找任何想法。

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

clazz.getAnnotation(RequestMapping.class)

(clazz = AnsController.class)

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

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

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

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

换句话说,在我的 Test Servlet 中调用 RequestMapping.class 会产生与在 RequestDispatcher servlet 中调用完全相同的代码不同的 Class 对象。

假设使用相同的类加载器,这是否可能?这是否足以得出这样的结论:这些不同的 Class 对象实例应该表示一个相同的注释,必须由不同的类加载器生成?

我找不到任何书面内容来证实我的假设,即每个类只允许一个 Class 对象实例,但这似乎是合理的......或者我错了?

I have an extremely puzzling situation and looking for any ideas.

I'm running a small Spring MVC app, where I make use of the RequestMapping annotation on my controller "AnsController". When RequestDispatcher is scanning my beans for the RequestMapping annotation, at some point it comes down to this line:

clazz.getAnnotation(RequestMapping.class)

(clazz = AnsController.class)

The line above does not find the annotation, even though it is there.

I started investigating this in Eclipse debugger and found a very puzzling problem. The reason why the above line fails is b/c RequestMapping.class is returning a Class object which seems to describe the correct annotation, but has a different internal id and hashCode then the Class object stored in the annotations array on the AnsController.class!

I wrote a test servlet where I placed the above line of code, and I can see that the Class stored in the annotations array and the Class returned by RequestMapping.class are the same object.

Yet in the RequestDispatcher servlet, RequestMapping.class seems to instantiate another instance of the Class for that same annotation (I can tell b/c the internal id is much higher than the id of the Class object in the annotations map).

In other words, calling RequestMapping.class in my Test Servlet results in a different Class object than calling exactly the same code in RequestDispatcher servlet.

Should this be even possible, assuming the same classloader is being used? Is this sufficient evidence to conclude that these distinct instances of the Class object that are supposed to represent one and the same annotation must be produced by different classloaders?

I can't find anything in writing that would confirm my assumption that only one instance of Class object per class is allowed, but it seems reasonable... Or am I wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

水染的天色ゝ 2024-12-09 13:01:26

是的,这似乎很合理,但不幸的是它并不总是这样。引用 Java 语言规范:

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

It seems reasonable, yes, but unfortunately it does not always work that way. Quoth the Java Language Specification:

At run-time, classes and interfaces are loaded by the Java virtual machine using class loaders. Each class loader defines its own set of classes and interfaces. As a result, it is possible for two loaders to load an identical class or interface definition but produce distinct classes or interfaces at run-time.

〆凄凉。 2024-12-09 13:01:26

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

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

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

They must have been loaded by different class loaders. That's usually not a problem - the loader of RequestDispatcher should be the parent of the loader of the Controller; and a child loader should ask the parent loader first when a class (RequestMapping here) is requested; therefore both should see the same RequestMapping class.

If this is broken, all hell break loose; any Spring class that the Controller sees are different, and the framework and the controller cannot possibly interact.

Examine the class loader of the controller and see why it doesn't have the framework class loader as the parent.

鹤舞 2024-12-09 13:01:26

对于给定的类 T,每个类加载器只能有一个代表 TClass 实例。您的问题是“假设使用相同的类加载器,这是否可能?”,答案是“否”。

但在 Web 服务器上下文中,这也不是一个很好的假设 - 大多数 Web 服务器都会启动无数的类加载器,并且您很可能会获得一个类的多个副本,每个类加载器都有一个副本。

For a given class T, there can only be one instance of Class representing T per classloader. Your question was "Should this be even possible, assuming the same classloader is being used?", and the answer to that is "No".

But it's also not a very good assumption in a web server context - most webservers start up zillions of classloaders, and you might very well get multiple copies of a class, one from each classloader.

℉服软 2024-12-09 13:01:26

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

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

If you're trying to do this inside a HandlerInterceptorAdapter subclass, you should use the handler parameter to get the class. I've had no problem pulling annotations off of controllers with this approach.

...
public void preHandle(HttpServletRequest request, HttpServletResponse response,
                      Object handler)
{
    Class clazz = handler.getClass();
    ...
}
...
剧终人散尽 2024-12-09 13:01:26

现在我们就有了。在被这里的所有答案确信我的类加载器一团糟之后,我找到了问题所在。一年前的一次黑客攻击让我痛不欲生:)。

当时我编写了一个 Eclipse 插件,用于启动 Jetty 6 服务器并就地部署应用程序。该插件通过命令行 -classpath 开关将所有 Web 项目构建依赖项放置在 AppClasspath 上。这对于我使用它的应用程序来说是理想的,因为它使我能够大大简化特定情况下开发模式下的类加载策略。然而,在这种情况下,我最终在 AppClassLoader 和 WebClassLoader 上得到了 Spring jar,因为 Spring jar 位于 WEB-INF/lib 中代码>.

一旦我意识到这一点,我只需在我的jetty配置中将WebAppContext上的parentLoaderPriority设置为true,问题就消失了。当然,这仍然是一个 hack,但对于我在这里做的快速而肮脏的应用程序来说已经足够好了。

感谢您所有有用的回复!

And there we have it. After being convinced by all the answers here that I have a classloader mess, I was able to find the problem. I was bit in the ass by a hack I did a year ago :).

At the time I wrote an Eclipse plugin that I used to launch the Jetty 6 server with the app deployed in-place. That plugin placed all of the web project build dependencies on the AppClasspath via the commandline -classpath switch. This was desirable for the app I was using it for, b/c it allowed me to greatly simplify the classloading strategy in development mode for that specific case. In this case, however, I ended up with Spring jars on the AppClassLoader and WebClassLoader, b/c the Spring jars were in WEB-INF/lib.

Once I realized that, I just had to set parentLoaderPriority on WebAppContext to true in my jetty config and the problem went away. It's still a hack of course, but good enough for the quick-and-dirty app I'm doing here.

Thank you for all the helpful responses!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文