<p dir="auto"><strong><a href="https://jira.spring.io/secure/ViewProfile.jspa?name=kmudrick" rel="nofollow">Kevin Mudrick</a></strong> opened <strong><a href="https://jira.spring.io/browse/SPR-9670?redirect=false" rel="nofollow">SPR-9670</a></strong> and commented</p>
<p dir="auto">We are seeing a pretty substantial performance hit with the creation of large quantities of prototype-scoped beans in the 3.1.x branch versus 3.0.x (and 2.5.6)</p>
<p dir="auto">In our example, we have call context.getBean() with a bean consisting of 2 levels of nested bean referenced, in a pretty large loop.</p>
<p dir="auto">3.0.7: 40604ms<br>
3.1.2: 685150ms</p>
<p dir="auto">The only difference between these runs is the version of the spring dependencies.</p>
<p dir="auto">After profiling using YourKit, I found the hotspots in 3.1.x to be in the following places:</p>
<p dir="auto">org.springframework.beans.TypeConverterDelegate: This seems to be the bigger problem.<br>
The changes introduced in 3.1 (specifically, in commit <a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/spring-projects/spring-framework/commit/6f146737f475828b6d765784017773378c2c4922/hovercard" href="https://github.com/spring-projects/spring-framework/commit/6f146737f475828b6d765784017773378c2c4922"><tt>6f14673</tt></a>) to findDefaultEditor() end up (expensively) initializing an editor registry every time this is called.  Since we get here each time we apply a property on every bean creation, this really adds up.  Previously, the commented-out code ended up shortcutting this.  I found that replacing the commented out code (the part that relied on the now-removed PropertyTypeDescriptor/PropertyDescriptor with static use of PropertyEditorManager.findEditor(), cuts down on the slowness considerably, at the expense of relying on java.beans - which a few commit comments seem to indicate is something to be avoided for better platform compatibility.</p>
<p dir="auto">org.springframework.core.convert.Property: This also seems to be a problem.<br>
The overhead of the annotation parsing via reflection is already noted in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="398118763" data-permission-text="Title is private" data-url="https://github.com/spring-projects/spring-framework/issues/13981" data-hovercard-type="issue" data-hovercard-url="/spring-projects/spring-framework/issues/13981/hovercard" href="https://github.com/spring-projects/spring-framework/issues/13981">#13981</a> - but that issue doesn't seem to mention this example.  My profiling is showing that this is being constructed for every property found on every bean being created.  With lots of prototype beans - this comes to light pretty quickly.  Perhaps if ReflectionUtils used a similar caching strategy to apache commons-beanutils PropertyUtilsBean for annotations, this performance hit could be mitigated.</p>
<hr>
<p dir="auto"><strong>Affects:</strong> 3.1.2</p>
<p dir="auto"><strong>Issue Links:</strong></p>
<ul dir="auto">
<li><a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="398118763" data-permission-text="Title is private" data-url="https://github.com/spring-projects/spring-framework/issues/13981" data-hovercard-type="issue" data-hovercard-url="/spring-projects/spring-framework/issues/13981/hovercard" href="https://github.com/spring-projects/spring-framework/issues/13981">#13981</a> org.springframework.core.convert.Property class performs expensive operations in its constructor (<em><strong>"is duplicated by"</strong></em>)</li>
<li><a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="398106550" data-permission-text="Title is private" data-url="https://github.com/spring-projects/spring-framework/issues/12081" data-hovercard-type="issue" data-hovercard-url="/spring-projects/spring-framework/issues/12081/hovercard" href="https://github.com/spring-projects/spring-framework/issues/12081">#12081</a> Concurrent prototype creation causes NullPointerException</li>
<li><a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="398113052" data-permission-text="Title is private" data-url="https://github.com/spring-projects/spring-framework/issues/13109" data-hovercard-type="issue" data-hovercard-url="/spring-projects/spring-framework/issues/13109/hovercard" href="https://github.com/spring-projects/spring-framework/issues/13109">#13109</a> TypeConverterDelegate contains commented out code</li>
</ul>
<p dir="auto">0 votes, 6 watchers</p>
<p dir="auto"><strong><a href="https://jira.spring.io/secure/ViewProfile.jspa?name=gredler2" rel="nofollow">Daniel Gredler</a></strong> opened <strong><a href="https://jira.spring.io/browse/SPR-9601?redirect=false" rel="nofollow">SPR-9601</a></strong> and commented</p>
<p dir="auto">As a result of the changes [1] made to RequestMappingHandlerMapping.isHandler(Class) to make it detect both Controller and RequestMapping annotations (see <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="398118925" data-permission-text="Title is private" data-url="https://github.com/spring-projects/spring-framework/issues/14010" data-hovercard-type="issue" data-hovercard-url="/spring-projects/spring-framework/issues/14010/hovercard" href="https://github.com/spring-projects/spring-framework/issues/14010">#14010</a>), one of our HttpRequestHandler implementations that was working in 3.1.1 no longer works in 3.1.2:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="@RequestMapping(&quot;/console&quot;)
@Transactional
public class ConsoleHandler implements HttpRequestHandler, ApplicationContextAware {

    ...

    @RequestMapping(method = RequestMethod.GET)
    public void handleRequest(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ...
    }

    ...
}"><pre class="notranslate"><code class="notranslate">@RequestMapping("/console")
@Transactional
public class ConsoleHandler implements HttpRequestHandler, ApplicationContextAware {

    ...

    @RequestMapping(method = RequestMethod.GET)
    public void handleRequest(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ...
    }

    ...
}
</code></pre></div>
<p dir="auto">In 3.1.1, this handler was mapped by the DefaultAnnotationHandlerMapping, which was able to forward HTTP requests to this handler. However, because of the above change, in 3.1.2 this handler is mapped by the RequestMappingHandlerMapping, which throws the following exception when it tries to actually handle an HTTP request:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="java.lang.IllegalArgumentException: object is not an instance of declaring class
HandlerMethod details: 
Controller [$Proxy109]
Method [public void com.dpgm.connect.framework.server.remoting.ConsoleHandler.handleRequest(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws javax.servlet.ServletException,java.io.IOException]
Resolved arguments: 
[0] [type=org.eclipse.jetty.server.Request] [value=(GET /misc/console)@10144214 org.eclipse.jetty.server.Request@9ac9d6]
[1] [type=org.eclipse.jetty.server.Response] [value=HTTP/1.1 200 
]
      at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:217)
      at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
      at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
      at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
      at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
      at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
      at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
      at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
      at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
      at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:693)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)
      at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:558)
      at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:488)
      at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:119)
      at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:520)
      at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:233)
      at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:973)
      at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:417)
      at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:192)
      at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:907)
      at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
      at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:47)
      at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:110)
      at org.eclipse.jetty.server.Server.handle(Server.java:350)
      at org.eclipse.jetty.server.HttpConnection.handleRequest(HttpConnection.java:442)
      at org.eclipse.jetty.server.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:924)
      at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:582)
      at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:218)
      at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:51)
      at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:586)
      at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:44)
      at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:598)
      at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:533)
      at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
      at java.lang.reflect.Method.invoke(Unknown Source)
      at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
      ... 34 more"><pre class="notranslate"><code class="notranslate">java.lang.IllegalArgumentException: object is not an instance of declaring class
