开发者

How can the java 'class' literal return different instances of the Class object for the same class?

开发者 https://www.devze.com 2023-04-01 13:20 出处:网络
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 Reque

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 t开发者_如何学Pythonhat 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?


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.


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.


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.


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();
    ...
}
...


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!

0

精彩评论

暂无评论...
验证码 换一张
取 消