HandlerMethod details: 
Controller [$Proxy109]
Method [public void com.dpgm.connect.framework.server.remoting.ConsoleHandler.handleRequest(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws javax.servlet.ServletException,java.io.IOException]
Resolved arguments: 
[0] [type=org.eclipse.jetty.server.Request] [value=(GET /misc/console)@10144214 org.eclipse.jetty.server.Request@9ac9d6]
[1] [type=org.eclipse.jetty.server.Response] [value=HTTP/1.1 200 
]
      at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:217)
      at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
      at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
      at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
      at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
      at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
      at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
      at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
      at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
      at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:693)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)
      at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:558)
      at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:488)
      at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:119)
      at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:520)
      at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:233)
      at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:973)
      at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:417)
      at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:192)
      at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:907)
      at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
      at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:47)
      at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:110)
      at org.eclipse.jetty.server.Server.handle(Server.java:350)
      at org.eclipse.jetty.server.HttpConnection.handleRequest(HttpConnection.java:442)
      at org.eclipse.jetty.server.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:924)
      at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:582)
      at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:218)
      at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:51)
      at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:586)
      at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:44)
      at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:598)
      at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:533)
      at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
      at java.lang.reflect.Method.invoke(Unknown Source)
      at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
      ... 34 more
</code></pre></div>
<p dir="auto">I'm attaching the debug logs of the server startup + one HTTP request, one log file using Spring 3.1.1 and one log file using Spring 3.1.2. I've massaged the logs a little bit (removed most timestamps, normalized the hashcodes in the toStrings) so that it's easier to see the differences if you diff the files.</p>
<p dir="auto">[1] <a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/spring-projects/spring-framework/commit/2fa0e63e5ab84ac964c55a28000104d3855c886c/hovercard" href="https://github.com/spring-projects/spring-framework/commit/2fa0e63e5ab84ac964c55a28000104d3855c886c"><tt>2fa0e63</tt></a></p>
<hr>
<p dir="auto"><strong>Affects:</strong> 3.1.2</p>
<p dir="auto"><strong>Attachments:</strong></p>
<ul dir="auto">
<li><a href="https://jira.spring.io/secure/attachment/20110/console-out-3.1.1.txt" rel="nofollow">console-out-3.1.1.txt</a> (<em>675.28 kB</em>)</li>
<li><a href="https://jira.spring.io/secure/attachment/20109/console-out-3.1.2.txt" rel="nofollow">console-out-3.1.2.txt</a> (<em>687.57 kB</em>)</li>
</ul>
<p dir="auto"><strong>Referenced from:</strong> commits <a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/spring-projects/spring-framework/commit/7c6a1a1bf0db6b428344ec6797a9152c5f4f6d30/hovercard" href="https://github.com/spring-projects/spring-framework/commit/7c6a1a1bf0db6b428344ec6797a9152c5f4f6d30"><tt>7c6a1a1</tt></a></p>